티스토리 뷰

직접 공부하며 정리한 내용이라 틀린 내용이 있을 수 있습니다!


JavaConfig방식을 통해 스프링이 구동되는 순서를 알아보자.

먼저 xml설정일 때의 예로, 전체적인 그림을 보면 다음과 같다.

출처 : https://javannspring.tistory.com/231

웹 어플리케이션 실행

웹 어플리케이션이 실행되면 톰캣에 의해 web.xml이 로딩되면서 스프링 구동된다.

Java Config방식에서는 web.xml의 역할을 할 클래스를 만들어야 한다.
이는 WebApplicationInitializer 인터페이스를 구현하여 onStartup 메소드를 작성하면 된다.

public class WebApplication implements WebApplicationInitializer {
    @Override
  public void onStartup(ServletContext servletContext) {}
}

이곳이 web.xml 의 역할을 하는 entry point가 된다.

onStartup 메소드에서는 톰캣에서 관리하는 서블릿 컨테이너ServletContext 로 들어온다.

ContextLoaderListener

web.xml 에는 ContextLoaderListener 가 등록되어 있다.

우리는 JAVA코드 내에서 직접 위 클래스를 만들어 사용한다.

ContextLoaderListener contextLoaderListener = new ContextLoaderListener(rootContext);
servletContext.addListener(contextLoaderListener);

contextLoaderListener 를 생성한 후에 서블릿 컨테이너 에 등록한다.
rootContext 는 이후에 설명하겠다.

ContextLoaderListenerApplicationContext를 생성하는 역할을 수행한다.
즉, 스프링 컨테이너를 생성하고 관리하기 위해 ContextLoaderListener 를 이용한다.

root-context

이제 스프링 컨테이너를 구동시켜보자.

xml방식에서는 root-context.xml 혹은 applicationContext.xml 파일을 로딩해 스프링 컨테이너를 구동하게 된다. 위 설정 파일에는 스프링 컨테이너에 등록할 빈의 정보들이 명시되어 있다

이렇게 구동된 스프링 컨테이너를 루트 컨테이너 라고 한다.
그리고 루트 컨테이너 를 등록할 때는 web과 관련된 빈들은 등록하지 않는다.

우리는 Java Config방식을 사용하므로 applicationContext.xml 역할을 할 클래스를 만들자.

ApplicationConfiguration 라는 이름으로 클래스를 만들었다. 내용은 다음과 같다.

@Import({DbConfiguration.class})
@PropertySource("classpath:application.properties")
@Configuration
@ComponentScan({"com.aram.smartstore.**.service"})
public class ApplicationConfiguration {}

빈을 등록할 때는 컴포넌트 스캔 방식을 사용하기에 클래스 내부에 직접 빈을 등록하지 않았다.

만약 직접 빈을 등록한다면 ApplicationConfiguration 클래스 내에 @Bean 어노테이션을 이용해 등록하면 된다.

위처럼 컴포넌트 스캔 방식을 사용하게 되면 com.aram.smartstore.**.service 에 해당하는 클래스 중 @Component 어노테이션이 붙은 클래스를 자동으로 빈에 등록한다.

@Import({DbConfiguration.class}) 는 DB관련 설정을 담고 있는 DbConfiguration 클래스도 ApplicationConfiguration 가 로딩되는 시점에 import하여 함께 로딩하겠다는 말이다.

따라서, DbConfiguration 에 등록되어 있는 빈들도 스프링 컨테이너에 등록된다.

DB configuration

나는 DB관련 설정도 해줬기에 다음처럼 DB연결을 위해 사용할 빈들을 등록해 주었다.

@Configuration
@MapperScan({"com.**.mapper"})
public class DbConfiguration {

  @Value("${db.driverClassName}")
  private String driverClassName;

  @Value("${db.url}")
  private String url;

  @Value("${db.username}")
  private String username;

  @Value("${db.password}")
  private String password;

  @Bean
  public DataSource dataSource() {
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    dataSource.setDriverClassName(driverClassName);

    return dataSource;
  }

  @Bean
  public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }

  @Bean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource, ApplicationContext applicationContext) throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource);

    org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
    configuration.setMapUnderscoreToCamelCase(true);
    factoryBean.setConfiguration(configuration);

