푸들푸들

1218 [Error] Listener 접속자 수 count 본문

카테고리 없음

1218 [Error] Listener 접속자 수 count

COCO_develop 2024. 12. 18. 14:04

Listener

@SpringBootApplication @ServletComponentScan 어노테이션 추가

@SpringBootApplication
@ServletComponentScan // @WebServlet, @Filter, @WebListener 사용가능
public class AppApplication {

	public static void main(String[] args) {
		SpringApplication.run(AppApplication.class, args);
	}
}

 

 

@Mapper
public interface StatsMapper {
	Integer selectStatsAmountByToday();
	Integer selectStatsAmountByTotal();
	String selectStatsCurdate();
	int insertStats();
	int updateStats();
}
<mapper namespace="com.example.app.mapper.StatsMapper">
	<!-- 오늘 접속자 수 -->
	<select id="selectStatsAmountByToday" resultType="Integer">
		SELECT amount
		FROM stats
		WHERE last_update = CURDATE()
	</select>
	
	<!-- 누적 접속자 수 -->
	<select id="selectStatsAmountByTotal" resultType="Integer">
		SELECT SUM(amount) FROM stats
	</select>
	
	<!-- 오늘 날짜에 데이터(접속자)가 있는지 -->
	<select id="selectStatsCurdate" resultType="String">
		SELECT last_update lastUpdate
		FROM stats
		WHERE last_update = CURDATE()
	</select>
	
	<!-- 오늘 날짜의 amount 1로 insert -->
	<insert id="insertStats">
		INSERT INTO stats(amount) VALUES (1)
	</insert>

	<!-- 오늘 날짜의 amount +1 증가 update -->
	<update id="updateStats">
		UPDATE stats SET amount = amount+1
		WHERE last_update = CURDATE()
	</update>
</mapper>
@Service
@Transactional
public class StatsService {
	@Autowired StatsMapper statsMapper;
	
	public Integer getStatsAmountByToday() {
		return statsMapper.selectStatsAmountByToday();
	}
	public Integer getStatsAmountByTotal() {
		return statsMapper.selectStatsAmountByTotal();
	}
	public String getStatsCurdate() {
		return statsMapper.selectStatsCurdate();
	}
	public int addStats() {
		return statsMapper.insertStats();
	}
	public int modifyStats() {
		return statsMapper.updateStats();
	}
}
@WebListener
public class TomacatListener implements ServletContextListener {
	 public TomacatListener() { }

    // 톰캣이 켜질 때
    public void contextInitialized(ServletContextEvent sce)  { 
    	// jsp application 속성 변수 추가
    	// ServletContext -> context (jsp에서는 application)
        ServletContext sc = sce.getServletContext();
    	sc.setAttribute("totalCount",0); // 톰캣이 켜질때 totalCount라는 변수 생성
    }

    public void contextDestroyed(ServletContextEvent sce)  { 
         // 톰캣이 꺼질 때
    }
	
}

컨텍스트 생명주기 리스너 : 톰켓 부팅시 현재접속자수에 사용될 컨텍스트(application)변수 totalCount 초기화

@Slf4j
@WebListener
public class SessionListener implements HttpSessionListener {
	@Autowired StatsService statsService;
	
    public SessionListener() {
    }

    // 현재 서버(tomcat context)에 새로운 세션이 생성될 때 콜백
    public void sessionCreated(HttpSessionEvent se)  { 
    	// 현재 접속자 +1
    	ServletContext sc = se.getSession().getServletContext();
    	// application.totalCount 값을 +1 해서 덮어쓰기
    	sc.setAttribute("totalCount",(Integer)(sc.getAttribute("totalCount")) + 1);
    	// 오늘 접속자 +1 -> StatsService
    	// 1) 오늘 데이터 있는지 확인
    	String todayData = statsService.getStatsCurdate();
    	if(todayData == null) { // 오늘 데이터가 없으면
    		log.debug("insert 실행");
    		statsService.addStats();
    	} else {
    		log.debug("update 실행");
    		statsService.modifyStats();
    	}
    }

    // 현재 서버(tomcat context)에 새로운 세션이 폐기될 때 콜백
    public void sessionDestroyed(HttpSessionEvent se)  { 
    	// 현재 접속자 -1
    	ServletContext sc = se.getSession().getServletContext();
    	// application.totalCount 값을 -1 해서 덮어쓰기
    	sc.setAttribute("totalCount",(Integer)(sc.getAttribute("totalCount")) - 1);
    }
	
}

Session 생명주기 리스너 : 세션이 생성되고 사라질때 마다 콜백되는 메서드 구현

 

 

home.jsp

<body class="container">
	<h1>HOME</h1>
	<br>
	<div>
		<a href="/addMember">회원가입</a>
		<a href="/login">로그인</a>
	</div>
	
	<div>
		<div>현재 접속자 수 : ${totalCount}</div> <!-- 서버에 세션이 몇개 만들어져 있는지 -->
		<div>오늘 접속자 수 : </div> <!-- DB -->
		<div>누적 접속자 수 : </div> <!-- DB -->
	</div>
</body>

 

 

--> 오류

Session event listener threw exception

Cannot invoke "com.example.app.mapper.StatsMapper.selectStatsCurdate()" because "this.statsMapper" is null

 

???

Listener에서 @Autowired가 작동하지 않는 듯?

-> 직접 주입

// bean 주입에 @Autowired를 사용할 수 없어서 코드로 찾아서 직접 주입
WebApplicationContext ctx 
	= WebApplicationContextUtils.getWebApplicationContext(se.getSession().getServletContext());
this.statsService = (StatsService)ctx.getBean("statsService");

 

 

* 이전 totalCount -> currentCount 이름 변경

@Slf4j
@Controller
public class HomeController {
	@Autowired StatsService statsService;
	
	@GetMapping({"/","/home"}) // /이나 /home 요청 -> 배열
	public String home(Model model) {
		Integer todayCount = statsService.getStatsAmountByToday();
		Integer totalCount = statsService.getStatsAmountByTotal();
		model.addAttribute("todayCount",todayCount);
		model.addAttribute("totalCount",totalCount);
		log.debug("home() 실행");
		return "home";
	}
}
<h1>HOME</h1>
<br>
<div>
    <a href="/addMember">회원가입</a>
    <a href="/login">로그인</a>
</div>

<div>
    <div>현재 접속자 수 : ${currentCount}</div> <!-- 서버에 세션이 몇개 만들어져 있는지 -->
    <div>오늘 접속자 수 : ${todayCount}</div> <!-- DB -->
    <div>누적 접속자 수 : ${totalCount}</div> <!-- DB -->
</div>

--> 오류

현재 접속자 수 카운트 안됨

 

@SpringBootApplication에서 @ServletComponentScan 제거 후
@WebListener에서 -> @Component로 수정 후
리스너에서도 @Autowired 애노테이션을 사용 가능 하도록 수정

 

@Autowired 사용가능, 현재 접속자 수 count 해결!