본문 바로가기
Spring/인프런 토비의 Spring Boot

섹션 4-10 [독립 실행형 스프링 애플리케이션] Bean 생명주기 메소드

by include_hoany 2024. 6. 1.

Bean생명주기 메소드

지금까지 우리는 비즈니스를 처리하기 위해 HelloController, HelloService를 Bean으로 등록하고 관리하였습니다 그러나 이외 추가적으로 오브젝트를 새로 생성하는 게 두가지 정도가 있습니다. TomcatServletWebServerFactory, DispatcherServlet 입니다. Standart Alone애플리케이션을 만들기 위해서 보이지 않는곳에서 일을 해주는 오브젝트를 만들어야 해서 직접 코드로 생성을 했는데 이러한 오브젝트들도 Spring Bean으로 등록하여 관리될 수 있도록 코드를 수정하려고 합니다.

 

TobyspringbootApplication.class

package com.tobyspring.tobyspringboot;  
  
  
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;  
import org.springframework.boot.web.server.WebServer;  
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.ComponentScan;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;  
import org.springframework.web.servlet.DispatcherServlet;  
  
/*  
    Bean을 Spring Container가 인지하기 위해서는 한가지 작업을 더 해야하는데
    그게 뭐냐면 SpringContainer가 Bean오브젝트를 생성하는 Factory Method를 가진 클래스다
    라는것을 인지하도록 클래스 레벨에도 애노테이션을 붙여줘야 합니다.  
    그 어노테이션은 Configuration입니다.
*/  
/*  
    @ComponentScan 애노테이션은 Component 애노테이션이 붙은 클래스들을 찾아서 빈으로 등록하라는 명령을 
    @ComponentScan을 붙이는걸로 컨테이너에게 전달이 됩니다. @ComponentScan이 붙어있는 클래스가 있는 패키지부터
    시작해 그 하위 패키지를 확인해서 컴포넌트라는 애노테이션이 붙은 모든 클래스를 Bean으로 등록합니다
    Bean으로 등록할 때 필요하다면 의존 오브젝트를 찾아내고 이걸 생성자를 호출할 때 파라미터로 넘겨줍니다.
*/
@ComponentScan  
@Configuration  
public class TobyspringbootApplication {  
  
    @Bean  
    public ServletWebServerFactory servletWebServerFactory() {  
       return new TomcatServletWebServerFactory();  
    }  
  
    @Bean  
    public DispatcherServlet dispatcherServlet() {  
       return new DispatcherServlet();  
    }  
  
    public static void main(String[] args) {  
  
       /*  
          Spring Container 구현, DispatcherServlet을 사용하기 위해서는 GenericApplicationContext이 아닌
          GenericWebApplicationContext 형식으로 생성해야합니다. 앞서 Bean을 우리가 직접 클래스 타입을 구성정보로 등록했다면
          섹션 4-8에서는 구성정보를 어노테이션으로 등록을 합니다. 다만 애노테이션으로 구성정보를 등록할때는 GenericWebApplicationContext
          은 애노테이션 구성정보를 인지할 수 없습니다. 다라서 변경을 해야하는데 AnnotationConfigWebApplicationContext를 사용하면 애노테이션
          구성정보를 Spring Container가 인지할 수 있습니다.
      */
      AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext() {  
          @Override  
          protected void onRefresh() {  
             super.onRefresh();  
             
             // Bean으로 등록한 ServletWebServerFactory를 요청하여 오브젝트를 반환받는다. 
             ServletWebServerFactory serverFactory = this.getBean(ServletWebServerFactory.class);  
             
             // Bean으로 등록한 DispatcherServlet를 요청하여 오브젝트를 반환받는다.  
             DispatcherServlet dispatcherServlet = this.getBean(DispatcherServlet.class);  
             
             // Spring Container를 DispatcherServlet에 등록  
             /*                
             	그러나 구지 등록하지 않아도 Spring Container가 ApplicationContext를 등록해줍니다.
                DispatcherServlet는 ApplicationContextAward 인터페이스를 구현하고있기 때문에
                Spring Continer는 ApplicationContextAward인터페이스를 구현하고 있는 오브젝트라면
                해당 오브젝트에 ApplicationContext를 setter 메소드를 통해 주입해줍니다.
            	*/  
  
             dispatcherServlet.setApplicationContext(this);  
  
       /*  
          웹서버 서블릿 컨테이너를 생성하는 함수 serverFactory.getWebServer()  
          리턴타입이 디폴트로 설정한 Tomcat이라는 명칭은 사라지고  WebServer명칭으로 된이유는
          스프링 부트가 톰캣 외에 제티나 언더토우같은 다양한 서블릿 컨테이너를 지원할 수 있고
          지원하되 일관된 방식으로 사용할 수 있도록 동작하게할 수 있도록 추상화 해놨기 때문이다.
      	*/  
             WebServer webServer = serverFactory.getWebServer(servletContext -> {  
                // DispatcherServlet등록  
                servletContext.addServlet("dispatcherServlet", dispatcherServlet)  
                   .addMapping("/*");  
             });  
  
        /*  
          Servlet 컨테이너 동작 함수
      		*/
          webServer.start();  
  
          }  
       };  
  
       // 애토테이션으로 bean구성정보를 등록한다면 bean구성정보를 담고있다고 명시하는 클래스레벨에 명시한 애노테이션  
       // Configuration이 등록된 클래스를 구성정보로 Spring Container에 등록을 해줘야 합니다.
       applicationContext.register(TobyspringbootApplication.class);  
  
       // ApplicationContext가 refresh메소드를 통해 빈 오브젝트를 생성합니다.  
       applicationContext.refresh();  
  
    }  
  
}

 

