위의 구조를 보면 Controller가 DAO객체를 직접 이용하고 있다. 하지만, DAO클래스를 교체해야할 경우에 큰 어려움을 겪을 수 있다.
따라서, Controller가 인터페이스를 통해 비즈니스 컴포넌트를 이용하면 컴포넌트의 구현 클래스를 수정하거나 다른 클래스로 대체해도 이를 사용하는 클라이언트는 수정하지 않아도 된다. => 객체지향 언어의 특성 이용
@Controller
public class BoardController {
@Autowired
private BoardService boardService;
@RequestMapping("/insertBoard.do")
public String insertBoard(BoardVO vo) throws IOException{
System.out.println("글 등록 처리");
MultipartFile uploadFile = vo.getUploadFile();
if(!uploadFile.isEmpty()) {
String fileName = uploadFile.getOriginalFilename();
uploadFile.transferTo(new File("C:/myProject/" + fileName));
}
boardService.insertBoard(vo);
return "getBoardList.do";
}
@RequestMapping("/updateBoard.do")
public String updateBoard(@ModelAttribute("board")BoardVO vo) {
boardService.updateBoard(vo);
return "getBoardList.do";
}
@RequestMapping("/deleteBoard.do")
public String deleteBoard(BoardVO vo) {
System.out.println("글 삭제 처리");
boardService.deleteBoard(vo);
return "getBoardList.do";
}
@RequestMapping("/getBoard.do")
public String getBoard(BoardVO vo, Model model) {
System.out.println("글 상세보기 처리");
BoardVO board = boardService.getBoard(vo);
model.addAttribute("board",board);
return "getBoard.jsp";
}
@RequestMapping("/getBoardList.do")
public String getBoardList(BoardVO vo,Model model) {
System.out.println("글 목록 보기 처리");
List<BoardVO> boardList = boardService.getBoardList(vo);
model.addAttribute("boardList",boardList);
return "getBoardList.jsp";
}
}
BoardService 라는 인터페이스가 있고 이를 구현한 BoardServiceImpl 이라는 클래스 안에 DAO 클라스가 멤버변수로 있어, DAO 클라스를 쉽게 수정하거나 교체할 수 있다.
AOP 적용 : Controller 클래스는 비즈니스 컴포넌트의 인터페이스 타입의 멤버변수를 가지고 있으므로 이 변수에 비즈니스 객체를 의존성 주입해야 한다. 그러면 Controller 메서드는 인터페이스를 통하여 비즈니스 객체의 메서드를 호출할 수 있고, 결국 AOP로 설정한 어드바이스들이 동작한다.
여기서 문제가 발생한다.
BoardController가 @Autowired로 의존성 주입을 처리하려고 하는 데 BoardService 타입의 객체가 메모리에 없어서 의존성 주입을 할 수 없다. - > BoardController 보다 의존성 주입될 BoardServieimpe 객체가 먼저 생성되어야 한다.
(presentation-layer.xml 파일에서는 Controller 객체만 컴포넌트 스캔하도록 설정했기 때문에 BoardServiceImpl 객체는 생성되지 않는다)
결국, Controller 보다 의존성 주입 대상이 되는 비즈니스 컴포넌트를 먼저 생성하려면 비즈니스 컴포넌트를 생성하는 또 다른 스프링 컨테인너가 필요하고, 이는 Controller를 메모리에 생성하는 스프링 컨테이너보다 먼저 구동되어야한다.
이를 위해 ContextLoaderListener를 사용한다.
web.xml파일
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListner</listener-class>
</listener>
ContextLoaderListner는 기본적으로 /WEB-INF/applicationContext.xml 파일을 읽어서 구동시킨다.
하지만, 추후에 유지보수를 위해 applicationContext.xml 파일을 src/main/resources 폴더에 위치시킨다.
이를 위해 <context-param>설정을 추가한다.
이제, AOP 적용이 가능하다.
스프링 컨테이너의 관계를 정리해보면, 아래 그림과 같다.
먼저, 서버(톰캣)을 구동하면 web.xml 파일을 로딩하여 Servlet Container가 구동된다.
서블릿 컨테이너는 web.xml파일의 ContextLoaderListener객체를 로딩한다.
ContextLoaderListener 객체를 생성하고 이 객체는 applicationContext파일을 로딩하여 스프링 컨테이너를 만든다.
이를 Root Container라 부른다. 여기서 Service 구현 클래스나 DAO객체들이 메모리에 생성된다.
사용자가 *.do 요청을 서버에 전달하면 서블릿 컨테이너는 DispatcherServlet 객체를 생성하고 이 객체는 presentation-layer.xml이라는 파일을 로딩하여 두번째 스프링 컨테이너를 만든다. 여기서 Controller 객체가 메모리에 생성된다.
Root 컨테이너는 부모 컨테이너이며 자식 컨테이너는(DispatcherServlet이 생성한 두 번째 컨테이너) 부모 컨테이너가 생성한 객체를 참조할 수 있다. (역은 불가능하다.)
이상입니다.
'Spring' 카테고리의 다른 글
BeanCreationException, NoSuchBeanDefinitionException 에러 해결 (0) | 2022.08.19 |
---|---|
this.userDAO is null - NullPointerException (0) | 2022.08.19 |
Spring - BeanCreationException해결 / .metadata (0) | 2022.07.26 |
Spring MVC 구조 (0) | 2022.07.26 |
Spring - 트랜잭션 처리 (0) | 2022.07.21 |