본문 바로가기
Spring

Spring MVC 구조(2)

by Big Sun 2022. 7. 29.
728x90

Spring MVC 구조 (tistory.com)

 

Spring MVC 구조

아래 그림은 Spring MVC구조의 흐름이다. 1 . 클라이언트의 모든 " *.do "요청을 DispatcherServlet이 받는다. 아래 코드는 WEB-INF/web.xml파일 안의 코드이다. => DispatcherServlet 등록 action org.springfra..

rasony.tistory.com

 

 

 

위의 구조를 보면 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이 생성한 두 번째 컨테이너) 부모 컨테이너가 생성한 객체를 참조할 수 있다. (역은 불가능하다.)

 

 

이상입니다.

728x90