HelloController.class

package com.tobyspring.tobyspringboot;  
  
import java.util.Objects;  
  
import org.springframework.beans.BeansException;  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.ApplicationContextAware;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
/*  
    DispatchServlet이 맵핑정보를 만들때 클랙스 레벨에 있는 정보를 먼저 참고한다.    그리고난 후 메소드 레벨에 붙어있는 정보를 추가합니다.
*/  
/*  
    Component라는 어노테이션으로 스프링컨테이너에 들어가는 컴포넌트야 라고 선언을 한다.
*/  
// @RestController애노테이션은 @Component애노테이션을 메타 애니토에션으로 포함하고 있기때문에  
// @Component애노테이션 생략을해도 Bean으로 등록이 가능합니다.  
  
// Spring Container가 ApplicationContextAware를 구현하고있는 오브젝트에 ApplicationContext를 주집하는지 테스트  
@RestController  
@RequestMapping("/hello")  
public class HelloController implements ApplicationContextAware {  
  
    private final HelloService helloService;  
  
    private ApplicationContext applicationContext;  
  
    public HelloController(HelloService helloService) {  
       this.helloService = helloService;  
    }  
  
    // GET 메소드를 사용하고 /hello 경로로 접근하는 요청을 매핑하겠다.  
    @GetMapping  
    /*  
     일반적으로 String으로 리턴을 했다면 String으로 전달된 값의 View가 존재하는지 확인합니다.
     다만 현재 String으로 전달된 값의 View를 찾을 수 없으면 404 Not Found 오류가 발생하게 됩니다.  
     String값을 그대로 Web 응답에 Body에 넣어서 전달하게 하는 텍스트 플레인으로 전달하게 하는 방식을 사용하려면
     ResponseBody를 사용하면 됩니다. @ResponseBody애노테이션은 @RestController에 메타 애노테이션으로 포함하고 있기 때문에
     생략이 가능합니다.
     */  
    // @ResponseBody 
    // @RequestMapping을 사용해도 되지만 간결하게 사용하기위해 @GetMapping로 합쳐진 애노테이션이 만들어졌다.
    // @RequestMapping(method = RequestMethod.GET, value = "/hello")
    public String hello(String name) {  
       return helloService.sayHello(Objects.requireNonNull(name));  
    }  
  
    // Spring Container가 ApplicationContextAware를 구현하고있는 오브젝트에 ApplicationContext를 주집하는지 테스트  
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
       System.out.println(applicationContext);  
       this.applicationContext = applicationContext;  
    }  
}