//        factoryBean.setTypeAliasesPackage("com.**.dto");
//        factoryBean.setMapperLocations(applicationContext.getResources("classpath:com/**/mapper/*.xml"));
    return factoryBean.getObject();
  }

}

이제 ApplicationConfiguration 에서 위 설정을 @Import 하고 있으므로 ApplicationConfiguration 와 DB에 있는 빈이 함께 스프링 컨테이너에 등록된다.

Application Context 생성

다시 WebApplication 으로 돌아와서 위에서 만든 설정파일들을 통해 스프링 컨테이너를 생성하자.

AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(ApplicationConfiguration.class);

rootContext 라는 이름으로 스프링 컨테이너를 만들었다. 이때 ApplicationConfiguration 가 사용되는데 이 내부에 DB관련 설정도 함께 포함되어 있다.

이제 아까 사용했던 new ContextLoaderListener(rootContext); 코드가 이해 될 것이다.

ContextLoaderListener 는 위 스프링 컨테이너를 생성하고 관리하는 역할을 하게 된다.

여기까지 하게 되면 아래 그림의 (4)번 까지 하게 된 것이다.
서블릿 컨테이너에 ContextLoaderListener 를 등록하고 ContextLoaderListenerrootContext 라는 이름의 스프링 컨테이너를 생성하고 관리하고 있다.

클라이언트로 부터 웹 어플리케이션 요청이 옴

위에서 rootContext 를 생성할 때 web과 관련된 빈은 등록하지 않았다.
따라서 지금 상태로는 클라이언트로 부터 어떤 요청이 오더라도 해당 요청을 받아줄 컨트롤러가 없다.

먼저 클라이언트로 요청이 올 때 해당 요청을 받아 적절한 처리를 해줄 DispatcherServlet 이 필요하다.

디스패처 서블릿은 FrontController의 역할을 수행하여 클라이언트로 온 HTTP메세지를 분석해 알맞은 Controller에게 전달하고, 응답을 받아 응답해주는 역할을 수행한다.

먼저 디스패처 서블렛을 생성하자.

DispatcherServlet dispatcherServlet = new DispatcherServlet();

디스패처 서블릿은 컨트롤러와 관련된 빈들을 관리할 스프링 컨테이너를 구동한다.

따라서, web과 관련된 설정을 담고 있는(컨트롤러 빈을 관리할) 클래스를 만들자.

WebMvcConfigurer 를 구현한 WebConfiguration 클래스를 만들었다.

@Configuration
@EnableWebMvc
@ComponentScan({"com.aram.smartstore.**.controller"})
public class WebConfiguration implements WebMvcConfigurer {

  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(new MappingJackson2HttpMessageConverter());
  }
}

@Configuration@EnableWebMvc 어노테이션을 붙이면 된다.
@ComponentScan({"com.aram.smartstore.**.controller"}) 에 의해 컨트롤러 빈이 스프링 컨테이너에 등록된다.

나는 컨버터를 MappingJackson2HttpMessageConverter 로 설정했다.

이제 위 설정클래스를 이용해 ApplicationContext를 만들자. 방식은 rootContext와 같다.

AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
webContext.register(WebConfiguration.class);

webContext 라는 이름으로 ApplicationContext를 만들었다.

이제 앞서 생성한 디스패처 서블릿에 해당 context를 추가하면 된다.

dispatcherServlet.setApplicationContext(webContext);

이렇게 디스패처 서블릿을 생성하는 것 까지 완료했다.

서블릿 컨테이너에 디스패처 서블릿 추가

이제 서블릿 컨테이너에 앞서 만든 디스패처 서블릿을 추가하자.

ServletRegistration dispatcher = servletContext.addServlet("dispatcher", dispatcherServlet);

이제 모든 그림이 완성되었다.

클라이언트로 요청이 온다면 디스패처 서블릿 이 만들어지게 되고, 디스패처 서블릿은 WebConfiguration 를 로딩하여 컨트롤러를 관리하는 스프링 컨테이너를 만들게 된다.

이제 디스패처 서블릿은 클라이언트로 부터 온 요청을 분석하여 알맞은 컨트롤러에 보내고 응답 결과를 클라이언트로 보내주는 역할을 하게 된다.

이렇게하여 스프링 컨테이너가 어떤식으로 동작되는지 확인할 수 있었다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함