public class SpringbootmineApplication {
public static void main(String[] args) {
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer();
webServer.start();
}
}
@SpringBootApplication annotation 없이 직접 tomcat을 기동 시키는 과정이다.
public class HellobootApplication {
public static void main(String[] args) {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean(HelloController.class);
applicationContext.registerBean(SimpleHelloService.class);
applicationContext.refresh();
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer(servletContext -> {
servletContext.addServlet("hello", new HttpServlet() {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 인증, 보안, 다국어, 공통 기능
if (req.getRequestURI().equals("/hello") && req.getMethod().equals(HttpMethod.GET.name())) {
String name = req.getParameter("name");
HelloController helloController = applicationContext.getBean(HelloController.class);
String ret = helloController.hello(name);
resp.setContentType(MediaType.TEXT_PLAIN_VALUE);
resp.getWriter().println(ret);
}
else {
resp.setStatus(HttpStatus.NOT_FOUND.value());
}
}
}).addMapping("/*");
});
webServer.start();
}
}
tomcat(servlet container)을 메뉴얼하게 하나 띄워서
hello servlet을 하나 추가하였고 if else 분기문을 통해 FrontController를 구현하였다. 또한 HelloController , HelloService를 spring bean container에서 관리하고 사용하고 있다.
public class HelloController {
private final HelloService helloService;
public HelloController(HelloService helloService) {
this.helloService = helloService;
}
public String hello(String name) {
return helloService.sayHello(Objects.requireNonNull(name));
}
}
HelloController 빈을 register하고 HelloService도 register하고 나서 refresh 해주는 순간 생성자 주입을 런타임에 넣어준다.
등록하는 순서는 전혀 상관이없다.
public static void main(String[] args) {
GenericWebApplicationContext applicationContext = new GenericWebApplicationContext();
applicationContext.registerBean(HelloController.class);
applicationContext.registerBean(SimpleHelloService.class);
applicationContext.refresh();
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer(servletContext -> {
servletContext.addServlet("dispatcherServlet",
new DispatcherServlet(applicationContext))
.addMapping("/*");
});
webServer.start();
}
앞의 mapping 하는과정이 DispatcherServlet이 담당하게 되었다. 해당 서블릿이 routing을 담당하니 어떤 bean에게 보내줘야할지 알아야하니 ioc container를 넘겨주게된다.
현재는 아직 어떤 routing 정보도 주지 않아서 404 에러가 나게된다. xml, annotation 같은 방법으로 routing 정보를 넘겨주어야한다.
@RequestMapping("/hello")
public class HelloController {
private final HelloService helloService;
public HelloController(HelloService helloService) {
this.helloService = helloService;
}
@GetMapping
@ResponseBody
public String hello(String name) {
return helloService.sayHello(Objects.requireNonNull(name));
}
}
helloService 레이어에서 전달해주는 값이 String은 맞지만 만약 @ResponseBody가 없었다면 그 에 알맞은 .html 을 찾으려할텐데 없어서 404 에러를 던져줄것이다.
@ResponseBody를 통해 http body에 고대로 응답을 넣어준것이다.
public static void main(String[] args) {
GenericWebApplicationContext applicationContext = new GenericWebApplicationContext() {
@Override
protected void onRefresh() {
super.onRefresh();
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer(servletContext -> {
servletContext.addServlet("dispatcherServlet",
new DispatcherServlet(this))
.addMapping("/*");
});
webServer.start();
}
};
applicationContext.registerBean(HelloController.class);
applicationContext.registerBean(SimpleHelloService.class);
applicationContext.refresh();
}
refresh 메소드를 통해 bean 초기화 과정중에 부가적으로 할일이 있을 때 onRefresh 메소드를 사용하면된다.
@Configuration
public class HellobootApplication {
@Bean
public HelloController helloController(HelloService helloService) {
return new HelloController(helloService);
}
@Bean
public HelloService helloService() {
return new SimpleHelloService();
}
public static void main(String[] args) {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext() {
@Override
protected void onRefresh() {
super.onRefresh();
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer(servletContext -> {
servletContext.addServlet("dispatcherServlet",
new DispatcherServlet(this))
.addMapping("/*");
});
webServer.start();
}
};
applicationContext.register(HellobootApplication.class);
applicationContext.refresh();
}
}
자바 코드로 bean을 생성하는 방법이다. @Configuration이 적힌 클래스를 applicationContext에 register해줘야 내부에 @Bean이 동작하면서 필요한 빈들을 생성하고 주입해준다.
@Configuration
@ComponentScan
public class HellobootApplication {
public static void main(String[] args) {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext() {
@Override
protected void onRefresh() {
super.onRefresh();
ServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
WebServer webServer = serverFactory.getWebServer(servletContext -> {
servletContext.addServlet("dispatcherServlet",
new DispatcherServlet(this))
.addMapping("/*");
});
webServer.start();
}
};
applicationContext.register(HellobootApplication.class);
applicationContext.refresh();
}
}
@ComponentScan 은 현재 클래스가 있는 패키지 및 하위 패키지들을 탐색해서 @Component 가 붙은 클래스를 빈으로 만들어주게 된다.
@Configuration
@ComponentScan
public class HellobootApplication {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
public static void main(String[] args) {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext() {
@Override
protected void onRefresh() {
super.onRefresh();
ServletWebServerFactory serverFactory = this.getBean(ServletWebServerFactory.class);
DispatcherServlet dispatcherServlet = this.getBean(DispatcherServlet.class);
WebServer webServer = serverFactory.getWebServer(servletContext -> {
servletContext.addServlet("dispatcherServlet", dispatcherServlet)
.addMapping("/*");
});
webServer.start();
}
};
applicationContext.register(HellobootApplication.class);
applicationContext.refresh();
}
}
factory method를 이용하여 dispatcherServlet을 빈으로 만들었지만 어디에서도 dispatcherServlet이 필요한 applicationContext를 주입하지 않았다.
DispatcherServlet은 ApplicationContextAware를 구현하고 있다. 해당 interface를 구현한 메소드는 spring container(application context)에서 setter를 이용해서 applicationcontext를 주입해준다.
@Configuration
@ComponentScan
public class HellobootApplication {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
public static void main(String[] args) {
MySpringApplication.run(HellobootApplication.class, args);
}
}
기존에 main method안에 있던 로직을 메소드로 만들고 클래스를 인자로 받아서 scan할대상도 주입하고 있는것을 확인
spring boot와 굉장히 유사해졌다.
'WEB > Spring Boot' 카테고리의 다른 글
웹 서버와 서블릿 컨테이너 (0) | 2023.05.08 |
---|---|
스프링 부트 자세히 살펴보기 (0) | 2023.02.14 |
Spring JDBC 자동 구성 개발 (0) | 2023.02.14 |
외부 설정을 이용한 자동 구성 (0) | 2023.02.13 |
자동 구성 기반 애플리케이션 + 조건부 자동 구성 (0) | 2023.02.09 |