-
스프링mvc 시작시(서버기동시)메소드 실행방법spring 2020. 9. 18. 18:05
스프링에서 서버 기동시 바로 실행되야 하는 메소드가 있을 수 있다.
그럴때 먼저, WebApplicationInitializer 인터페이스를 구현(implements)해서 할 수도 있다.
blog.naver.com/PostView.nhn?blogId=mankeys&logNo=220719095906&redirect=Dlog&widgetTypeCall=true
하지만 이경우, 빈들이 다 생성되지 않은 상태에서 실행되서, 데이터베이스에서 데이터를 받아야 하는 메소드를 실행해야 할 경우 문제가 생겼다. 그래서 찾은 방법이
qastack.kr/programming/2401489/execute-method-on-startup-in-spring
여기서 밑으로 내리다 보면 ApplicationListener를 구현해서 하는 방법이 있다.
ApplicationListener<ContextRefreshedEvent>를 구현한 클래스를 서비스 컴포넌트로 만들어 사용할 수 있었다.
스프링 4.2이상부터는 @EventListener(ContextRefreshedEvent.class) 어노테이션을 메소드 위에 붙여 사용할 수도 있다고 한다.
이 방법을 검색하게 된 계기는
데이터베이스에서 게시판 이름을 받아와 상단메뉴를 만드는 게시판예제를 만들다가, 강사님은 인터셉터로 모든 메소드가 실행될 때마다 상단메뉴 이름을 받아와 HttpServletRequest영역에 저장해 상단메뉴를 구현하는 식으로 하셨는데, 모든 메소드가 실행될 때 마다 데이터베이스에 접속해서 메뉴이름을 받아오면 아무래도 조금 비효율적일 수도 있겠다는 생각이 들어 검색하고 구현해보았다.
package kr.co.jaehyeon.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Service; @Service public class InitService implements ApplicationListener<ContextRefreshedEvent>{ @Autowired TopMenuService topMenuService; @Override public void onApplicationEvent(ContextRefreshedEvent event) { // TODO Auto-generated method stub System.out.println("hh"); topMenuService.getTop(); } }
ServletAppContext에서 @ComponentScan으로 빈을 등록해주면 모든 빈이 등록 된후 구현된 메소드를 실행해준다. 나는 탑메뉴 서비스에서 상단메뉴를 가져오는 메소드를 실행하게 하였고,
@Service(value = "TopMenuService") @DependsOn(value = {"TopMenuDao"}) public class TopMenuService { public TopMenuService() { System.out.println("TopMenuService Bean 생성"); } @Autowired private TopMenuDao topMenuDao; @Autowired ServletContext context; public List<BoardInfoBean> getTop(){ System.out.println("getTop실행"); List<BoardInfoBean> top = topMenuDao.getTop(); for(BoardInfoBean z : top) { System.out.println(z.getBoard_info_name()); System.out.println("------------"); } context.setAttribute("top", top); return top; } }
topMenuService에서 ServletContext를 주입받아 Application영역에 상단메뉴를 저장해주었다.
그런데, Service영역에서 HttpServletRequest나 HttpSesion을 사용하려고 하자 에러가 발생했다.
dveamer.github.io/backend/SpringRequestContextHolder.html
이 블로그를 참고하여
HttpServletRequest req = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
를 사용하여 request영역이나 session영역에 setAttribute를 통해 Attribute를 저장하고자 했지만 SEVERE: Context initialization failed
java.lang.NullPointerException
에러가 발생하였다.
아직 확실하진 않지만, HttpServletRequest영역이 아직 만들어지지 않았거나, 만들어졌어도 null이기 때문에 이런 오류가 발생하는 듯 싶다. 이 문제 해결을 위해 offbyone.tistory.com/144
등등 다양한 방법을 구글링했지만 아직 답을 얻지 못했다. 위의 메소드가 호출되는 시점에서 HttpServlet이 생성되지 않아서 발생하는 문제같은데, 자세한 내용은 스프링과 Thread, WAS에 대해 더 공부해봐야겠다.
사실, 하면서 든 생각인데 그냥 Interceptor에서 패턴을 수정해주고 HttpServletRequest에서 session이나 servletContext를 추출하여 session이나 application영역에 저장해주면 될것같다는 생각이 들어 해봤는데, 이것도 괜찮은 것 같다. 그런데 url을 직접 쳐서 메인페이지가 아닌 다른곳으로 접속하는 사용자가 있을 수 있으니깐 인터셉터를 이용해 상단메뉴를 구성하면 결국에는 모든 패턴에서 상단메뉴를 다시 불러오게끔 해야할 것 같다. 아니면, 인터셉터에서 현재 request에 상단메뉴를 저장하는 Attribute가 있는지 검사하고 없으면 넣어주는 식으로 하면 될 것 같다. request나 session영역에 넣는 문제가 해결안되는 문제라고 하더라도 implements ApplicationListener<ContextRefreshedEvent>를 구현한 클래스를 통해 빈이 모두 생성된 후, 바로 메서드를 실행하는 방법을 알았다.
이 문제를 거의 오늘 하루 종일 잡고있었다. 사실 인터셉터에서 간단하게 해결될 수 있는 문제였던 것 같다. 사고를 확장하고, 실력을 길러야겠다.
++혹시몰라서 컨르롤러로 넘긴후, 컨트롤러에서 HttpServletRequest를 주입받아 넣어보기도하고,
InitService implements ApplicationListener<ContextRefreshedEvent>에서 HttpServletRequest를 주입받거나, 만들어 넣어보기도 했지만 오류가 발생한다.
++아직 스프링부트를 해보지는 않았지만, 스프링부트에서는 CommandLineRunner등 더 편한 방법이 있나보다.
'spring' 카테고리의 다른 글
message를 쓸때 주의사항 (0) 2020.09.16