복습

Spring Project (Web Application) 구조

 

환경설정 파일 정리

1. Spring 환경설정 파일

- servlet-context.xml

- root-context.xml

 

2. 프로젝트 환경설정 파일

- web.xml

 

3. Maven 환경설정 파일

- pom.xml

 

root-context.xml

- DB 연동 관련 내용

- 주로 bean 을 만들어서 DB 접속을 처리한다

- 어노테이션 기반 DI + Setter DI 도 사용함

- beans 루트 엘리먼트 안에 bean 을 추가해서 사용

- 어노테이션 방식 + 직접 bean 객체 생성 방식 둘다 사용함

 

servlet-context.xml

- View 파일들이 저장된 최상위 디렉토리 위치를 잡음, 그래야 View 파일로 찾아갈 수 있다

- ViewResolve (prefix, suffix)

- ViewResolve 에도 Setter DI 사용함

 

 

- 어노테이션 기반 DI로 처리됨

 

 어노테이션 기반 DI 사용 조건

1. servlet-context.xml 파일에 base-package 가 지정되어야한다

 

2.해당 클래스위에 4가지 어노테이션 중 하나가 와야한다

3. 주입할 프로퍼티(객체) 위에 @Autowired 작성

 

어노테이션 기반 DI

- Controller 클래스에서 Service 클래스로 넘어가는 법 : Service 객체를 생성해서 메소드를 호출

- Service 클래스에서 DAO 클래스로 넘어가는 방법 : DAO 객체를 생성해서 메소드 호출

- 객체를 생성할때 어노테이션 기반 DI 로 생성하는 것임


Spring Web Application 예제 1

실습 준비

- 기본파일들이 있는 클라우드의 hello 프로젝트를 STS로 import

 

Spring 예제 1

hello 프로젝트 구조

- src/main/java 안에 보통 DAO, Service, Controller 클래스들이 들어있다, 현재는 Controller 클래스만 존재

- View 파일들은 view 폴더 안에 저장되어 있다

- index 파일은 반드시 webapp 폴더 안에 있어야한다

 

- 파일을 하나씩 살펴보자


hello 프로젝트의 환경설정 파일 1 : Maven 환경설정 파일

pom.xml

- Maven 환경설정 파일 pom.xml 에 이미 기본적인 내용이 세팅되어 있다

 

pom.xml 에 작성된 Java 버전, Spring 버전

 

pom.xml 에 추가된 의존 라이브러리 부분 설명

- jacskon 라이브러리 : 콜백함수 비동기로 처리할때 DTO, 리스트를 Json 으로 변환시켜줄 수 있다

- 오라클 비공식 원격저장소, 오라클 JDBC 라이브러리

- MyBatis, MyBatis-Spring 연결 라이브러리 : MyBatis 라이브러리 뿐 아니라 MyBatis-Spring 연결 라이브러리까지 필요

- inject 라이브러리 : @inject 어노테이션 사용할때 필요 (@Autowired 어노테이션과 같은 역할)

- commons-fileupload 라이브러리 : cos 라이브러리를 대신해서 첨부파일 업로드를 위한 라이브러리


hello 프로젝트의 환경설정 파일 2 : 프로젝트 환경설정 파일

web.xml

- Dynamic Project 때부터 계속 만들어졌던 파일

- Apache Tomcat 이 구동될때 자동으로 실행되며, 가장 먼저 읽어오는 파일

- WEB-INF 폴더 내에 있어야 한다

 

web.xml 에 들어가는 주요 3가지 내용

1. Dispatcher Servlet 이름, 위치 및 매핑 설정

- web.xml 부분

- <servlet-class> : Dispatcher Servlet 위치 설정

- <servlet> : 요청시 내부적으로 Dispatcher Servlet (Front Controller) 로 먼저 찾아가기 위해 web.xml 에 Dispatehcer Servlet 의 이름과 위치 등록

- <servlet-mapping> : Dispatcher Servlet 으로 찾아가도록 Servlet 매핑을 잡는 내용

- <servlet-name> : <servlet> 과 <servlet-mapping> 의 <servlet-name> 을 일치시켜야한다

- <url-pattern> : 우리는 이 패턴값만 필요에 따라 조절하면 된다, Model 2 에선 @WebServlet 어노테이션으로 찾아갔었다

 

2. 환경설정 파일 2개 불러오기 : root-context.xml 과 servlet-context.xml 파일

- web.xml 부분

- web.xml 파일은 Apache Tomcat 구동시 자동으로 실행되므로, 자동으로 실행 안되는 다른 환경설정 파일 root-context.xml, servlet-context.xml 을 여기서 부른다

- Application 에서는 main() 메소드에서 환경설정 파일을 직접 읽어왔지만 여기서는 web.xml 이 불러주므로 root-context.xml, servlet-context.xml 은 자동으로 실행됨

+ 만약 이 두 환경설정 파일을 resources 폴더에 넣는다면, classpath: 를 앞에 붙여서 경로를 잡아야한다

 

3. 한글 인코딩

- web.xml 부분

- CharacterEncodingFilter 는 Spring 지정 라이브러리

- 한글값을 post 방식으로 전송할때 한글값을 UTF-8 로 인코딩 시켜주는 역할을 한다

- 자동으로 들어가있지 않고 추가해야하는 코드이다

- 더이상 값이 넘어오는 파일이나, Service 클래스에서 request.setCharacterEncoding("utf-8") 코드를 작성할 필요가 없다


hello 프로젝트의 환경설정 파일 3 : Spring 환경설정 파일

root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
		
</beans>

- DB 연동에 필요한 내용이 들어갈 것

- 현재는 DB연동을 안하는 프로젝트 이므로 비어있다

- 주로 필요한 bean 을 직접 안에 추가한다 이때 주입시엔 주로 Setter DI 사용 + Constructor DI 도 사용

- 어노테이션 기반 DI 도 사용한다


hello 프로젝트의 환경설정 파일 4 : Spring 환경설정 파일

servlet-context.xml

- DB 연동에 필요한 파일만 root-context.xml 에서 처리, 나머지 대부분의 환경설정은 주로 servlet-context.xml 이 처리

 

1. 어노테이션 기반으로 bean 을 사용하기 위해 base-package 를 지정

- base-package 로 지정된 패키지안의 모든 클래스들을 읽어온다는 의미

- base-package 로 지정된 패키지안의 클래스들은 4가지 어노테이션 중 하나의 어노테이션이 있어야한다

ex) Service 클래스 위에는 @Servlet 어노테이션, Controller 클래스 위에는 @Controller 어노테이션

+ 현재는 DB연동을 하지 않으므로 Service 클래스는 없고 Controller 클래스와 DTO 클래스가 있음

 

+ top-level 패키지

- Spring Project 만들때 설정했던 top-level 패키지 com.ch.hello 가 src/main/java 안에 들어가 있다

- 나중엔 이 top-level 패키지를 쓰지 않고 다른 패키지를 직접 만들어서 사용함

 

2. ViewResolve 로 View 페이지가 저장될 최상위 디렉토리 설정, 확장자 설정

- Spring 에서 지원되는 InternalResourceViewResolver 클래스로 bean 객체를 생성해서 Setter DI 로 주입하고 있다

- prefix : View 파일들이 저장된 최상위 디렉토리

- suffix : View 파일의 확장자

+ InternalResourceViewRoslver 클래스에는 필드 prefix, suffix 와 Setter 메소드들이 있음

- Controller 클래스에서 리턴할땐 경로인 prefix 와 확장자인 suffix 를 생략해서 return 해야한다

- /WEB-INF/view/home.jsp 가 아닌 "home" 만 작성해서 return 해야함

 

3. 기본적인 매핑을 통해 View 페이지 위치 설정

- 다음에 설명


hello 프로젝트 기능

- index 파일인 index.jsp 를 실행해보자

- hello 클릭시 현재 시간 출력

- 배경색 클릭시 랜덤하게 배경색을 보여줌

- 구구단 클릭시 랜덤하게 구구단 한단을 보여줌

- 회원가입 클릭시 회원가입 폼이 나오고, 회원가입 시킬수 있다 (DB연동은 되어있지 않고 메모리에만 저장)


index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	function chk() {
		var url = document.getElementById("url").value;
		location.href = url;
	}
</script>
</head>
<body>
	<h2>원하는 작업을 선택하세요</h2>
	<!-- <script type="text/javascript">
			location.href="hello";
		</script> -->
	<select id="url" onchange="chk()">
		<option value="hello">hello</option>
		<option value="color">배경색</option>
		<option value="gugu">구구단</option>
		<option value="joinForm">회원가입</option>
	</select>
</body>
</html>

 

- select 옵션에서 옵션 선택시 change 이벤트 발생, onchage 시 chk() 호출

- chk() 에서 id 가 url 인 태그, 즉 select 태그를 구해오고 value 속성으로 옵션의 value 구해온다

- "gugu" 선택시 location.href 를 "gugu" 로 설정하며 요청하게 됨

 

- 요청을 하면 HomeController.java 의 @RequestMapping 에서 요청을 받는다

+ 옛날 버전에서 사용했던 Handler Mapping 대신 현재는 @RequestMapping 어노테이션으로 요청을 받음


@RequestMapping 어노테이션

- Controller 클래스에서 클라이언트 요청을 받을때 사용하는 어노테이션

- Get / Post 방식으로 요청을 받을 수 있다

ex) index.jsp 에서 요청을 하면 HomeController.java 의 @RequestMapping 에서 요청을 받는다

- value 속성값 : 요청 이름값을 작성, value 값은 서로 다르게 작성해야 한다

- method 속성값 : get 방식으로 요청시 RequestMethod.GET, post 방식으로 요청시 RequestMethod.POST 라 작성

- 그 요청을 받을때 아래의 메소드가 자동으로 실행된다

 

주의

- value 속성값은 "/hello" 처럼 요청이름명 앞에 / 를 붙여도 되고, "hello" 처럼 / 를 빼도 된다.

- 하지만 요청을 보내는 쪽에서는 "/" 를 써선 안된다

- 또한 value 도 생략해도 된다, 바로 value 속성값만 쓰면 된다

- 그리고 Get 방식은 method 속성 자체를 생략해도 된다


Spring MVC 패턴 흐름 설명 예시 1

 

index.jsp 에서 location 객체를 통해 "hello" 로 요청이 왔다면

- select-option 에서 "hello" 를 선택시 "hello" 로 요청이 간다

- 클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스 -> Dispatcher Servlet -> View 페이지

- 이 흐름을 반으로 나눠서 각각 설명

 

클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스

1. 가장 먼저 Dispatcher Servlet 으로 찾아감

- web.xml 에 Servlet 매핑을 "/" 로 잡아뒀으므로 아무거나 요청해도 Dispatcher Servlet 으로 먼저 찾아간다

 

2. 내가 만든 Controller 클래스에서 @RequestMapping 중 요청 이름값과 요청 방식이 일치하는 곳으로 간다

- 아래 코드가 요청 이름값과 요청 방식이 일치하는 곳이다. 

- 요청이름값인 value 는 모두 달라야한다

+ 옛날 버전에서 사용했던 Handler Mapping 대신 현재는 @RequestMapping 어노테이션으로 요청을 받음

 

3. 찾아간 @RequestMapping 아래의 메소드가 자동 호출됨

- hello() 가 자동으로 실행됨

+ 주로 요청이름값과 메소드 명을 일치시키는 경우가 많다 ex) "/hello" 요청시 실행되는 메소드는 hello()

+ 메소드 앞의 자료형은 대부분 String 자료형이 온다

 

+ 3-1. 값을 가져갈때만 :

- View 페이지로 값을 가져가기 위해 Model 객체 또는 ModelAndView 객체를 사용

- 매개변수로 Model 을 설정하면 Model 객체가 자동으로 생성된다

- 위에 Model 클래스를 import 하고 있음

+ 매개변수로 Session 을 설정하면 Session 객체가 자동으로 생성된다

 

Controller 클래스 -> Dispatcher Servlet -> View 페이지

+ 현재는 DB연동하지 않으므로 Service 클래스로 가지 않고 Controller 에서 바로 View 로 이동

 

hello() 안의 내용

4. Date 객체 생성하고 DateFormat 객체로 날짜 시간을 LONG 형으로 길게 설정하고 format() 으로 포맷 적용

 

5. Model 객체 model 로 addAttribute() 메소드를 사용해서 키-밸류 형태로 값을 가져간다

 

6. return "home" 처럼 return 뒤에 View 페이지의 경로값 설정

- servlet-context.xml 에서 prefix, suffix 로 설정했던 것을 생략해야함

- 리턴자료형이 String 이므로 String 형으로 리턴

 

7. Dispatcher Servlet 으로 내부적으로 이동 -> ViewResolver 에서 prefix, suffix 붙이기 -> View 인 WEB-INF/views 안의 home.jsp 로 이동

- servlet-context.xml 의 ViewResolver 에서 설정했던 prefix, suffix 를 붙여서 그 경로로 찾아간다

 

8. View 페이지에서 이 값을 가져온다

- home.jsp 부분

- value 값으로 어떤 값의 형태가 오는지에 따라 사용방법이 달라진다 (기본자료형, DTO, 리스트)

 

- "hello" 요청 캡처


Spring MVC 패턴 흐름 설명 예시 2 (간략)

 

index.jsp 에서 location 객체를 통해 "color" 로 요청이 왔다면 (간략)

- select-option 에서 "배경색" 를 선택시 "color" 로 요청이 간다

- 클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스 -> Dispatcher Servlet -> View 페이지

 

1. index.jsp 에서 location.href 를 통해 "color" 로 요청

2. 내부적으로 Dispatcher Servlet 으로 찾아감

3. HomeController 클래스에서 요청 이름값, 요청 방식이 일치하는 @RequestMapping 으로 "color" 요청을 받음

- 이름값이 맞으면 무조건 값을 받는다

- Contorller 클래스는 여러개일 수 있지만 value 값은 달라야함

4. 난수를 발생시켜서 랜덤으로 색 하나를 구한 뒤 그걸 Model 객체 model 에 value 로 저장함

5. return "color" 와 ViewResolver의 prefix, suffix 에 의해 View 페이지인 /WEB-INF/view/color.jsp 로 찾아간다 

6. color.jsp 에서 body 태그의 bgcolor 속성으로 ${color} 로 해당 값 불러오기

 

- "color" 요청 캡처


Spring MVC 패턴 흐름 설명 예시 3 (간략)

 

index.jsp 에서 location 객체를 통해 "gugu" 로 요청이 왔다면 (간략)

- select-option 에서 "배경색" 를 선택시 "gugu" 로 요청이 간다

- 클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스 -> Dispatcher Servlet -> View 페이지

 

1. index.jsp 에서 location.href 를 통해 "gugu" 로 요청

2. 내부적으로 Dispatcher Servlet 으로 찾아감

3. HomeController 클래스에서 요청 이름값, 요청 방식이 일치하는 @RequestMapping 으로 "gugu" 요청을 받음

- 이름값이 맞으면 무조건 값을 받는다

- Contorller 클래스는 여러개일 수 있지만 value 값은 달라야함

4. 난수를 발생시켜서 랜덤으로 2 ~ 9 중 하나의 숫자를 Model 객체 model 에 value 로 저장함

+ View 페이지에 가져갈 값이 없을땐 매개변수에 아무것도 작성하지 않아도 된다

- 값을 가져갈때만 매개변수로 Model 객체를 받는다

5. return "gugu" 와 ViewResolver의 prefix, suffix 에 의해 View 페이지인 /WEB-INF/views/gugu.jsp 로 찾아간다 

- 이때 이 파일은 반드시 설정되어있는 WEB-INF/views 안에 있어야한다

6. gugu.jsp 에서 JSTL forEach 태그 안에서 해당 단의 구구단을 구하기

 

+ JSTL 코어 라이브러리 불러오기

 

- "gugu" 요청 캡처


Spring MVC 패턴 흐름 설명 예시 4 (간략)

 

index.jsp 에서 location 객체를 통해 "joinForm" 로 요청이 왔다면 (간략)

- select-option 에서 "회원가입" 를 선택시 "joinForm" 로 요청이 간다

 

Controller 클래스는 여러개 만들 수 있다, @RequestMapping 값만 서로 다르게 설정하면 된다

- HomeController 클래스 안에는 "joinForm" 요청을 받는 어노테이션이 없다

- JoinController 클래스에서 @RequestMapping("/joinForm") 으로 요청을 받는 어노테이션이 있다

- JoinController 클래스로 간다

 

- 클라이언트 요청 -> Dispatcher Servlet -> Controller 클래스(JoinController.java) -> Dispatcher Servlet -> View 페이지

 

1. index.jsp 에서 location.href 를 통해 "joinForm" 로 요청

2. 내부적으로 Dispatcher Servlet 으로 찾아감

3. JoinController 클래스에서 요청 이름값, 요청 방식이 일치하는 @RequestMapping 으로 "joinForm" 요청을 받음

 

- 이름값이 맞으면 무조건 값을 받는다

- Contorller 클래스는 여러개일 수 있지만 value 값은 달라야함

4. return "joinForm" 와 ViewResolver의 prefix, suffix 에 의해 View 페이지인 /WEB-INF/views/joinForm.jsp 로 찾아간다 

- 바로 회원가입 폼인 "joinForm.jsp 로 간다

- View 페이지에 가져갈 값이 없으므로 매개변수에 아무것도 작성하지 않아도 된다

- 이때 이 파일은 반드시 설정되어있는 WEB-INF/views 안에 있어야한다

6. joinForm.jsp 인 회원가입 폼으로 간다

 

7. 전송버튼 누르면 post 방식 "join" 으로 요청한다

- 다시 Dispatcher Servlet -> Controller 클래스로 간다

- 입력양식의 name 값들을 유심히 보기

- 회원가입 할때 회원 정보를 저장하기 위한 DTO 클래스 Member 의 프로퍼티명과 입력양식의 name 값들을 일치시켜야한다

- 그래야만 매핑을 통해 자동으로 DTO 객체에 입력양식의 값들이 들어간다 * 아래에 자세히 설명

- Member.java 부분

8. Controller 클래스에 @RequestMapping 으로 찾아가서 join() 메소드 실행

9. 매개변수로 이미 값이 자동 세팅된 DTO 객체를 받는다

- DTO 객체인 member 에 값을 세팅하지 않아도 폼에서 넘어온 입력양식의 값들이 자동으로 DTO 객체 member 안에 세팅됨

 

- Member member 앞에 @ModelAttribute 생략된 것임

- 이 @ModelAttribute 가 뒤쪽의 DTO 객체에 Setter 메소드로 자동으로 세팅해준다

- 다만, DTO 객체의 필드명과 입력양식의 name 값들이 일치할때만 가능하다!

- 그 자동 세팅된 객체 member 를 View 로 보내기 위해 addAttribute() 사용

10. Model 객체 model 을 통해 addAttribute() 로 DTO 객체 member 를 저장

11. return "joinResult" 를 통해 WEB-INF/views/joinResult.jsp 로 간다

12. DTO 객체가 Model 객체에 저장되었으므로 View 페이지에서 EL 로 출력할때는 ${공유네임값.필드명} 인 ${member.id} 로 출력

 


@ModelAttribute 어노테이션

- 어노테이션을 선언하고 뒤에는 DTO 객체를 선언한다

- 뒤쪽의 DTO 객체에 폼을 통해 한꺼번에 넘어온 값들을 세팅해준다

- 가입되는 양식의 name 값 과 DTO 클래스의 필드명을 일치시켜야만 가능

- @ModelAttribute 어노테이션을 생략해도 DTO 필드명과 name값이 일치하면 DTO 객체에 값이 자동으로 들어감

- 더이상 Setter 메소드로 넘어온 값들을 DTO 객체에 값을 설정하지 않아도 된다

- 그래서 가입되는 양식의 name 값 과 DTO 클래스의 필드명을 일치시켜야함


+ header.jsp 파일에는 공통적으로 들어가는 내용들이 있다, 그걸 include 하고 있다

- joinForm.jsp 부분

- header.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery.js"></script>
<script src="js/bootstrap.min.js"></script>
<style>	.err { color: red; font-size: 20px;  } </style>

- 부트스트랩을 적용, 각종 Javascript 파일을 포함

 

- "joinForm" 요청 캡처

- @ModelAttribuate 로 DTO 객체에 값이 세팅되는지 확인하기 위해 위 코드 작성 후 콘솔 확인

- 아이디를 잘 출력하는 것을 보아 DTO 객체에 값이 잘 세팅되었음을 확인 가능


요청이름명으로 Controller 로 찾아갈때 주의

- value 속성값은 "/hello" 처럼 요청이름명 앞에 / 를 붙여도 되고, "hello" 처럼 / 를 빼도 된다.

- 또한 value 도 생략해도 된다, 바로 value 속성값만 쓰면 된다

- 그리고 Get 방식은 method 속성 자체를 생략해도 된다

 

- 하지만 요청을 보내는 쪽에서는 "/" 를 써선 안된다

 

- joinForm.jsp 부분

- index.jsp 부분


- HomeController.java (Controller 클래스, 전체 코드)

package com.ch.hello;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

	@RequestMapping(value = "/hello", method = RequestMethod.GET)
	public String hello(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		String formattedDate = dateFormat.format(date);
		model.addAttribute("serverTime", formattedDate);
		return "home";
	}

	@RequestMapping("/color")
	public String color(Model model) {
		String[] color = { "red", "orange", "yellow", "green", "blue", "navy", "violet" };
		int num = (int) (Math.random() * 7);
		model.addAttribute("color", color[num]);
		return "color";
	}

	@RequestMapping("/gugu")
	public String gugu(Model model) {
		int num = (int) (Math.random() * 8) + 2;
//		int a = num / 0;
		model.addAttribute("num", num);
		return "gugu";
	}
	/*
	 * @ExceptionHandler(ArithmeticException.class) 
	 * public String err() { 
	 * return
	 * "arr-err"; }
	 */
}

- 새로운 어노테이션을 보기 위해 다른 파일을 보자

 

- person.jsp (수정 전)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
	<title>Insert title here</title>
</head>
<body>
	<h2>이름과 주소</h2>
	<form action="addr">
	<!-- <form action="addr2"> -->
		이름 : <input type="text" name="name">
		<p>
			주소 : <input type="text" name="addr">
		<p>
			<input type="submit" value="확인">
	</form>
</body>
</html>

- action 값이 "addr" 인 경우 form 태그 먼저 설명 * action 값이 "addr2" 인 경우는 아래에서 설명

- 이 person.jsp 실행시 이름, 주소 입력 양식

- 이름 입력양식의 name 값은 "name", 이 값을 받기 위해 request.getParameter() 를 사용했었다

- 주소 입력양식의 name 값은 "addr", 이 값을 받기 위해 request.getParameter() 를 사용했었다

- 이제 이걸 어노테이션으로 대체 가능

 

- '확인'을 누르면 이름과 주소가 Dispatcher Servlet -> Controller 클래스로 간다

- Controller 클래스에서 "addr" 을 받는 @RequestMapping 어노테이션을 찾아보자

- PersonController.java 부분

- 위에 @Controller 어노테이션이 붙어있는 Controller 클래스 중 하나이다

- action 값이 "addr" 인 요청을 @RequestMapping 어노테이션이 있다

- 폼에서 넘어온 값을 받아보자

 

폼에서 넘어온 값 받기

- @RequestParam 어노테이션을 통해 앞에서 넘어온 값들을 name 값을 통해 가져와서 매개변수에 넣어준다

 

@RequestParam 어노테이션

- name 으로 값을 받을때 사용하는 어노테이션

- @RequestParam("name") 으로 앞에서 넘어온 값을 받아서 뒤쪽의 String 형 변수 name 에 저장한다

- @ReuqestParam("addr") 으로 앞에서 넘어온 값을 받아서 뒤쪽의 String 형 변수 addr 에 저장한다

+ 다운캐스팅 명시하지 않아도 자동으로 뒤에 쓰인 자료형으로 자동 변환된다

- 그러면 넘어온 name, addr 값을 addr() 메소드 안에서 바로 사용가능

- 넘어온 값을 받을때 request.getParameter("name") 으로 받았던 것을 @RequestParam 으로 받을 수 있다

 

- 즉, 아래 두 줄의 코드는 같은 코드이다

String name = request.getParameter("name")
@RequestParam("name") String name

 

@RequestParam 어노테이션 생략가능한 조건

- 위 코드는 아래처럼 @RequestParam 을 생략할 수도 있다

- 전달되는 name 값과 같은 이름의 변수로 값을 받을때는 @RequestParam 을 생략 가능하다

- 앞에서 넘어온 name 값인 "name", "addr" 과 같은 이름인 "name", "addr" 을 매개변수에 썼으므로, @RequestParam 을 생략가능하다

 

- 이후, 받아온 name, addr 값들을 다시 Model 객체에 저장해서 View 페이지인 WEB-INF/views/addr.jsp 로 간다\

- addr.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>${name }님 ${addr }에 사시는 군요
</body>
</html>

- Model 객체에 String 형으로 저장되어있으므로 단순히 ${name}, ${addr} 을 통해 View 에서 출력 가능

 


 

- 개별적으로 전달되는 값을 받을때는 @RequestParam 을 사용했다

- 이번에는 전달되는 값을 한번에 받아서 객체에 자동으로 저장하는 @ModelAttribute 를 사용해보자

 

- person.jsp 에서 action 값이 "addr2" 인 경우 form 태그를 설명

- person.jsp (수정 후)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
	<title>Insert title here</title>
</head>
<body>
	<h2>이름과 주소</h2>
	<!-- <form action="addr"> -->
	<form action="addr2">
		이름 : <input type="text" name="name">
		<p>
			주소 : <input type="text" name="addr">
		<p>
			<input type="submit" value="확인">
	</form>
</body>
</html>

- 입력하고 '확인' 을 누르면 이름과 주소가 Dispatcher Servlet -> Controller 클래스로 간다

 

- PersonController.java 부분

- 즉 앞에 @ModelAttribute 가 생략되어 있는 것이다

- 폼에서 넘어온 값들을 한번에 DTO Person 객체인 p 에 저장(세팅)한다

- 이때, DTO Person 클래스의 프로퍼티명과 넘어온 값의 name 이 일치하므로, 넘어온 값이 자동 매핑되어 Person 객체에 저장되는게 가능하다

 

- Person.java (DTO 클래스)

package com.ch.hello;

public class Person {
	private String name;
	private String addr;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAddr() {
		return addr;
	}

	public void setAddr(String addr) {
		this.addr = addr;
	}
}

- 폼에서 넘어온 name, addr 을 받아 저장하도록 프로퍼티가 같은 이름으로 있다

- 그러므로 자동으로 이 Person 객체에 넘어온 값들을 세팅해서 저장 가능

 

- 그리고 그렇게 세팅한 Person 객체 p 를 Model 객체 model 에 저장한 후 WEB-INF/views/addr2.jsp 로 이동

- addr2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>${person.name }님 ${person.addr }에 사시는 군요
</body>
</html>

- DTO 객체 p 가 "person" 이란 네임으로 저장되었으므로 View 페이지에서 출력할땐 ${person.필드명} 으로 출력하고 있다

 

+ 객체 p 에 값이 제대로 세팅되었는지 확인해보자

- 위 한줄을 추가하고 person.jsp 실행해서 값 입력 후 전송

- 콘솔창 보기

- 폼에서 넘어온 값들이 DTO Person 객체 p 에 잘 저장되었음을 확인 가능


정리

- @ModelAttribute : (주로 폼을 통해) 넘어온 값을 한번에 받아서 DTO 객체에 저장하고 싶을때 사용

- @RequestParam : 개별적으로 값을 받아 변수에 저장하고 싶을때 사용

- 수십개의 어노테이션이 있다, 최소 10개 정도는 알아야 사용 가능

 

Model 객체에 저장되는 값에 따라 값을 View 에서 출력하는 방법

1. 기본자료형 변수 : ${키} 로 출력

2. DTO 객체 : ${키.필드명} 으로 출력

3. 리스트 : forEach 태그의 items 속성에 ${키} 를 써서 사용



- Controller 클래스에서 메소드 앞에 자료형이 String 이 아닌 DTO, 리스트가 오는 경우를 보자

 

- sample.jsp (수정 전)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<script type="text/javascript">
		location.href="sample";
		// location.href = "list";
	</script>
</body>
</html>

- location.href 로 "sample" 을 주는 경우부터 먼저 하자 * "list" 로 주는 경우는 아래에

- sample.jsp 를 실행되자마자 "sample" 로 요청을 한다


- sample.jsp 실행시

- json 형태 데이터를 출력함

- json 은 이런 키:밸류 형태로 되어있다

- 1명에 대한 정보를 DTO 객체에 저장시켜서 JSON 형태로 돌려준 것이다

- pom.xml 에 추가된 jackson-databind 라이브러리가 DTO 객체를 JSON 데이터로 변환시킨것이다


- sample.jsp 에서 "sample" 로 온 요청을 받아주는 Controller 클래스의 @RequestMapping 어노테이션으로 가자

- SimpleController.java 부분

- 보통은 메소드의 리턴자료형이 String 이지만 이 경우엔 DTO 클래스인 SampleVo 클래스가 리턴자료형이다

- 비동기적으로 처리할때 많이 사용함

- DTO SampleVo 객체를 sv 를 생성 후 Setter 메소드로 값을 저장시킴

- DTO SampleVo 객체 sv 를 리턴하면 @ResponseBody 어노테이션때문에 값을 "요청한 곳(호출한 곳)"으로 돌려준다

- 즉, "sample" 로 요청했던 브라우저인 sample.jsp 로 돌려준다

- 돌려줄때 DTO 객체를 JSON 형태로 변환한 후 돌려준다

+ @RestController = @Controller + @ResponseBody

 

- sample.jsp 로 세팅한 DTO 객체를 돌려주는데 JSON 으로 변환해서 콜백함수 형태로 브라우저에 돌려주므로 브라우저에 아래처럼 출력됨 

- 이때, JSON 형태 데이터의 키는 그 DTO SampleVo 클래스의 필드명이고, 밸류는 DTO 객체에 세팅했던 값이 된다

 

@RestController 어노테이션

- Spring 4점대부터 지원하는 어노테이션

- 두가지 어노테이션을 결합한 어노테이션

- @RestController = @Controller + @ResponseBody

- 메소드 위에 @ResponseBody 어노테이션이 붙어있으면, 요청한 곳으로 결과를 콜백함수로 돌려주는 역할을 한다

- 따로 콜백함수를 설정하지 않았으므로 요청한 곳으로 값을 돌려줌, 콜백함수가 있을때는 거기로 돌려준다

 

- @RestController 어노테이션을 @Controller 와 @ResponseBody 로 나누는 방법

- 이러면 아래의 @RestController 만을 사용했던 코드와 같다

 

- SampleVo.java (DTO 클래스) 부분

- mno, firstName, lastName 3개의 프로퍼티가 만들어져있고, getter / setter 메소드가 있다


- sample.jsp 에서 "list" 로 요청하는 경우를 보자

- sample.jsp (수정 후)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<script type="text/javascript">
		// location.href="sample";
		location.href = "list";
	</script>
</body>
</html>

 

- SampleController.java 부분

+ Controller 클래스명위에 @Controller 가 있고 여기 함수명 위에는 @ResponseBody 를 붙였다

- 이번엔 List 로 리턴하는 경우이다

- 리스트를 생성한 후 반복문에서 DTO 객체를 생성, 세팅하고 리스트에 추가하는 것을 반복

- jackson 라이브러리가 DTO 객체를 JSON 형태로 변환한 후, 요청한 곳으로 해당 리스트를 돌려준다

- 즉, 요청한 브라우저인 list.jsp 로 돌려준다

 

- sample.jsp 실행시

- JSON 형태로 변환된 DTO 객체의 값들이 출력되어있다


Spring Web Application 예제 2

 

실습 준비

- 클라우드에서 ch07 프로젝트를 다운받아 import

 

Spring Web Application 예제 2\

ch07 프로젝트 구조

- 파일별 자세한 기능은 이전 프로젝트인 hello 프로젝트의 설명을 보기

- 지금은 hello 프로젝트에서 달라진 내용만 설명


추가된 라이브러리

- pom.xml 부분 1

- Spring 에선 cos 라이브러리 대신 주로 fileupload 라이브러리로 첨부파일 업로드

 

cos 라이브러리 vs fileupload 라이브러리

- fileupload 라이브러리는 업로드 시켜주는 코드가 따로 있다

- fileupload 라이브러리는 중복되는 이름의 파일명을 자동처리해주지 않아서 내가 처리해야함

 

- pom.xml 부분 2

- 이메일로 전송해주는 라이브러리

- 아이디, 비밀번호 찾기를 했을때 아이디, 비밀번호를 이메일로 전송해주는 기능을 위해서 이 라이브러리들을 추가해야함


URL 패턴 설정

- web.xml 부분

- <url-pattern> 을 *.do 로 설정했다

- Dispatcher Servlet 으로 찾아갈때 do 확장자를 가진 요청이름값일때만 Dispatcher Servlet 으로 보내겠다는 의미


Spring 환경설정 파일 위치 및 부르기

- 현재는 Web Application 이므로 webapp 폴더가 존재하고, 그 webapp 폴더 아래 어딘가에 servlet-content.xml, root-context.xml 이 있다

- 이 Spring 환경설정 파일들은 위치가 고정된 것이 아니며, 나중에 주로 resources 폴더에 저장시키기도 한다

- resources 폴더에 저장되어있을땐, web.xml 에서 환경설정 파일을 불러올때 classpath: 를 붙여야한다

 

ex) servlet-context.xml 이 resources 폴더에 저장되어있다면 web.xml 에서 불러올때 classpath:servlet-context.xml 으로 경로 설정

- web.xml 부분

	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

한글값 인코딩

- 위에서 web.xml 에 들어가는 내용 중 Spring 환경설정 파일 부르기, Dispatcher Servlet 위치 및 패턴 지정을 했다

- 마지막인 한글값 인코딩 내용 또한 들어가 있다

- post 로 넘어온 한글값에 대한 인코딩 완료


Spring 환경설정 파일

 

root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	</bean>		
		
</beans>

- 직접 bean 을 추가해서 객체를 생성해야한다

 

servlet-context.xml 부분 1

	<context:component-scan base-package="com.ch.ch07" />

- 어노테이션 기반으로 쓰기 위한 첫번째 조건이다

- com.ch.ch07 패키지 안의 모든 클래스를 읽어오겠다는 의미

 

- LoginCheck.java , UploadController.java 클래스를 읽어온다

- 해당 UploadController.java 클래스명 위에 @Controller 어노테이션이 붙어있다

- 어노테이션 기반으로 쓰기 위한 두번째 조건이다

 

servlet-context.xml 부분 2

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

- View 파일들은 모두 /WEB-INF/views 안에 들어가있어야한다

- 확장자는 jsp 이다

 

servlet-context.xml 부분 3

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />
	<resources mapping="/css/**" location="/WEB-INF/css/" />
	<resources mapping="/js/**" location="/WEB-INF/js/" />
	<resources mapping="/fonts/**" location="/WEB-INF/fonts/" />

- "View 페이지에 사용할" CSS, JS, 폰트 등의 매핑을 설정하는 역할

- CSS 파일들은 /WEB-INF/css/ 폴더 안에 저장되어있어야함

- JS 파일들은 /WEB-INF/js/ 폴더 안에 저장되어있어야함

- Fonts 들은 /WEB-INF/fonts/ 폴더 안에 저장되어있어야함

- 이렇게 매핑이 잡혀있어야만 불러올 수 있음

 

- 여기까지는 아는 내용, 다음은 새로하는 내용

 

servlet-context.xml 부분 4 : Intercepter 설정

	<!-- Interceptor설정 -->	
	<beans:bean id="lc" class="com.ch.ch07.LoginCheck"/>
	<interceptors>
		<interceptor>
			<mapping path="/upload.do"/>
			<beans:ref bean="lc"/>
		</interceptor>
	</interceptors>

* Intercepter 내용 아래에서 자세히 설명

- 회원관리에서 주로 사용


인터셉터 (Intercepter)

- 클라이언트에서 특정 URL 요청시 DispatcherServlet이 Controller를 호출하기 이전에 해당 요청을 가로채는 역할을 수행한다.

 

인터셉터 (Intercepter) 사용 예제

- 세션을 공유한 후, 정상적으로 접근시에는 세션이 공유되지만 비정상적 접근시에는 세션이 공유되지 않는다

- 세션 있는지 확인한 후 없다면 로그인 폼으로 보내는 역할을 Intercepter 가 하게 됨

 

- servlet-context.xml 에서 Intercepter 를 설정한 부분을 보자

- "upload.do" 로 요청이 들어오면 Intercepter 인 LoginCheck 가 가로채도록 설정했다

 

- LoginCheck.java

package com.ch.ch07;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginCheck implements HandlerInterceptor {
	
	// DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이후에 처리, Spring MVC의 Front Controller인 DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이전에 가로채는 역할 (세션이 없으면, 로그인 폼으로 이동 하도록 해준다 )
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
		HttpSession session = request.getSession();
		if (session.getAttribute("id") == null) {
			response.sendRedirect("loginForm.do");
			return false;
		}
		return true;
	}
}

- LoginCheck.java 는 Intercepter 관련 인터페이스를 구현한 클래스

- preHandle() 메소드안에서 만약 세션이 없다면 로그인 폼으로 보내는 역할을 하고 있다

 

- 이제 제대로 예제를 설명

 

인터셉터 (Intercepter) 사용 예제의 흐름

Intercepter 흐름

- 특정 URL 로 요청시 Dispatcher Servlet 이 Controller 를 호출하기 이전에 Intercepter 가 가로챈다

- 여기 예제에선 "upload.do" 로 요청시 Controller 로 가기 전에 가로채서 로그인이 되어있는지 되어있지 않는지 판별한다

- 로그인이 되어있지 않으면 비정상 접근이라고 생각해서 로그인 폼으로 보낸다

+ Intercepter 는 회원관리 프로그램에서만 사용함

 

인터셉터(Intercepter) 구현 방법 2가지

1. abstract class HandlerInterceptorAdapter 클래스를 상속 받아서 구현

2. interface HandlerInterceptor 인터페이스를 상속 받아서 구현

 

- 이후 그 클래스에서 아래의 3개의 메소드를 오버라이딩 해서 인터셉터 기능을 구현한다

+ 인터페이스를 상속받은 경우에는 반드시 3개를 모두 오버라이딩 해야함

- boolean preHandle()

- void postHandle()

- void afterCompletion()

 

- 인터셉터를 어떻게 구현했는지 LoginCheck.java 에서 보자

- LoginCheck.java

package com.ch.ch07;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginCheck implements HandlerInterceptor {
	
	// DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이후에 처리, Spring MVC의 Front Controller인 DispatcherServlet의 화면 처리가 완료된 상태에서 처리
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}

	// 지정된 컨트롤러의 동작 이전에 가로채는 역할 (세션이 없으면, 로그인 폼으로 이동 하도록 해준다 )
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
		HttpSession session = request.getSession();
		if (session.getAttribute("id") == null) {
			response.sendRedirect("loginForm.do");
			return false;
		}
		return true;
	}
}

- Intercepter 를 구현하는 2가지 방법 중 HandlerInterceptor 인터페이스를 상속받았다

- 인터페이스의 추상메소드 3개를 모두 오버라이딩 한다

- 이 메소드들은 호출되는 시점이 다르다 * 호출 시점 아래에서 설명

- 현재 예제는 이 중 preHandle() 메소드만 사용하고있고 나머지는 오버라이딩 만하고 비어있다

- preHandle() 에서 session 객체를 생성하고, session 값이 있는지 없는지 따진다

1) session 값이 없다면 sendRedirect() 로 로그인폼으로 가고 return false; 를 돌려준다

2) session 값이 있다면 return true; 를 하면 원래 가던 Controller 클래스로 돌아간다

 

Intercepter 관련 3개의 메소드가 호출되는 시점

- preHandle() 메소드 : Dispatcher Servlet 에서 Controller 로 가기전에 가로채서 자동으로 실행됨

- postHandle() 메소드 : Controller 클래스에서 Dispatcher Servlet 으로 가기 전에 가로채서 자동으로 실행됨

- afterCompletion() 메소드 : View 페이지에서 리턴할때 호출됨 (View 페이지 완성될때)

- 현재 예제는 이 중 preHandle() 메소드만 사용하고있고 나머지는 오버라이딩 만하고 비어있다

- preHandle() 에서 세션이 있는지 없는지 따져서, 없다면 로그인폼으로 보내는 역할을 한다

 

Intercepter 의 장점

- 로그인이 되어있는지(세션이 있는지) 각 페이지마다 신경쓸 필요가 없다

 

Inetercepter 환경 설정 방법

- servlet-context.xml부분

	<!-- Interceptor설정 -->	
	<beans:bean id="lc" class="com.ch.ch07.LoginCheck"/>
	<interceptors>
		<interceptor>
			<mapping path="/upload.do"/>
			<beans:ref bean="lc"/>
		</interceptor>
	</interceptors>

- bean 을 만들고, class 속성값으로는 패키지부터 Intercepter 클래스인 LoginCheck 클래스까지의 경로 설정

- "/upload.do" 로 요청이 들어올떄 매핑을 잡는다, 그럼 id가 "lc" 인 곳의 class 속성으로 설정된 클래스 LoginCheck 가 실행된다


로그인 시도시 흐름

- index.jsp 를 실행해보자

- index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<script type="text/javascript">
		location.href = "upload.do";
	</script>
</body>
</html>

1. "upload.do" 로 요청하므로 Intercepter 에 걸린다! (servlet-context.xml 에서 설정)

2. 그럼 Intercepter 클래스인 LoginCheck 클래스가 실행된다

3. 세션값이 없으므로 비정상적인 접근으로 인식해서 Intercepter 가 로그인 폼으로 보내준다

- session.getAttribute("id") 가 null 이므로 로그인 폼 loginform.do 로 요청

4. Controller 클래스에서 @RequestMapping 이 맞는 곳으로 간다, "loginForm" 으로 간다

5. loginForm.jsp 가 실행됨

- loginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div class="container">
		<form action="login.do">
			<h2 class="text-primary">로그인</h2>
			<c:if test="${not empty msg }">
				<span class="err">${msg }</span>
			</c:if>
			<table class="table table-bordered">
				<tr>
					<th>아이디</th>
					<td><input type="text" name="id" required="required"></td>
				</tr>
				<tr>
					<th>암호</th>
					<td><input type="password" name="pass" required="required"></td>
				</tr>
				<tr>
					<th colspan="2"><input type="submit" value="확인"></th>
				</tr>
			</table>
		</form>
	</div>
</body>
</html>

+ header.jsp 가 include 되어있다

- header.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<c:set var="path" value="${pageContext.request.contextPath }" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="${path}/css/bootstrap.min.css" rel="stylesheet">
<script src="${path}/js/jquery.js"></script>
<script src="${path}/js/bootstrap.min..js"></script>
<style>
.err {
	color: red;
	font-size: 20px;
}
</style>

- core 라이브러리 set 태그로 path 변수에 현재 프로젝트명을 저장했다

- ${pageContext.request.contextPath} 는 현재 프로젝트명을 가져옴, 그걸 변수 path 에 저장

- 링크로 부트스트랩, jQuery 파일들을 불러올때 앞에 ${pat} 를 붙여서 부트스트랩, jQuery 파일들의 절대경로를 구해오고 있음

- 아이디, 비번이 틀렸을때 .err 부분을 loginForm.jsp 에서 불러옴

 

6. loginForm.jsp 로그인폼에서 아이디, 비번 입력 후 로그인 시도

- "login.do" 요청되어 Controller 로 간다

UploadController.java 부분

+ 이때 loginForm.jsp 폼에서 넘어온 값이 바로 String id, String pass 에 저장된다

+ 즉 앞에 @RequestParam 이 생략되어있음

1) 아이디, 비번을 맞게 입력하여 로그인에 성공하면 id 를 세션설정하고 loginSuccess.jsp 로 간다

2) 로그인에 실패하면 loginForm.jsp 로 돌아오고 이떄 아래의 if 태그에서 msg 가 비어있지 않게되므로 if 조건을 만족하여 "똑바로 입력해" 메세지를 출력함

loginForm.jsp 부분

- Controller 로 갔다왔을때만 msg 가 비어있지 않게되어서 메세지를 출력함

 

7. 로그인 성공시 loginSuccess.jsp 로 이동

+ 실패시엔 위에작성, loginForm.jsp 로 간다

- loginSuccess.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="header.jsp"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2 class="text-primary">로그인 성공</h2>
	<a href="upload.do" class="button button-success">업로드</a>
</body>
</html>

- 여기도 '업로드' 클릭시 "upload.do" 로 요청하므로 Intercepter 에 걸린다

- 인터셉터 클래스 LoginCheck 이 실행됨, preHandle() 메소드가 실행됨

- 지금은 세션값이 있는 상태이므로 null 값이 아니다, return true; 해서 원래 가려고 했던 Controller 쪽으로 찾아간다

 

8. 현재는 loginSuccess.jsp 에서 Get 방식으로 요청했으므로 return "upload" 에 의해 upload.jsp 로 간다

- 위로 찾아가서 upload.jsp 로 이동

 


index.jsp 실행 캡처

- 아이디는 "java" 비번은 "1234" 로 입력해야 로그인 성공이다

- 틀린 비번 입력시

 

- 맞는 비번 입력시

Spring / Springboot

- Model 2 를 빠르게 개발하기 위해 사용하는 프레임워크

 

Spring

- Maven 으로 라이브러리를 관리 (선택불가)

- 그래서 구조가 Maven 프로젝트와 비슷

- EL 과 JSTL 로 결과를 출력

 

Springboot

- Maven 또는 Gradle 로 라이브러리 관리 가능

- EL, JSTL, 타임리프로 결과를 출력

 

+ 타임리프

- 타임리프 사용시 View 페이지가 html 문서가 되어야함, 기존 JSP 태그를 사용하지 못하게됨


Spring Project (Web Application) 구조

 

Spring Project import / export 할때

- Maven, Spring 프로젝트를 import / export 할때는 war 파일이 아닌 zip 파일을 압축해제해서 import, 복붙해서 output

- 서버에 최종적으로 배포할때는 war 파일로 

 

환경설정 파일 정리

1. Spring 환경설정 파일

- servlet-context.xml

- root-context.xml

 

2. 프로젝트 환경설정 파일

- web.xml

 

3. Maven 환경설정 파일

- pom.xml

 

web.xml 에 들어가는 주요 3가지 내용

1. DispatcherServlet 위치 설정 : <servlet-class> 태그 안에 있다

2. 환경설정 파일 2개 불러오기 : root-context.xml 과 servlet-context.xml 파일

3. 한글 인코딩 (아직 안들어가있다)

 

root-context.xml

- DB 연동 관련 내용

- 주로 bean 을 만들어서 DB 접속을 처리한다

- 여기서 Setter DI 도 사용함

- beans 루트 엘리먼트 안에 bean 을 추가해서 사용

- 어노테이션 방식 + 직접 bean 객체 생성 방식 둘다 사용함

 

servlet-context.xml

- View 파일들이 저장된 최상위 디렉토리 위치를 잡음, 그래야 View 파일로 찾아갈 수 있다

- ViewResolve (prefix, suffix)

- ViewResolve 에도 Setter DI 사용함

- 어노테이션 기반으로 처리됨

 

DI
- 환경설정 파일에서 주로 사용한다 (MyBatis 환경설정 파일, Spring 환경설정 파일)

- Setter DI 를 주로 사용함

 

DI 에서 주의

- 할당(주입) 할때 다른 bean 의 id 를 사용할때는 value 속성 대신 ref 속성 사용

- 메인 메소드에서 resources 폴더에 저장된 환경설정 파일을 불러올때는 classpath: 를 붙이기 

 

+ SqlSession 와 객체 생성

- SqlSession 은 부모 인터페이스

- SqlSessionTemplate 는 SqlSession 을 상속하는 구현 클래스

- SqlSession 은 객체 생성할 수 없으므로 SqlSessionTemplate 으로 bean 객체를 만들고 업캐스팅 해야함

 

+ Anotaion 을 이용한 DI

- 나중엔 어노테이션 기반으로 바뀐다

- bean 객체를 만들지 않고 처리 가능


Spring DI 예제 (이어서)

Spring DI 예제 10 (sample 9 와 비슷)

- src/main/java/sample10/ 안의 파일들

- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함

- 또한 Spring 의 환경설정 파일에서 필드값을 초기화함

- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명

- Vehicle.java : 부모 인터페이스, 추상메소드 rider()

- VehicleImpl.java : Vehicle 인터페이스를 상속하는 구현 클래스, rider() 메소드를 오버라이딩함, rider() 에서 매개변수로 전달된거 출력

- Outputer.java : 인터페이스, 추상메소드 output()

- FileOutputer.java : Output 인터페이스를 상속하는 구현 클래스, output() 메소드를 오버라이딩함

- 이 구현클래스 2개로 bean 객체 따로 만듬

 

- VehicleImpl.java

package sample10;

public class VehicleImpl implements Vehicle {
	private String name;
	private String rider;
	private Outputer out;

	public void setName(String name) {
		this.name = name;					// name="대박이"
	}

	public void setRider(String rider) {
		this.rider = rider;					// rider="비행기"
	}

	public void setOut(Outputer out) {
		this.out = out;						// out=out
	}

	public void rider() {
		String msg = name + "(이)가 " + rider + "을(를) 탄다";
		System.out.println(msg);
		out.output(msg);
	}
}

- 필드, setter 메소드, 메소드 로 구성되어있다.

- 프로퍼티에 값을 할당하는 방법 중 Setter 메소드를 사용할 것

- 인터페이스 Outputer 객체 output 의 setter 메소드 setOutput() , 매개변수는 인터페이스 Outputer 형

- setOut() 의 매개변수 자료형은 부모 인터페이스인 Output 이고, 여기 매개변수에 주입되는 값은 bean10.xml 에서 만들어진 자식인 FileOutputer 객체가 주입됨 (업캐스팅)

+ 인터페이스로는 객체 생성을 못하므로 자식 구현 클래스로 객체를 생성해서 업캐스팅 해야함

- 그럼 프로퍼티 out 은 FileOutpter 객체를 받은 것임

- Vehicle의 rider() 메소드를 여기서 오버라이딩

- rider() 안에서 초기화된 필드값을 출력하고, FileOutputer 객체 output 으로 FileOupter 클래스에서 오버라이딩된 메소드 output() 호출하자, 그럼 output() 에서는 파일을 생성해준다

* 잘 이해가 안되면 아래를 읽고 다시 여기 돌아와서 읽어보기

 

- Outputer.java

package sample10;

public interface Outputer {
	void output(String msg);
}

 

- FileOutputer.java

package sample10;

import java.io.FileWriter;
import java.io.IOException;


public class FileOutputter implements Outputer {
	private String fileName;

	public void setFileName(String fileName) {
		this.fileName = fileName;			// fileName="aa.txt"
	}

	public void output(String msg) {
		try {
			FileWriter fw = new FileWriter(fileName);
			fw.write(msg);
			fw.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}
}

- 부모 인터페이스 Output 을 상속

- output() 메소드를 메소드 오버라이딩 한다, 파일을 생성하고 매개변수로 받은 msg 를 파일에 쓰고 있음

- FileOutputer 로 bean 객체를 생성하고 그 필드 fileName 에 Setter 메소드인 setFileName() 으로 Setter DI 할 것

- 그리고 그 fileName 으로 파일명을 설정해서 FileWriter 객체 fw 를 생성

 

- Ex01.java

package sample10;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("/sample10/beans10.xml");
		Vehicle vh = (Vehicle) ac.getBean("vh");
		vh.rider();
	}
}

 

- beans10.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="vh" class="sample10.VehicleImpl">
		<property name="name" value="대박이"></property>
		<property name="rider" value="비행기"></property>
		<property name="out" ref="out"></property>
	</bean>
	<bean id="out" class="sample10.FileOutputter">
		<property name="fileName" value="aa.txt"></property>
	</bean>
</beans>

- 2개의 bean 객체를 생성하고 있다

 

첫번째 객체 생성 (beans01.xml 부분)

	<bean id="vh" class="sample10.VehicleImpl">
		<property name="name" value="대박이"></property>
		<property name="rider" value="비행기"></property>
		<property name="out" ref="out"></property>
	</bean>

- VehicleImpl 클래스로 bean 객체를 생성하고 프로퍼티 name, rider, out 에 Setter 메소드로 값 초기화 (= Setter DI)

- VehicleImple 객체 vh 의 프로퍼티 name 에 "대박이", rider 에 "비행기", out 에 아래에서 생성한 두번째 객체인 FileOutputter 객체 out 을 주입

- 프로퍼티 out 의 자료형은 Outputer 인터페이스, 주입되는 값은 FileOutputter 객체, 즉 업캐스팅

ex) Outputer out = new FileOutputter() 와 같다

 

- 위에서 이렇게 out 객체를 프로퍼티 out 에 주입해야만 VehicleImpl 객체 vh 에서 VehicleImpl 클래스의 오버라이딩된 메소드 rider() 를 호출하면서, 그 rider() 메소드 안에서 프로퍼티이자 FileOutputter 로 만든 객체 out 으로 오버라이딩 된 메소드 output() 호출 가능 

Ex01.java 에서 VehicleImpl 로 만든 객체 vh 로 메소드 오버라이딩된 rider() 호출
VehicleImpl.java 에서 rider() 를 메소드 오버라이딩 하는 코드


두번째 객체 생성 (beans01.xml 부분)

	<bean id="out" class="sample10.FileOutputter">
		<property name="fileName" value="aa.txt"></property>
	</bean>

- 힙메모리에 새 공간을 생성해서 FileOutputter 객체 out 을 생성한다

- FileOutputter 클래스로 객체 out 을 생성하고 프로퍼티 fileName 에 "aa.txt" 를 Setter DI 로 주입

- 객체 생성 및 주입되는 시점 : beans10.xml 을 메인 메소드에서 읽어올때 메모리에서 자동으로 모든 객체 생성, 모든 주입이 일어남

 

- Ex01.java 실행시

- 파일 aa.txt 가 생성되었고 원하는 메세지가 작성되었음


Spring DI 예제 11 (DAO, DTO, Service 등도 있다)

- src/main/java/sample11/ 안의 파일들

- 지금까지의 예제와 다른 형태, DAO, DTO, Service 등도 있음

 

Spring DI 예제 11 구조 & 흐름

- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller

- 현재는 Web Application 이 아닌  Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임

- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자

 

- service 와 dao 폴더에는 인터페이스 하나, 구현 클래스 하나 씩 있다

- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다

- dao/ProductDao.java : 인터페이스, 추상메소드 getProduct()

- dao/ProductDaoImpl.java : ProductDao 를 상속하는 구현클래스, 추상메소드 getProduct() 를 오버라이딩

- service/ProductService.java : 인터페이스, 추상메소드 getProduct()

- service/ProductServiceImpl.java : ProductService 를 상속하는 구현클래스, 추상메소드 getProduct() 를 오버라이딩

- model/Product.java : DTO 클래스, 상품이름 name 필드, 상품가격 price 필드, setter / getter 메소드가 있다, 값을 저장하고 돌려줄때 사용

 

main() -> Service -> DAO 로 가는 방법

- 먼저 Spring 환경설정 파일 beans11.xml 에서 구현 클래스 두개 ProductServiceImpl, ProductDaoImpl 로 bean 를 만들 것

- main() 메소드에서 getBean 으로 Service 클래스 객체를 가져오고, 그걸로 Service 객체의 메소드 사용 가능

- Service 클래스에서는 DAO 객체를 가져와서, 그걸로 DAO 객체의 메소드 사용 가능

 

- main() (Controller)에서는 Service 클래스 객체를 구해와야만 Service 의 메소드를 호출 가능

- Service 클래스 객체는 DAO 객체를 구해와야만 DAO 의 메소드를 호출 가능

- 구해온다 = 프로퍼티 자료형이 DAO

 

- beans11.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="pd" class="sample11.dao.ProductDaoImpl"></bean>
	<bean id="ps" class="sample11.service.ProductServiceImpl">
		<property name="pd" ref="pd"></property>
	</bean>
</beans>

<첫번째 객체>

- ProductDaoImpl 클래스(DAO 클래스)로 객체 pd 생성

- 이 객체는 아래에서 참조할 것

<두번째 객체>

- 이 객체는 main() 에서 getbean("ps") 로 가져올 것

- ProductServiceImpl 클래스(Service 클래스)로 객체 ps 생성

- ProductServiceImpl 클래스의 프로퍼티에 주입을 해야하는데, 그 프로퍼티는 ProductDao 자료형이므로 ProductDaoImpl 클래스로 생성된 객체가 주입되어야함, 그래서 위에서 만든 ProductDaoImpl 객체 pd 를 ref 로 주입함

 

- ProductServiceImpl.java

package sample11.service;

import sample11.dao.ProductDao;
import sample11.model.Product;

public class ProductServiceImpl implements ProductService {
	private ProductDao pd;

	public void setPd(ProductDao pd) {
		this.pd = pd;
	}

	public Product getProduct() {
		return pd.getProduct("라면");
	}
}

- 프로퍼티 ProductDao pd 가 있다

- 이 Service 클래스로 객체를 생성과 주입을 할때 프로퍼티 pd 에는 객체가 들어가야하므로 ref 로 DAO 구현클래스 객체를 넘겨줘야한다 

- 이 프로퍼티 자료형을 인터페이스 ProductDao 로 해도 되고, 구현클래스 ProductDaoImpl 로 해도 된다

- 여기선 인터페이스 ProductDao 가 프로퍼티의 자료형이므로 ProductDaoImpl 으로 객체를 만들어서 업캐스팅 해야함

- beans11.xml 에서 이 프로퍼티이자 DAO 객체 pd 에 주입(SetPd() Setter DI 로) 을 할 것 * 위로 가서 beans11.xml 보자

 

- ProductDaoImpl.java

package sample11.dao;

import sample11.model.Product;

public class ProductDaoImpl implements ProductDao {
	public Product getProduct(String name) {
		return new Product(name, 2000);
	}
}

 

- Ex01.java

package sample11;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import sample11.model.Product;
import sample11.service.ProductService;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new GenericXmlApplicationContext("/sample11/beans11.xml");
		ProductService ps = (ProductService) ac.getBean("ps");
		Product product = ps.getProduct();
		System.out.println(product); // println(product.toString()) 와 같다
	}
}

- Web Application 에서는 Controller 클래스가 이 main() 메소드 역할을 한다

- 같은 패키지 안에 beans11.xml 이 있으므로 패키지명부터 해당 파일명까지 써준다, 읽어올때 객체생성되고 DI 까지 완료됨

- main() -> Service 로 넘어간다는 의미 = main() 에서 Service 클래스의 메소드를 호출한다

- main() -> Service 로 넘어가야하므로 beans11.xml 에서 만들어진 Service 객체인 "ps" 를 구해온다

- Service 객체를 구해왔으므로 Service 클래스에서 오버라이딩된 메소드인 getProduct() 호출 가능


전체 흐름 (중요)

main() -> Service

- main() -> Service 로 넘어간다는 의미 = main() 에서 Service 클래스의 메소드를 호출한다

- main() (Controller)에서는 Service 클래스 객체를 구해와야만 Service 의 메소드 getProduct()를 호출 가능

+ 이때 main() 에서는 Service 객체를 getbean("ps") 로 객체 ps를 구해온다

- Service 객체를 구해왔으므로 Service 클래스에서 오버라이딩된 메소드인 getProduct() 호출 가능

Service -> DAO

- Service  -> DAO로 넘어간다는 의미 = DAO 에서 DAO클래스의 메소드를 호출한다

- Service 클래스의 오버라이딩한 getProduct() 안에서는 DAO 의 getProduct() 를 호출하고 있다, 호출하면서 매개변수 "라면" 전달

- 이때 Service 클래스 객체는 DAO 객체를 구해와야만 DAO 의 메소드 getProduct() 를 호출 가능

- 구해온다 = 프로퍼티 자료형이 DAO

- 이미 main() 에서 beans11.xml 을 읽어올때 Service 객체가 생성되며 프로퍼티인 DAO 객체도 구해졌으므로 바로 DAO의 getProduct() 호출 가능

DAO

- DAO 클래스의 getProduct() 에 매개변수로 "라면" 이 전달되어 왔다

- 현재는 DB연동 안되었으므로 그냥 돌아간다, 돌아갈때 DTO 객체를 생성하고, 매개변수로 받은 "라면" 과 2000 을 저장한 DTO 객체를 Service 로 돌려줌

+ DTO 객체

- 프로퍼티 name , 프로퍼티 price

- 생성자는 public Product(String name, int price)

- 프로퍼티 name, price를 결합해서 리턴해주는 메소드 toString() 이 있다

DAO -> Service (돌아옴)

- DAO 로 부터 돌려받은 것(DTO 객체)을 그대로 main() 으로 리턴함

Service -> main() (돌아옴)

- Service 로 부터 돌려받은 결과(DTO 객체)를 출력함


Spring DI 예제 12 (DAO, DTO, Service 등도 있다, sample 11와 비슷)

- src/main/java/sample12/ 안의 파일들

-  DAO, DTO, Service 가 있다, sample11 과 비슷

 

Spring DI 예제 12 구조 & 흐름

- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller

- 현재는 Web Application 이 아닌  Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임

- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자

 

- service 와 dao 폴더에는 인터페이스 하나, 구현 클래스 하나 씩 있다

- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다

- dao/BookDao.java : 인터페이스, 추상메소드 getBook()

- dao/BookDaoImpl.java : BookDao를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩

- service/BookService.java : 인터페이스, 추상메소드 getBook()

- service/BookServiceImpl.java : BookService를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩

- model/Book.java : DTO 클래스, 책제목 title 필드, 가격 price 필드, setter / getter 메소드가 있다, 값을 저장하고 돌려줄때 사용

 

main() -> Service -> DAO 로 가는 방법

- 먼저 Spring 환경설정 파일 beans11.xml 에서 구현 클래스 두개 ProductServiceImpl, ProductDaoImpl 로 bean 를 만들 것

- main() 메소드에서 getBean 으로 Service 클래스 객체를 가져오고, 그걸로 Service 객체의 메소드 사용 가능

- Service 클래스에서는 DAO 객체를 가져와서, 그걸로 DAO 객체의 메소드 사용 가능

 

main() vs Service 에서 객체를 구하는 방법

- main() 에서 Service 객체를 구할때는 beans12.xml 을 읽어서 getbean() 으로 가져옴

- Service 클래스에서 DAO 객체를 구할때는 Service 클래스의 프로퍼티의 자료형을 DAO 로해서 그 프로퍼티로 DAO 객체를 구함

+ main() 에서 beans12.xml 을 읽을때 이미 Service 객체, DAO 객체 다 생성되고 할당되었다 -> 즉 Service 클래스에서 DAO 객체를 프로퍼티로 이미 자동으로 주입받았다

 

+ toString()

- toString() 은 Object 클래스의 메소드이고, toString() 을 메소드 오버라이딩해서 사용

+ 모든 클래스는 Object 클래스를 상속받는다

- DTO 리턴시 자동으로 toString() 이 실행되어 리턴된 값이 실행됨

 

- Ex01.java

package sample12;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sample12.model.Book;
import sample12.service.BookService;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("/sample12/beans12.xml");
		BookService bs = (BookService) ac.getBean("bs");
		Book book = bs.getBook();
		System.out.println(book);
	}
}

+ 현재는 Application 이므로 main() 에서 beans12.xml 를 불러와야만 beans12.xml 가 실행된다

+ Web Application 으로 넘어가면 web.xml 이 실행될때 환경설정 파일을 불러오면서 beans12.xml 가 실행된다

- main() -> Service 로 가기 위해 beans12.xml 에서 Service 객체 bs 를 구해와서 Service 의 getBook() 호출 

 

- beans12.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="bd" class="sample12.dao.BookDaoImpl"></bean>
	<bean id="bs" class="sample12.service.BookSericeImpl">
		<property name="bd" ref="bd"></property>
	</bean>
	
</beans>

<첫번째 객체>

- DAO 클래스로 객체 "bd" 를 생성

<두번째 객체>

- Service 클래스로 객체 "bs" 를 생성

- 객체 bs 를 생성하고, 자료형이 BookDao 인 프로퍼티 bd 에 값을 Setter DI 로 할당하는데, 이때 위에서 생성된 BookDaoImpl 객체 bd 를 할당함

- 프로퍼티 자료형은 인터페이스 BookDao , 주입되는 값은 구현클래스 BookDaoImpl, 즉 업캐스팅으로 주입

 

- BookServiceImpl.java

package sample12.service;

import sample12.dao.BookDao;
import sample12.model.Book;

public class BookSericeImpl implements BookService {
	private BookDao bd;

	// BookDao bd = new BookDaoImpl()
	public void setBd(BookDao bd) {
		this.bd = bd;
	}

	public Book getBook() {
		return bd.getBook("대박인생");
	}
}

- main() 에서 beans12.xml 을 불러올때 이미 메모리에 BookServiceImpl 객체 생성되고, 프로퍼티 bd 에도 값이 주입되었음, DAO 의 클래스 사용 가능해졌다

- main() 에서 Service 의 getBook() 을 호출했다, 여기서는 DAO 로 가기위해 DAO 객체 bd 로 bd.getBook("대박인생") 호출

 

- BookDaoImpl.java

package sample12.dao;

import sample12.model.Book;

public class BookDaoImpl implements BookDao {
	public Book getBook(String title) { //  title="대박인생"
		return new Book(title, 20000);
	}
}

- Service 클래스에서 이 DAO 의 getBook() 을 호출했다, "대박인생" 이 매개변수로 넘어옴

- 여기서 DTO 객체를 생성하고, DTO 객체 프로퍼티 title 에 "대박인생" , price에 20000 을 저장해서 돌려줌

 

Book.java (DTO)

package sample12.model;

public class Book {
	private String title;
	private int price;

	public Book(String title, int price) {
		this.title = title;			// title="대박인생"
		this.price = price;         // price=20000
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public String toString() {
		return "책[제목:" + title + ", 가격:" + price + "]";
	}
}

+ 값을 리턴할때, 전달할때 DTO 객체 생성

- Object toString() 을 오버라이딩해서 저장된 값들을 한꺼번에 돌려주려한다

- toString() 은 DTO 객체가 리턴될때 자동으로 실행됨, 실행되면 값들을 리턴해줌

 

- 이제 DAO -> Service 로 돌아가야한다

- BookServiceImpl.java (중복)

package sample12.service;

import sample12.dao.BookDao;
import sample12.model.Book;

public class BookSericeImpl implements BookService {
	private BookDao bd;

	// BookDao bd = new BookDaoImpl()
	public void setBd(BookDao bd) {
		this.bd = bd;
	}

	public Book getBook() {
		return bd.getBook("대박인생");
	}
}

- 돌려받은 값을 그대로 main() 으로 리턴함

 

- Ex01.java (중복)

package sample12;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sample12.model.Book;
import sample12.service.BookService;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("/sample12/beans12.xml");
		BookService bs = (BookService) ac.getBean("bs");
		Book book = bs.getBook();
		System.out.println(book);
	}
}

 

- Service 의 getBook() 으로부터 결과를 바당옴

<toString()>
- toString() 은 DTO 객체가 리턴될때 자동으로 실행됨, 그래서 객체 book 을 출력해도 book.toString() 과 같은 결과가 나옴

+ 만약 DTO 객체에 toString() 을 오버라이딩하지 않았으면, System.out.println(book) 할때 주소값이 나옴

- DTO 의 getter 메소드로 개별적인 값을 불러올 수도 있다

 


어노테이션을 이용한 DI

- 어노테이션 기반으로 처리시 Spring 환경설정 파일에 bean 객체 생성하지 않음

 

- Sample13 예제를 본격적으로 하기전에 Simple13 을 간략하게 보면서 어노테이션을 이용한 DI 가 뭔지 보자

- beans13.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 
	1. sample13 패키지 하위 클래스를 스캔한다는 의미를 가진다.
    2. @Component, @Controller, @Service, @Repository 어노테이션이
             붙어있는 클래스는 @Autowired 어노테이션을 이용해서 필요한 빈 객체를  
       setter 메소드 없이 주입을 받는다.
 -->	
	
	<context:component-scan base-package="sample13"/>

</beans>

- bean 을 더이상 만들고 있지 않고 단 한줄만 있다, 이게 어노테이션 기반

- base-package 는 베이스가 되는, 기본이 되는 패키지를 의미함

- 저 한줄은 sample13 패키지 하위의 모든 클래스를 모두 읽어오라는 의미

- 지정된 패키지 sample13 하위의 클래스들에는 클래스명 위에 4가지 어노테이션 중 하나가 붙어있어야함

- @Component, @Controller, @Service, @Repository

 

어노테이션 4가지

- @Component : 아무 클래스에나 붙일 수 있는 어노테이션

- @Controller : 컨트롤러 클래스 위에 붙이는 어노테이션

- @Service : Service 클래스 위에 붙이는 어노테이션

- @Repository : Repository 클래스 위에 붙이는 어노테이션

- 클래스명 위에 이 4개의 어노테이션 중 한가지만 붙어있어도 된다

- sample13 에서는 모두 @Component 를 붙이지만, 나중엔 클래스에 맞는 어노테이션을 붙인다

ex) Service 클래스 위에 @Service 어노테이션

 

- 이후 Setter 메소드가 없어도 생성이 필요한 객체 위해 @Autowired 어노테이션을 붙이면 main() 에서 beans13.xml을 읽어올때 필요한 bean 객체가 생성 및 주입됨

 

필요한 bean 객체를 주입하기 위한 조건

1. base-package 에 적힌 해당 패키지의 모든 클래스를 읽어오는 코드가 있어야함

2. 어노테이션 4개 중 하나 붙어있어야함

3. 만들고자 하는 객체 위에 @Autowired 어노테이션을 붙여야함

 

ex) Service -> DAO 로 갈때

- Service 에서 DAO 객체를 구해와야하는데, 이때 Setter 메소드 필요없고, bean 객체를 직접 생성하지 않아도 된다

- 자동으로 DAO bean 객체를 생성해줌

- 조건에 맞는지 보자

1) base-package 에 sample13 패키지가 적혀있다

2) Service 클래스 위에 @Component 어노테이션이 붙어있다

3) 필요한 객체인 DAO 객체(Service의 프로퍼티) 위에 @Autowired 어노테이션이 붙어있다

- 조건이 맞으므로 Service 클래스에 Setter 메소드가 없어도 Service 클래스에 DAO 객체가 생성되어서 프로퍼티에 값(객체)이 주입됨, 이때 객체 생성 및 주입 시점은 main() 에서 beans13.xml 을 읽어왔을때이다.

- 즉 아래의 빨간선으로 표시한 내용이 자동으로 실행된다, 실행되는 시점은 main() 에서 beans13.xml 을 읽어왔을때

- sample11.xml 부분

 

+ 현재는 DAO 에서 DB 연동안하므로 DAO 에서 객체 생성하진 않음, 나중엔 SqlSession 객체를 생성

 


Spring DI 예제 13 (어노테이션 기반)

- src/main/java/sample13/ 안의 파일들

- 더이상 bean 을 만들지 않고, 어노테이션 기반으로 처리함

 

Spring DI 예제 13 구조 & 흐름

- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller

- 현재는 Web Application 이 아닌  Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임

- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자

 

- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다

- productDao.java : 인터페이스, 추상메소드 getProduct()

- ProductDaoImpl.java : productDao를 상속하는 구현클래스, 추상메소드 getProduct() 를 오버라이딩

- ProductService.java : 인터페이스, 추상메소드 getProduct()

- ProductServiceImpl.java : ProductService를 상속하는 구현클래스, 추상메소드 getProduct() 를 오버라이딩

- Prodjct.java : DTO 클래스, 값을 저장하고 돌려줄때 사용

 

어노테이션

- Service 구현 클래스와 DAO 구현 클래스에만 어노테이션이 붙어있다

- Service 구현 클래스, DAO 구현 클래스위에 @Component 어노테이션이 붙어있음

 

문제

- 흐름은 main() -> Service -> DAO -> Service -> main()

- Service -> DAO 로 갈때는 @Autowired 로 DAO 객체를 구해온다

- 그럼 main() -> Service 로 갈때는 bean 객체가 없는데 어떻게 getbean() 으로 Service 객체를 구해올까?

 

- Ex01.java

package sample13;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("/sample13/beans13.xml");
		ProductService ps = ac.getBean(ProductService.class);
		Product product = ps.getProduct();
		System.out.println(product);
	}
}

- 먼저 beans13.xml 을 불러온다

- getBean() 안에 bena 이 없고, Service 클래스명을 직접 구해와야한다 (인터페이스명도 되고 구현클래스명도 됨)

- 이러면 Service 객체를 구해와서 ps 로 받고, Service 클래스 안의 메소드 getProduct() 를 호출

 

- ProductServiceImpl.java

package sample13;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class ProductServiceImpl implements ProductService {
	@Autowired
	private ProductDao pd;

	public Product getProduct() {
		return pd.getProduct("짜장면");
	}
}

- beans13.xml 을 main() 에서 불러올때 이미 DAO 객체 pd 에 주입이 되었다

- 여기 SetterPd() 메소드가 없어도 주입됨!

- 객체 pd 가 주입되어있으므로 pd.getProduct() 로 DAO 클래스의 getProduct("짜장면") 호출

 

- ProductDaoImpl.java

package sample13;

import org.springframework.stereotype.Component;

@Component
public class ProductDaoImpl implements ProductDao {
	public Product getProduct(String name) {//name="짜장면"
		return new Product(name, 2500);
	}
}

- DTO 객체를 생성하고 매개변수로 넘어온 "짜장면", 2500 을 객체에 저장해서 그 객체를 리턴

 

- 다시 Service 로 돌아가야한다

- ProductServiceImpl.java (중복)

package sample13;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class ProductServiceImpl implements ProductService {
	@Autowired
	private ProductDao pd;

	public Product getProduct() {
		return pd.getProduct("짜장면");
	}
}

- 받은 DTO 객체를 그대로 main() 으로 돌려줌

 

- Ex01.java (중복)

package sample13;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("/sample13/beans13.xml");
		ProductService ps = ac.getBean(ProductService.class);
		Product product = ps.getProduct();
		System.out.println(product);
	}
}

- DTO 객체를 돌려받음

- toString() 은 써도 안써도 자동 실행됨


 

나중에 할 예제의 흐름

- Controller 클래스에서 Service 객체를 생성할때 @Autowired 사용해서 생성

- Controller -> Service로 넘어가는 방법 = Service 객체 생성


Web Application 에서의 어노테이션

- 잠시 Web Application 인 springtest 프로젝트를 보자

 

어노테이션으로 Controller -> Service 로 가는 방법

- 조건에 맞는지 보자

1) servlet-context.xml 에서 base-package 를 설정하고 있다

- com.myhome.springtest 란 패키지 안의 모든 클래스를 읽어옴, 그 안에는 Controller 클래스인 HomeController.java

- Spring Project 생성시 입력헀던 top-level 패키지이다, 이 패키지 com.myhome.springtest 는 src/main/java 폴더 하위에 생성된다

2) HomeController 클래스 위에 @Controller 어노테이션이 있다

3) Controller 클래스 HomeController 클래스 안에서 Service 객체 생성이 필요한 곳에서 @Autowired 를 붙임

 

- 1,2,3 조건 3개를 모두 만족하므로 Controller 에서 Service 객체를 생성할때 자동으로 생성해서 Controller 클래스의 프로퍼티에 주입해준다

 

Spring 환경설정 파일과 어노테이션

- Web Application 에서 Spring 의 환경설정 파일 중 root-context.xml 은 어노테이션 뿐 아니라 기존 처럼 bean 태그로 객체 생성하는 방법도 사용한다

- Web Application 에서 Spring 의 환경설정 파일 중 servlet-context.xml 은 어노테이션 기반이다, base-package 가 지정되어있음, 


 

Spring DI 예제 14 (어노테이션 기반, Sample13 과 비슷)

- src/main/java/sample14/ 안의 파일들

- 더이상 bean 을 만들지 않고, 어노테이션 기반으로 처리함

 

Spring DI 예제 14 구조 & 흐름

- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller

- 현재는 Web Application 이 아닌  Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임

- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자

 

- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다

- BookDao.java : 인터페이스, 추상메소드 getBook()

- BookDaoImpl.java : BookDao를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩

- BookService.java : 인터페이스, 추상메소드 getBook()

- BookServiceImpl.java : BookService를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩

- Book.java : DTO 클래스, 값을 저장하고 돌려줄때 사용

 

예제 sample14 / sample15 의 흐름

- 현재는 Controller 클래스가 없으므로 main() 에서 그 역할을 한다

- main() 에서 Service 객체를 생성하는 것이 아니라 그냥 불러왔음

 

어노테이션

- Service 구현 클래스와 DAO 구현 클래스에만 어노테이션이 붙어있다

- Service 구현 클래스, DAO 구현 클래스위에 @Component 어노테이션이 붙어있음

 

- beans14.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
	<context:component-scan base-package="sample14"/>

</beans>

- 저 한줄이 첫번째 조건이다

 

- Ex01.java

package sample14;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new GenericXmlApplicationContext("/sample14/beans14.xml");
		BookService bs = ac.getBean(BookService.class);
		Book book = bs.getBook();
		System.out.println(book);
	}
}

- 지금은 Controller 클래스가 아니라 main 메소드이므로 어노테이션을 안쓰고 있음

- bean 을 만들지 않았으므로 Service 객체가 없다

- 그래서 getBean(BookService.class) 로 Service 클래스 객체를 구해온다

 

- BookSeviceImpl.java

package sample14;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class BookServiceImpl implements BookService {
	@Autowired
	private BookDao bd;

	public Book getBook() {
		return bd.getBook("바람과 함께 사라지다");
	}
}

- Service 클래스 위에 @Component 어노테이션

- DAO 객체를 @Autowired 로 프로퍼티 bd 에 주입

- DAO 객체를 구해왔으므로 DAO 의 getBook() 메소드 호출, 매개변수로 "바람과 함께 사라지다" 를 준다

 

- BookDaoImpl.java

package sample14;

import org.springframework.stereotype.Component;

@Component
public class BookDaoImpl implements BookDao {
	public Book getBook(String title) {
		return new Book(title, 25000);
	}
}

- 매개변수로 전달된 타이틀값과 가격으로 DTO Book 객체를 생성해서 객체를(주솟값을) 리턴

- DAO 클래스 위에 @Componet 어노테이션

 

- 다시 돌아가는 과정은 리턴만 하면 되므로 생략

 


Spring DI 예제 15 (어노테이션 기반, Sample14 과 비슷)

- src/main/java/sample15/ 안의 파일들

- 더이상 bean 을 만들지 않고, 어노테이션 기반으로 처리함

- 이제 어노테이션 @Componet 대신 Service 클래스 위에 @Service, DAO 클래스 위에 @Repository

 

Spring DI 예제 15 구조 & 흐름

- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller

- 현재는 Web Application 이 아닌  Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임

- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자

 

- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다

- BookDao.java : 인터페이스, 추상메소드 getBook()

- BookDaoImpl.java : BookDao를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩

- BookService.java : 인터페이스, 추상메소드 getBook()

- BookServiceImpl.java : BookService를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩

- Book.java : DTO 클래스, 값을 저장하고 돌려줄때 사용

 

어노테이션

- 이제 어노테이션 @Componet 대신 Service 클래스 위에 @Service, DAO 클래스 위에 @Repository

- 인터페이스 위에는 어노테이션 쓰지 않음, 구현 클래스 위에만 어노테이션 쓴다

 

클래스와 해당 어노테이션 관계

- Controller 클래스 위엔 @Controller

- DAO 클래스 위엔 @Repository

- Service 클래스 위엔 @Service

- @Component 로 해도 동작은 한다

 

예제 sample14 / sample15 의 흐름

- 현재는 Controller 클래스가 없으므로 main() 에서 그 역할을 한다


+ 나중에 Web Application 할때 할 예제의 흐름 (중복, 중요)

Controller -> Service 로 가는 법

- Controller 클래스에서 Service 객체를 생성할때 @Autowired 사용해서 생성

- Controller -> Service로 넘어가는 방법 = Service 객체 생성, 이때 주입할 프로퍼티 위에 @Autowired 를 써준다

 

1) servlet-context.xml 에서 top-level 패키지안의 모든 클래스 읽기, 즉 Conroller 클래스를 읽어옴

2) Controller 클래스에서 클래스명 위에 @Controller 붙이기

3) Service 객체를 생성해서 주입할 프로퍼티 위에 @Autowired 를 써준다

 

- 이 조건을 모두 만족시켜서 Controller 클래스에서 Service 클래스의 객체를 만들 수 있다

- Controlelr 클래스에 Setter 메소드가 없어도 만들고 주입 가능함

 

+ 지금 예제는 일반 Application 이므로 servlet-context.xml 에서 Controller 패키지를 읽어오는 작업을 하지도 않았고 Controller 패키지가 없으므로 main() 에서 Service 객체를 .class 로 불러와서 main() -> Service 로 이동


- Ex01.java

package sample15;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new GenericXmlApplicationContext("/sample15/beans15.xml");
		BookService bs = ac.getBean(BookService.class);
		Book book = bs.getBook();
		System.out.println(book);
	}
}

- beans15.xml 파일을 읽어온다

- beans15.xml 에 bean 태그로 객체를 생성하지 않았으므로 BookService.class (경로설정) 로 Service 클래스의 객체를 생성함

- 생성한 Service 객체로 Service 의 getBook() 메소드 호출

 

- beans15.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 
	1. sample15 패키지 하위 클래스를 스캔한다는 의미를 가진다.
    2. @Component, @Controller, @Service, @Repository 어노테이션이
             붙어있는 클래스는 @Autowired 어노테이션을 이용해서 필요한 빈 객체를  
       setter 메소드 없이 주입을 받는다.
 -->	
	
	<context:component-scan base-package="sample15"/>

</beans>

 

- BookServiceImpl.java

package sample15;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/*@Component*/
@Service
public class BookServiceImpl implements BookService {
	@Autowired
	private BookDao bd;

	public Book getBook() {
		return bd.getBook("바람과 함께 사라지다");
	}
}

- Service 클래스 위에는 @Service 를 주로 붙인다

- @Autowired 를 프로퍼티 bd 위에 쓰면 생성된 DAO 객체를 bd 에 주입해줌

- 이때 이 Service 클래스안에 setBd() 인 Setter 메소드가 없어도 자동으로 주입해준다

- 이때 생성 및 주입은 main() 에서 beans15.xml 을 읽어왔을때 이미 했음

+ 조건 1,2,3  을 만족하므로 자동으로 생성, 주입이 가능한 것임 (base-package="sample15", @Service, @Autowired)

 

- BookDaoImpl.java

package sample15;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

/*@Component*/
@Repository
public class BookDaoImpl implements BookDao {
	public Book getBook(String title) {//title="바람과 함께 사라지다"
		return new Book(title, 25000);
	}
}

- DAO 클래스 위에는 @Repository 를 주로 붙인다

- 현재는 DAO 에서는 아무 객체도 생성하고 있지 않지만 나중에 SQL문 실행을 위해서 SqlSession 객체를 @Autowired 로 생성 및 주입해야함

 - DTO 인 Book 객체를 만들어서 받은 "바람과 함께 사라지다" 와 25000 을 저장해서 객체를 돌려줌

 

+ Book.java (DTO) : Object 의 toString() 메소드를 오버라이딩 했다, DTO 리턴시 자동으로 실행됨

- 이제 다시 돌아가야한다

- BookServiceImpl.java : 받은 객체를 그대로 리턴한다

- Ex01.java : Service 클래스로부터 DTO 객체를 받아서 책제목과 가격을 리턴하는 toString() 을 실행


Sample 16 & Sample 17

- 콘솔로 만든 회원관리 프로그램

- 같은 내용이다

- Sample 16 은 bean 객체를 생성해서 만들고

- Sample 17 은 어노테이션 기반으로 만들었다


Spring DI 예제 16 (bean 객체 직접 생성, 회원관리 콘솔 프로그램)

- src/main/java/sample16/ 안의 파일들

- bean 을 직접 생성하여 만든 콘솔 회원관리 프로그램

 

Spring DI 예제 16 구조 & 흐름

- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller

- 현재는 Web Application 이 아닌  Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임

- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자

 

- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다, 명령어에 따라 회원정보 검색, 등록, 삭제, 수정 가능

- MemberService.java : 인터페이스, 추상메소드들이 많이 있다 ex) delete(), insert(), update(), select(), list()

- MemberServiceImpl.java : MemberService 를 상속, MemberService 의 추상메소드들을 모두 오버라이딩

- MemberDao.java : 인터페이스, 추상메소드들이 많이 있다 ex) delete(), insert(), update(), selectByEmail(), list()

- MemberDaoImpl.java : MemberDao를 상속, MemberDao 의 추상메소드들을 모두 오버라이딩

- Member.java : DTO, 필드 id,pass,email,name,reg_date 와 각각의 getter/setter 메소드, toString() 메소드

- RegisterMember.java : DTO, 필드 pass, confirmpass, name, email 와 각각의 getter/setter 메소드, passCheck() 메소드

- DTO 객체가 2개이다

- RegisterMember.java DTO 클래스는 가입할때만 사용된다

 

Service 클래스와 DAO 클래스의 메소드 명이 같은 게 많다

- 같게 설정해서 어느 메소드에서 어느 메소드를 호출할지 알기 쉽게 해둔 것이다

ex) Service의 delete() 메소드에서 DAO 의 delete() 메소드를 호출

 

인터페이스를 사용하는 이유

- 예전에는 통일성있는 클래스를 작성하기 위해 인터페이스를 사용

- 지금은 메소드가 많아서 그걸 관리하기 위해 인터페이스를 사용한다 (어떤 메소드가 있는지 색인처럼 확인 가능)

 

- beans16.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="md" class="sample16.MemberDaoImpl" />
	<bean id="ms" class="sample16.MemberServiceImpl">
		<property name="md" ref="md" />
	</bean>

</beans>

- bean 을 써서 Setter DI 를 직접 하고 있다

- Sample17에서는 같은 내용을 어노테이션으로 처리

- Service 객체 "ms" 와 DAO 객체 "md"가 생성됨

- 그리고 Service 객체 "ms" 의 프로퍼티 md 에 위에서 만들어진 DAO 객체 "md" 가 주입되었다

Service 클래스의 프로퍼티 md

- 이때 Service 클래스에는 Setter 메소드가 있어야만 함

 

- Ex01.java

package sample16;

import java.util.Collection;
import java.util.List;
import java.util.Scanner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Ex01 {
	static MemberService ms = null;

	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("/sample16/beans16.xml");
		ms = (MemberService) ac.getBean("ms");
		Scanner sc = new Scanner(System.in);
		while (true) {
			System.out.println("명령어를 입력하세요?");
			String command = sc.nextLine();
			if (command.equals("x")) {
				System.out.println("프로그램 종료");
				break;
			//startsWith() 메소드는 캐릭터 라인이, 지정된 접두사로 시작 여부를 감지하는 데 사용
			// public boolean startsWith(String prefix)
			} else if (command.startsWith("new")) {  // 입력한 문자가 new로 시작하면 true 리턴함
				insert(command.split(" "));
				continue;
			} else if (command.startsWith("select")) {
				select(command.split(" "));
				continue;
			} else if (command.equals("list")) {
				list();
				continue;
			} else if (command.startsWith("delete")) {
				delete(command.split(" "));
				continue;
			} else if (command.startsWith("update")) {
				update(command.split(" "));
				continue;
			}
			help();
		}
		sc.close();
	}

	public static void insert(String[] str) {
		if (str.length != 5) {
			help();
			return;
		}
		RegisterMember rm = new RegisterMember();
		rm.setEmail(str[1]);
		rm.setName(str[2]);
		rm.setPass(str[3]);
		rm.setConfirmPass(str[4]);
		
		if (!rm.passCheck()) {
			System.out.println("똑바로 암호 입력해");
		} else {
			int result = ms.insert(rm);
			if (result > 0)
				System.out.println("입력 성공");
		}
	}

	public static void select(String[] str) {
		if (str.length != 2) {
			help();
			return;
		}
		Member member = ms.select(str[1]);
		if (member != null)
			System.out.println(member);
		else
			System.out.println("없는 데이터 입니다");
	}

	public static void list() {
		Collection<Member> list = ms.list();
		if (list != null) {
			for (Member member : list) {
				System.out.println(member);
			}
		}
	}

	public static void delete(String[] str) {
		if (str.length != 2) {
			help();
			return;
		}
		int result = ms.delete(str[1]);
		if (result > 0)
			System.out.println("삭제 성공");
	}

	public static void update(String[] str) {
		if (str.length != 5) {
			help();
			return;
		}
		RegisterMember rm = new RegisterMember();
		rm.setPass(str[3]);
		rm.setConfirmPass(str[4]);
		rm.setEmail(str[1]);
		rm.setName(str[2]);
		if (!rm.passCheck()) {
			System.out.println("똑바로 암호 입력해");
		} else {
			int result = ms.update(rm);
			if (result > 0)
				System.out.println("수정 성공");
		}
	}

	public static void help() {
		System.out.println("잘못 입력했습니다");
		System.out.println("명령어 사용법:");
		System.out.println("new 이메일 이름 암호 암호확인");
		System.out.println("update 이메일 이름 암호 암호확인");
		System.out.println("delete 이메일");
		System.out.println("select 이메일");
		System.out.println();
	}
}

 

Service 객체 구하기 (Ex01.java 부분)

		ms = (MemberService) ac.getBean("ms");

 

Scanner 객체 구하기 (Ex01.java 부분)

		Scanner sc = new Scanner(System.in);

- 키보드로 회원정보를 입력받거나 목록을 수정/삭제 하기 위해 Scanner 객체 생성

 

- While문 안에서 계속해서 입력을 받을 수 있도록 무한 루프가 돈다, x 입력시 빠져나감

 

While문 안 명령 한 줄 받기 (Ex01.java 부분)

			String command = sc.nextLine();

 

입력에 따라 분기 (Ex01.java 부분)

if (command.equals("x")) {
				System.out.println("프로그램 종료");
				break;
			//startsWith() 메소드는 캐릭터 라인이, 지정된 접두사로 시작 여부를 감지하는 데 사용
			// public boolean startsWith(String prefix)
			} else if (command.startsWith("new")) {  // 입력한 문자가 new로 시작하면 true 리턴함
				insert(command.split(" "));
				continue;
			} else if (command.startsWith("select")) {
				select(command.split(" "));
				continue;
			} else if (command.equals("list")) {
				list();
				continue;
			} else if (command.startsWith("delete")) {
				delete(command.split(" "));
				continue;
			} else if (command.startsWith("update")) {
				update(command.split(" "));
				continue;
			}
			help();

- 이때의 입력은 이렇게 입력해야함 (아래)

	public static void help() {
		System.out.println("잘못 입력했습니다");
		System.out.println("명령어 사용법:");
		System.out.println("new 이메일 이름 암호 암호확인");
		System.out.println("update 이메일 이름 암호 암호확인");
		System.out.println("delete 이메일");
		System.out.println("select 이메일");
		System.out.println();
	}

 

- 분기 문 중 "new" 로 시작한걸 보자

사용자가 new 를 입력했다면 (Ex01.java 부분)

			} else if (command.startsWith("new")) {  // 입력한 문자가 new로 시작하면 true 리턴함
				insert(command.split(" "));
				continue;

- startWith() 는 어떤 특정한 문자로 시작하는지 확인 가능하다

- command 가 "new" 로 시작하면 comand.startWith("new") 는 True 를 반환

- True 면 insert() 메소드가 실행됨, 이 메소드는 Ex01 의 main() 아래에 정의되어있다

- 매개변수로는 command.split(" ") 으로 자른 배열을 주고 있다 (split() 의 리턴값은 String 형 배열이다)

- "new lay99@g.com lay 1999 1999" 를 입력했다면 공백을 기준으로 잘라서 배열에 저장해서 insert() 매개변수 인자로 준다

- 이 배열을 insert() 호출시 전달

 

- Ex01.java 의 insert()  (Ex01.java 부분)

- insert() 는 main() 바깥 아래에 있다

	public static void insert(String[] str) {
		if (str.length != 5) {
			help();
			return;
		}
		RegisterMember rm = new RegisterMember();
		rm.setEmail(str[1]);
		rm.setName(str[2]);
		rm.setPass(str[3]);
		rm.setConfirmPass(str[4]);
		
		if (!rm.passCheck()) {
			System.out.println("똑바로 암호 입력해");
		} else {
			int result = ms.insert(rm);
			if (result > 0)
				System.out.println("입력 성공");
		}
	}

0. 매개변수 String 배열이 넘어왔다

- str[0] = new, str[1] = lay99@g.com, str[2] = lay, str[3] = 1999, str[4] = 1999 가 넘어왔다

- 명령어new, 이메일, 이름, 암호, 암호확인 이 배열 str에 순서대로 들어가 있다

1. 이때 형식이 맞는지를 먼저 확인한다, str.len 이 5개여야하므로 5개가 아니면 help() 호출 후 insert() 를 나간다

- insert() 를 나가서 main() 으로 돌아가서 continue 문에 의해 다시 while 문에 의해 입력을 받음

2. DTO RegisterMember 객체 rm을 생성해서 그 안에 이메일, 이름, 암호, 암호확인을 객체에 저장

3. RegisterMember 의 passCheck() 메소드로 암호와 암호확인이 일치한지 확인

- 불일치시 "똑바로 암호 입력해", 일치시 main() 에서 받아온 Service 객체 ms (전역변수임) 로 ms.insert() 호출

- ms.insert() 호출시 매개변수로 RegisterMember(DTO) 객체 rm 을 전달

- ms.insert() 에서 리턴된 값이 0 보다 크면 "입력 성공" 출력

 

- Service 클래스의 메소드를 호출했으므로 Service 로 넘어갔다

 

- MemberServiceImpl.java

package sample16;

import java.util.Collection;
import java.util.Date;
import java.util.List;

public class MemberServiceImpl implements MemberService {
	private MemberDao md;

	public void setMd(MemberDao md) {
		this.md = md;
	}

	public int insert(RegisterMember rm) {
		int result = 0;
		Member member = md.selectByEmail(rm.getEmail()); //email주소로 1명의 정보를 구해옴
		if (member == null) {	// 동일한 email 주소가 없으면 회원가입함
			member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
			md.insert(member);
			result = 1;
		} else {
			System.out.println("이미 데이터가 있습니다");
		}
		return result;
	}

	public Member select(String email) {
		return md.selectByEmail(email);
	}

	public Collection<Member> list() {
		return md.list();
	}

	public int delete(String email) {
		int result = 0; // 데이터가 이미 있는지 확인
		Member member = md.selectByEmail(email);
		if (member != null) {
			md.delete(email);
			result = 1;
		} else {
			System.out.println("없는네 우찌 삭제하니");
		}
		return result;
	}

	public int update(RegisterMember rm) {
		int result = 0; // 데이터가 이미 있는지 확인
		Member member = md.selectByEmail(rm.getEmail());
		if (member != null) {
			member.setPass(rm.getPass());  // 비번과 이름 수정
			member.setName(rm.getName());
			md.update(member);
			result = 1;
		} else {
			System.out.println("없는네 우찌 고치니 ? 헐");
		}
		return result;
	}
}

- DAO 객체를 주입할 필드 md, Setter 메소드 setMd() 가 있다

- 이 필드 md 에는 이미 DAO 객체가 주입되었다 (main 에서 beans16.xml 을 읽었을때 주입되었음)

 

insert() 부분만 (MemberServiceImpl.java 부분)

	public int insert(RegisterMember rm) {
		int result = 0;
		Member member = md.selectByEmail(rm.getEmail()); //email주소로 1명의 정보를 구해옴
		if (member == null) {	// 동일한 email 주소가 없으면 회원가입함
			member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
			md.insert(member);
			result = 1;
		} else {
			System.out.println("이미 데이터가 있습니다");
		}
		return result;
	}

- DAO 객체를 받은 md 로 md.selectByEmail() 로 이메일 주소 중복검사를 한다, 여기서 DAO 로 이동한다

 

- DAO 의 selectByEmail() 를 호출했으므로 DAO 로 갔다

- MemberDaoImpl.java

package sample16;

import java.util.*;

public class MemberDaoImpl implements MemberDao {
	private Map<String, Member> map = new HashMap<String, Member>();
	private static int nextId = 0;

	public void insert(Member member) {
		member.setId(++nextId);
		map.put(member.getEmail(), member);
	}

	public Member selectByEmail(String email) {
		return map.get(email);
	}

	public Collection<Member> list() {
		return (Collection<Member>) map.values();
	}

	public void delete(String email) {
		map.remove(email);
	}

	public void update(Member member) {
		map.put(member.getEmail(), member);
	}
}

- DB 연동을 안하므로, Map 자료구조에 회원 데이터를 저장할 것임, 여기 DAO 의 Map 객체 map 에 저장할 것

- Map 자료구조에서 키값은 String, 밸류값은 Member(DTO) 가 자료형이다

- 키값이 Email 주소이고, 밸류값은 해당 회원의 정보를 담게 할 것임

- nextId 는 몇명이 등록되어있는지 저장하는 역할, insert() 가 되면 이 값을 증가시켜서 setId() 메소드로 넘어온 DTO 객체 member 에 저장 * 아래까지 모두 보고와야 이해됨

<selectbyemail() 메소드>

- map.get() 으로 넘어온 이메일을 키로, 그 키의 밸류인 DTO 객체를 돌려주고 있다

- 해당 키(매개변수로 넘어온) 에 해당하는 밸류가 있으면 밸류를 리턴, 없으면 null 을 리턴

 

- Service 로 돌아가자

- Service 의 insert() 부분만

	public int insert(RegisterMember rm) {
		int result = 0;
		Member member = md.selectByEmail(rm.getEmail()); //email주소로 1명의 정보를 구해옴, *
		if (member == null) {	// 동일한 email 주소가 없으면 회원가입함
			member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
			md.insert(member);
			result = 1;
		} else {
			System.out.println("이미 데이터가 있습니다");
		}
		return result;
	}

<DAO selectByEmail() 에서 돌아온 뒤>

- * 가 돌아온 곳의 줄이다

- selectByEmail() 로 부터 돌아와서 Member 객체 member 로 가져온 객체를 저장한다

- select 문에서 돌려주는 값이 없어야 동일한 Email 이 없는 것임, 그래서 member == null 일때 삽입시켜야함

- 만약 객체를 제대로 가져왔다면 이메일 중복이 아니므로 이 회원을 등록(삽입) 할 것임

- 즉, 중복이 아닐때는 DAO 객체를 받은 md 로 md.insert() 해서 DAO 의 insert() 호출

<회원 등록>
- Member DTO 객체 member 에 매개변수로 넘어온 RegisterMember DTO 객체 rm 에서 암호, 이메일, 이름을 꺼내서 세팅

- 그 후 Service 객체 md 로 insert() 호출, 넘겨주는 값은 객체 member 이다

 

- 그럼 다시 DAO 로 간다

- DAO의 insert() 부분만

	public void insert(Member member) {
		member.setId(++nextId);
		map.put(member.getEmail(), member);
	}

- 매개변수로 넘어온 객체 member 에 setId() 로 id(아이디 비번할떄 id 가 아니라 인식을 위한 id) 를 세팅

- 현재는 DB 가 아닌 메모리에 저장한다, 그래서 map 자료구조에 put() 으로 키를 이메일, 밸류를 상세정보를 저장한 member 로 준다

+ map 은 키값 중복 안됨

- DAO 의 insert() 에서 반환은 하지 않음

 

- 다시 Service로 돌아옴

- Service 클래스 insert() 부분만

	public int insert(RegisterMember rm) {
		int result = 0;
		Member member = md.selectByEmail(rm.getEmail()); //email주소로 1명의 정보를 구해옴
		if (member == null) {	// 동일한 email 주소가 없으면 회원가입함
			member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
			md.insert(member); // * 돌아온 곳 
			result = 1;
		} else {
			System.out.println("이미 데이터가 있습니다");
		}
		return result;
	}

* 돌아온 곳

- DAO의 insert 에서 반환은 하지 않는다, 그냥 삽입만 한 것

- 여기선 DB연동이 아니므로 DAO까지 갔다 온 후 내가 임의로 result 에 1 을 넣고 그 result 를 반환

 

- 그러면 다시 Ex01.java 로 간다

- Ex01.java 에서 insert() 부분만

	public static void insert(String[] str) {
		if (str.length != 5) {
			help();
			return;
		}
		RegisterMember rm = new RegisterMember();
		rm.setEmail(str[1]);
		rm.setName(str[2]);
		rm.setPass(str[3]);
		rm.setConfirmPass(str[4]);
		
		if (!rm.passCheck()) {
			System.out.println("똑바로 암호 입력해");
		} else {
			int result = ms.insert(rm); // * 돌아온 곳
			if (result > 0)
				System.out.println("입력 성공");
		}
	}

* 돌아온 곳

- result 에 1이 반환되었으므로 아래의 "입력 성공" 을 출력

 

- 실제 입력을 해보자


- 이제 검색을 해보자

- 검색할때는 이메일 주소로만 검색 가능

 

Ex01.java

- main() 에서의 명령에 따른 분기문은 아까와 같은 원리이므로 설명 생략, select() 로 간다

 

Ex01.java 의 select()

	public static void select(String[] str) {
		if (str.length != 2) {
			help();
			return;
		}
		Member member = ms.select(str[1]);
		if (member != null)
			System.out.println(member);
		else
			System.out.println("없는 데이터 입니다");
	}

- 형식이 "select 이메일" 이므로 length 가 2인지 확인

- Service 객체 ms 로 ms.select() 하고, 매개변수로는 이메일을 넘김

 

- Service 클래스 MemberServiceImpl.java 의 select() 부분만

	public Member select(String email) {
		return md.selectByEmail(email);
	}

- DAO 객체 md 로 DAO 의 selectByEmail() 을 호출, 매개변수로는 넘어온 email 을 그대로 넘겨줌

 

- DAO 클래스 MemberDaoImpl.java의 selectByEmail() 부분만

	public Member selectByEmail(String email) {
		return map.get(email);
	}

- 회원이 저장되어있는 Map 객체 map 에서 get(키) 로 밸류인 Member 객체를 구해옴

 

- 이제 돌아간다

- Service 클래스 MemberServiceImpl.java 의 select() 부분만

	public Member select(String email) {
		return md.selectByEmail(email);
	}

- 그대로 리턴함, Ex01.java 로 간다

 

- Ex01.java 의 select() 부분만

	public static void select(String[] str) {
		if (str.length != 2) {
			help();
			return;
		}
		Member member = ms.select(str[1]);
		if (member != null)
			System.out.println(member);
		else
			System.out.println("없는 데이터 입니다");
	}

- ms.select() 에서 만약 있는 회원이면 객체를 반환해서 member 에 저장, 없으면 null 값이 member 로 들어감

- 그래서 member != null 일땐 member 출력 (Member DTO 클래스에 toString() 이 있으므로 그걸로 회원 정보 모두 출력)

 

- 검색을 실제로 해보면

- 검색된다


- 이제 목록 출력을 해보자

 

- 목록 출력시에는 "list" 라고만 입력하면된다

 

Ex01.java

- main() 에서의 명령에 따른 분기문은 아까와 같은 원리이므로 설명 생략, list() 로 간다

- Ex01.java 의 list() 부분만

	public static void list() {
		Collection<Member> list = ms.list();
		if (list != null) {
			for (Member member : list) {
				System.out.println(member);
			}
		}
	}

- Service 객체 ms 로 ms.list() 호출하고 Collection<Member>가 자료형인 list 로 반환

+ Collection : 인터페이스, 객체의 모음을 저장, List, Set, Map 이 이 Collection 을 상속함

 

- Service 클래스 MemberServiceImpl.java 의 list() 부분만

	public Collection<Member> list() {
		return md.list();
	}

- DAO 객체 md 로 md.list() 를 호출하고 있다

 

- DAO 클래스 MemberDaoImpl.java 의 list() 부분만

	public Collection<Member> list() {
		return (Collection<Member>) map.values();
	}

- 회원 데이터를 저장하는 Map 객체 map 으로 map.values() 로 map 에 저장된 모든 value 값들을 가져와서 리턴함

- map.values() 는 모든 회원 정보, 즉 DTO 객체들의 모음이다

- map.values() 는 리턴자료형이 Collection 이다!

- 그럼 Service 클래스로 다시 돌아가자

 

- Service 클래스 MemberServiceImpl.java 의 list() 부분만

	public Collection<Member> list() {
		return md.list();
	}

- 그대로 Ex01 로 리턴함

 

- Ex01.java 의 list() 부분만

	public static void list() {
		Collection<Member> list = ms.list();
		if (list != null) {
			for (Member member : list) {
				System.out.println(member);
			}
		}
	}

- 돌아와서 데이터들을 받아서 for문을 통해서 하나씩 출력하고 있다 (toString() 사용됨)

 

- update / delete 는 간략히만 설명

 

update

- update는 기존에 있는 이메일을 써야하고 이름과 비번을 수정 가능하다

<Ex01.java>

- update 는 RegisterMember의 passCheck() 를 먼저 호출해서 암호와 암호확인이 일치하는지 확인 후 일치시 Service 의 update() 호출

<Service>

- Service 의 update() 안에서 DAO의 selectByEmail() 를 호출해서 있는 이메일인지 확인

- 중복이 아닌 이메일이면 DAO의 update() 를 호출해서 수정

<DAO의 update()>

- update 는 키값이 중복되면 마지막의 키값만 사용할 수 있다는 점을 이용

- 즉 정보수정을 원하는 회원의 키값과 똑같은 키값으로 새로운 데이터(수정용 데이터)를 저장

 

delete

- map 에서 remove() 메소드로 키값 email 로 해당 데이터를 삭제


Spring DI 예제 16 (어노테이션 기반, 회원관리 콘솔 프로그램)

- src/main/java/sample17/ 안의 파일들

- 어노테이션 기반으로 만든 콘솔 회원관리 프로그램

 

Spring DI 예제 17 구조 & 흐름

- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller

- 현재는 Web Application 이 아닌  Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임

- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자

 

- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다, 명령어에 따라 회원정보 검색, 등록, 삭제, 수정 가능

- MemberService.java : 인터페이스, 추상메소드들이 많이 있다 ex) delete(), insert(), update(), select(), list()

- MemberServiceImpl.java : MemberService 를 상속, MemberService 의 추상메소드들을 모두 오버라이딩

- MemberDao.java : 인터페이스, 추상메소드들이 많이 있다 ex) delete(), insert(), update(), selectByEmail(), list()

- MemberDaoImpl.java : MemberDao를 상속, MemberDao 의 추상메소드들을 모두 오버라이딩

- Member.java : DTO, 필드 id,pass,email,name,reg_date 와 각각의 getter/setter 메소드, toString() 메소드

- RegisterMember.java : DTO, 필드 pass, confirmpass, name, email 와 각각의 getter/setter 메소드, passCheck() 메소드

- DTO 객체가 2개이다

- RegisterMember.java DTO 클래스는 가입할때만 사용된다

 

Service 클래스와 DAO 클래스의 메소드 명이 같은 게 많다

- 같게 설정해서 어느 메소드에서 어느 메소드를 호출할지 알기 쉽게 해둔 것이다

ex) Service의 delete() 메소드에서 DAO 의 delete() 메소드를 호출

 

인터페이스를 사용하는 이유

- 예전에는 통일성있는 클래스를 작성하기 위해 인터페이스를 사용

- 지금은 메소드가 많아서 그걸 관리하기 위해 인터페이스를 사용한다 (어떤 메소드가 있는지 색인처럼 확인 가능)

 

- beans17.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
	<context:component-scan base-package="sample17"/>
</beans>

- 조건 3가지 중 첫번재를 만족한다, sample17 패키지를 읽어온다

 

- MemberServiceImpl.java

package sample17;

import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("ms")
public class MemberServiceImpl implements MemberService {
	@Autowired
	private MemberDao md;

	public int insert(RegisterMember rm) {
		int result = 0;
		Member member = md.selectByEmail(rm.getEmail());
		if (member == null) {
			member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
			md.insert(member);
			result = 1;
		} else {
			System.out.println("이미 데이터가 있습니다");
		}
		return result;
	}

	public Member select(String email) {
		return md.selectByEmail(email);
	}

	public Collection<Member> list() {
		return md.list();
	}

	public int delete(String email) {
		int result = 0; // 데이터가 이미 있는지 확인
		Member member = md.selectByEmail(email);
		if (member != null) {
			md.delete(email);
			result = 1;
		} else {
			System.out.println("없는네 우찌 삭제하니");
		}
		return result;
	}

	public int update(RegisterMember rm) {
		int result = 0; // 데이터가 이미 있는지 확인
		Member member = md.selectByEmail(rm.getEmail());
		if (member != null) {
			member.setPass(rm.getPass());
			member.setName(rm.getName());
			md.update(member);
			result = 1;
		} else {
			System.out.println("없는네 우찌 고치니 ? 헐");
		}
		return result;
	}
}

 

@Service 어노테이션 (MemberServiceImpl.java 부분)

- 조건 3개 중 두번째 조건이다

- @Service 어노테이션 안에서 Service 객체명의 이름을 지정할 수 있다

- sample16 에서는 getBean() 으로 구해왔다

- 여기선 bean 을 직접 만들지 않았으므로 sample15 처럼 getBean(서비스클래스명.class) 로 구해와야한다

- 여기선 @Service 어노테이션 안에 Service 객체명(객체 id값)으로 "ms" 를 지정했으므로 main() 에서 Service 객체를 구해올때 getBean("ms") 로 구해올 수 있다

 

+ Ex01.java 의 main() 부분

- 만약 @Service("ms") 가 아닌 @Service 만 했다면 ac.getBean(MemberService.class) 로 Service 객체를 구해야한다

- 하지만 Service 클래스 상단에서 @Service("ms") 로 그 객체에 이름을 줬으므로 이름으로 getBean("ms") 불러옴 

 

@Autowired 어노테이션 (MemberServiceImpl.java 부분)

- 조건 3개 중 세번째 조건이다

- DAO 객체를 주입받아야하는 프로퍼티 md 위에 @Autowired 를 작성

- 그럼 DAO 객체 생성과 md 로 주입까지 완료된다

 

- MemberDaoImpl.java

package sample17;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Repository;

@Repository
public class MemberDaoImpl implements MemberDao {
	private Map<String, Member> map = new HashMap<String, Member>();
	private static int nextId = 0;

	public void insert(Member member) {
		member.setId(++nextId);
		map.put(member.getEmail(), member);
	}

	public Member selectByEmail(String email) {
		return map.get(email);
	}

	public Collection<Member> list() {
		return (Collection<Member>) map.values();
	}

	public void delete(String email) {
		map.remove(email);
	}

	public void update(Member member) {
		map.put(member.getEmail(), member);
	}
}

 

@Reopsitory 어노테이션 (MemberDaoImpl.java 부분)

- 지금은 DAO 클래스에서 아무 객체도 주입받고 있지 않으므로 @Repository 가 없어도 된다

- 나중에 DAO 클래스에서 SqlSession 객체를 생성 해서 DAO의 프로퍼티로 주입할때는 @Repository 를 위에 붙여야한다

 


스스로 질문 & 스스로 답변

Q. Service 클래스 상단에서 @Service 만으로 썻으면 main() 에서 Service 객체 구해올때 getBean(Service클래스명.class) 로 구해왔었다, 근데 나중에 Web Application 를 해서 Controller -> Service 로 갈떄도 getBean(Service클래스명.class) 를 쓰는가?

 

A. 아니다, 지금 Application 에서는 main() 에서 Service 객체를 구해와야하므로 getBean() 을 쓰지만 나중에 Controller -> Service 로 갈때는 Controller 클래스에서 @Autowired 로 Service 객체를 바로 프로퍼티에 주입시키므로 그냥 바로 쓰면 된다, getBean() 으로 직접 구해올 필요 없이 이미 Spring 환경설정 파일을 읽어올때 다 생성되고 주입되었음, 주입된 Service객체를 바로 쓰면 됨

 

- 이렇게 @Autowired 로 Controller 의 프로퍼티 service 에 바로 Service 객체가 주입됨 

앞으로 학습할 것

Spring

- 환경 설정이 어렵다

- 환경 설정 파일이 맞물려서 돌아가므로 하나가 잘못되면 아파치 톰캣이 동작 안함

- 어노테이션 기반으로 처리되므로 값의 전달, 리턴이 보이지 않음

 

Springboot

- 환경 설정이 비교적 쉽다

- 출력 방법으로 JSTL 외에도 타임리프 표기법이 내장

 

타임리프

- 타임리프 사용시 View 페이지를 jsp 파일로 만들지 않고 html 파일로 만든다, 기존 jsp 태그 사용 불가

- EL / JSTL 과 같은 역할

 

- zip 으로 압축한 프로젝트를 바탕화면에 복사, 압축 해제 후 import 

- import 하는 방법은 Maven 프로젝트와 같다


Spring Framework

Spring framework 창시

- 2000 년 초반 경 등장

- 로드 존슨(Rod Johnson)이 자신의 JAVA 개발 경험과 노하우를 기반으로 출판한 책 (Expert One-oneOne J2EE Design and Development)에 샘플 프레임워크를 만들어서 저서에 실었는데, 

- 이것이 차후 스프링이란 자바 표준 프레임워크로 발전하게 되었다.

- 엔터프라이즈 어플리케이션 개발의 복잡성을 줄여주기 위한 목적으로 개발 되었다.

- 대용량 프로젝트 개발에 사용하던 복잡한 구조의 EJB 사용으로 수행되었던 모든 기능을 쉽게 경량화하여 일반 POJO(Plain Old Java Object) 를 사용해서 가능하게 하였다 (Java 를 써서 개발한다는 의미)

 

Spring framework 특징

1. "경량 컨테이너"(크기와 부하의 측면)로서 자바 객체를 직접 관리

- 즉 자바를 이용한 Spring 프레임워크

 

2. 제어 역행(IoC : Inversion of Control)

- 애플리케이션의 느슨한 결합을 도모.

- 기존 방식으로 객체를 생성해서 처리하는게 아니라 xml 파일로 처리

 

3. 의존성 주입(DI : Dependency Injection)

- 각각의 계층이나 서비스들 간에 의존성이 존재할 경우 프레임워크가 서로 연결시켜준다.

 

4. 관점지향 프로그래밍(AOP : Aspect-Oriented Programming)

- 트랜잭션이나 로깅, 보안과 같이 여러 모듈에서 공통적으로 사용하는 기능의 경우 해당 기능을 분리하여 관리할 수 있다.

- 트랜잭션이나 로깅, 보안과 같이 모든 개발자들이 공통적으로 처리해야하는 작업 (공통모듈) 을 따로 처리 가능

 

5. 모델-뷰-컨트롤러 패턴

- 웹 프로그램밍 개발 시 거의 표준적인 방식인 "Spring MVC"라 불리는 모델-뷰-컨트롤러(MVC) 패턴을 사용한다.

- 모델, 뷰, 컨트롤러로 분리시켜 개발하는 방법, Model 2 방식과 유사

 

Spring framework 환경 구축 2가지 방법

1. Eclipse에 STS프로그램을 plugin으로 추가해서 사용하는 방법
- 우리가 쓰는 이클립스 내에는 Spring 프로젝트를 만들 수 있는 메뉴가 없으므로 plugin 추가

- 기존 프로젝트가 잘 동작하지 않거나 sql 파일이 열리지 않는 등의 문제가 많이 발생한다

- 기존 Eclipse 에 [Help] - MarketPlace 에서 "sts" 검색 후 Spring Tools 3 설치

2. STS 프로그램 다운로드 받아서 사용하는 방법

- 이 방법을 사용할 것


Spring 환경 구축 (STS 프로그램 다운)

- STS 는 기존 이클립스와 유사

- 기존 이클립스와 다른 점 : Spring 이나 Springboot Project 를 만들 수 있는 메뉴 내장

+ 이제 이클립스는 실습시에 쓰지 않을 것, 나머지 수업은 모두 STS 로 진행

+ STS 에는 Data Source Management (DB접속) 기능이 빠져있다

 

- 2가지 방법 중 STS 프로그램 다운로드 받는 방법 사용

 

STS 버전

- STS 는 3.x 와 4.x 버전이 있다,

- 4.x 는 Spring boot project 만 만들 수 있는 메뉴만 나타나고 Spring project 만드는 메뉴 나타나지 않음

- 3.x 설치시 Spring project, Spring boot project 모두 만들 수 있는 메뉴 나타남

- 실습을 위해 3점대를 설치할 것, 자바를 낮은 버전을 쓰고 있으므로 STS 3.9.11 (3.9 중에서 가장 낮은 버전) 을 설치한다

 

STS (Spring Tool Suite) 4.x 다운로드

- https://spring.io/tools 접속

 

STS (Spring Tool Suite) 3.x 다운로드

- 이걸 다운받을 것

- https://github.com/spring-projects/toolsuite-distribution/wiki/Spring-Tool-Suite-3 접속

- 이 다운받은 zip 파일을 C 드라이브에 저장시켰다

- 압축 푼 후 sts-bundle/sts-3.9.11.RELEASE 로 가서 sts.exe 라는 실행 파일 실행시키면 STS 가 구동됨

- 해당 실행 파일을 단축 아이콘 만들 것 (오른쪽 마우스 -> 보내기 -> 바탕 화면에 바로 가기 만들기)

- 해당 실행 파일 실행시키기

- Workspace 를 지정, 난 기본 Workspace 그대로 사용

- 기본 Workspace : C:\Users\admin\Documents\workspace-sts-3.9.11.RELEASE

- 이클립스와 비슷함

- 이클립스와 다른 점 : Spring Project, Springboot Project 를 만들 수 있는 메뉴가 추가되어있다

- STS 에는 Apache Tomcat 말고도 Pivotal 서버가 기본 내장되어있다, 우리는 Apache Tomcat 만 사용할 것이므로 Pivotal 서버는 삭제하자

서버를 삭제시 2군데서 지워야한다

1. Servers 탭에서 삭제

2. [Window] -> Preferences -> Server -> Runtime Environment 에서 선택 후 Remove

 

Dynamic Web Project 를 생성

- 아파치 톰캣의 버전과 위치를 설정하자, 최초 1번은 연결시켜야함

- 프로젝트명은 jsptest

- 위의 Defalt output folder 는 바이트코드가 저장되는 위치임

- 이때 index 파일 생성 전에 Encoding 을 먼저 설정하자

- 프로젝트 생성 시 가장 먼저 Encoding 부터 설정해야함, 모두 UTF-8 로 변환

 

인코딩 설정

[Window] -> Preferences -> "encoding" 이라 검색

- Workspace, CSS Files, HTML Files, JSP Files 의 Encoding 을 모두 "UTF-8" 로 변환 후 적용

- XML Files 는 기본으로 UTF-8 로 설정되어있으므로 변경하지 않음

 

index 파일 생성

- jsptest 프로젝트 WebContent 폴더 하위에 index.jsp 생성

- 실행시 jsptest~!! 가 잘 출력됨

 

포트번호 확인

- 아래의 Tomcat v9.0 을 더블클릭

- 이클립스에서 쓰는 포트와 다른 포트를 써야함

- 이클립스에서 80 을 쓰고 있지 않으므로 sts 에서 실행됐던 것

- 변경을 원할땐 Servers 에서 server.xml 을 열어서 수정

 

폰트 크기 설정

[Window] - Preference - Appearance - Colors and Fonts

 

Spring MVC Project 생성

- [File] - New - Project

- 이 Templates 를 어떤걸 선택하냐에 따라 구조가 달라진다

- Spring MVC Project 를 만들때 top-level package 명을 3단계로 입력해야한다

- 도메인명 역순으로 3단계를 입력, com.myhome.springtest 로 하자

 

- Spring MVC Project 만들면 java, resources, webapp 3가지 폴더가 만들어짐

- 그 중 java 폴더 하위에 com.myhome.springtest 패키지 하위에 Controller 클래스가 자동 생성되게 된다

 

+ Maven 으로 라이브러리 관리하는게 기본이므로 Spring MVC Project 를 생성하면 기존에 없던 내용들을 로컬 저장소로 다운한다

 

+ 프로젝트 왼쪽 아이콘

s : spring project

m : maven project

 

- Spring 은 기본적으로 Maven 으로 라이브러리를 관리한다

- Maven 환경설정 파일인 pom.xml 을 열어보자

- pom.xml (초기)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.myhome</groupId>
	<artifactId>springtest</artifactId>
	<name>springtest</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.6</java-version>
		<org.springframework-version>3.1.1.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				 </exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
				
		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>	
		
		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>
				
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	
		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>        
	</dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

- Maven 프로젝트와 달리 Spring 프로젝트는 pom.xml 에 60% 이상의 기본적인 내용들이 모두 들어가 있음

- 많은 Dependency 들이 이미 추가되어있음 (inject, java servlet, jsp, jstl 등)

- groupId 와 ArtifactId 에 top-level package 등록한게 여기 나와있다

- <java-version> 을 현재 사용하고 있는 1.8 버전으로 나중에 수정하자 

- 현재 Spring 버전은 3.1.1 으로 되어있을을 확인 가능, 나중에 필요하면 올리면 된다

 

 

Spring MVC Project 구조

- main 폴더 아래에는 Maven 프로젝트처럼 java , resources, webapp 폴더가 있다

- java 폴더 안에 com/myhome/springtest 라는 top-level 프로젝트가 만들어져 있다, 그 안에 sample 로 만들어진 Controller 클래스 HomeController.java 가 있음

- test 폴더는 테스트 용도

- WEB-INF/views 폴더에는 View 파일들이 들어감

- WEB-INF 안에는 환경설정 파일 xml 파일들이 많이 있다

- webapp 폴더 안에 하위 폴더들이 여러개 있다

- webapp 폴더 안에 web.xml 파일도 있다

 

- HomeController.java

package com.myhome.springtest;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
	
}

- return "home" 으로 되어있다, home.jsp 파일을 의미

 

Model 2 에서 클라이언트가 요청시 Controller 로 갈때 2가지 방법

1) 어노테이션

2) web.xml 파일 안에 Controller 클래스 이름, 위치가 등록되어있고 매핑으로 찾아감

- DispatcherServlet 을 FrontController 클래스라 부름

- web.xml 파일 안에 이 DispatcherServlet 클래스로 찾아가도록 매핑을 잡는 내용들이 있다

- 현재는 web.xml 파일 안에서 매핑을 잡을때 <url-pattern> 이 / 이므로 어떤 패턴으로 요청해도 찾아간다

* web.xml 코드는 아래에

 

Spring MVC 흐름도 (간략)

- 클라이언트가 요청시 가장 먼저 앞에 있는 Controller 클래스라는 의미의 Front Controller 클래스(= Dispatcher Servlet) 로 찾아간다

- Dispatcher Servlet Class = Front Controller Class

- Dispatcher Servlet Class를 찾아갈때는 web.xml 에 매핑을 통해 찾아간다

 

- HomeController 클래스는 샘플로 만들어진 클래스이며 FrontController 클래스와 다름, 사진에서 오른쪽(뒤쪽) 에 있는 Controller 클래스이다

 

 

Spring Project 실행하는 방법

- springtest 프로젝트에서 오른쪽 마우스 -> Run As -> Run on Server

- 한글 인코딩이 되어있지 않아서 한글값이 깨져서 나온다

- 한글값이 깨지지 않게 설정해보자

 

- /webapp/WEB-INF/views/home.jsp 파일의 1라인에 아래의 코드를 추가후에 다시 실행

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

- 현재 문서의 한글값을 UTF-8 로 인코딩하는 코드이다

- home.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
</head>
<body>
<h1>
	Hello world!  
</h1>

<P>  The time on the server is ${serverTime}. </P>
</body>
</html>

- 인코딩이 되었다


Spring MVC 흐름 설명

1. 먼저 Client -> Dispatcher Servlet 으로 간다

- web.xml 을 통해 설정을 해주면 자동으로 간다

- Dispatcher Servlet 클래스는 내부적인 처리이고 우리가 볼 수 없음

2. Dispatcher Servlet -> Controller 로 이동

- Client 에서 요청한 요청이름명에 해당하는 Controller @RequestMappign 으로 간다

3. Controller 클래스에서 Service, DAO 와 작업을 함

- @RequestMapping 어노테이션 사용해서 나누기

- 이 부분은 나중에 설명

4.Controller 클래스에서 ModelAndView 에 값을 저장 해서 Dispatcher Servlet 으로 돌아감

- return 으로 지정된 곳의 View 페이지로 가게됨

5. Dispatcher Servlet 에서 ViewResolver 의 prefix, suffix 를 붙여서 해당 View 로 간다

6. View 에서 EL 등으로 출력

 

Spring MVC 흐름도

- 값이 전달되는 흐름을 보여준다

1. 먼저 Client -> Dispatcher Servlet 으로 간다

- Model 2 와 다른점 : Spring 에는 Dispatcher Servlet 클래스를 Front Controller 클래스라고 부른다

- 가장 앞에 있는 Controller 클래스 = Front Controller 클래스 = Dispatcher Servlet

- 요청이 오면 Front Controller 클래스로 가장 먼저 찾아가야함

- Dispatcher Servlet 클래스는 Spring 지원 라이브러리에 있다

 

Front Controller 클래스

- 모든 클라이언트의 요청은 가장 먼저 여기로 온다 , 흐름을 제어함

- web.xml 에서 매핑을 잡아오면 여기까지는 자동으로 온다

- 내부적으로 찾아가므로 url pattern 값만 수정하고, 여기까지 찾아가는 것은 크게 신경쓰지 않아도 됨

- 기본적으로 제공되는 Dispatcher Servlet 클래스 사용해서 만듬

- web.xml 에서 DIspatcher Servlet 클래스의 위치 또한 등록되어있다

 

Front Controller 클래스로 찾아가는 방법 2가지 중 web.xml 방법

- web.xml 은 Apache Tomcat 구동시 가장 먼저 읽어오는 파일이다

+ Dynamic Web Project 의 web.xml 에는 파일을 찾는 순서가 들어있다

- web.xml 에는 Dispatcher Servlet 클래스의 이름과 위치가 등록되어있으므로 이 클래스로 찾아가기 위한 Servlet 매핑을 잡는 내용이 web.xml 에 들어가 있다

 

- Dispatcher Servlet 까지는 자동으로 찾아가므로 크게 신경 쓰지 않아도 된다

- 우리는 패턴만 바꾸면 됨

- 그림은 뒤의 Service 와 DAO 가 나타나 있지 않음, Controller 클래스까지 가는 흐름만 그림에 표시

 

- web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>
	
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

- <servlet> : <servlet-class> 에 Front Controller 클래스 위치가 등록되어 있다

- <servlet-mapping> : 어떤 확장자로 요청할때만 찾아갈지 설정하는곳, URL 패턴 지정

- <servlet> 과 <servlet-mapping> 안에 있는 <servlet-name> 값은 일치해야함

- <servlet-class> : Front Controller 클래스는 DispatcherServlet 클래스로 생성하므로 DispatcherServlet 클래스의 위치가 등록되어있음

- <url-pattern> 에 / 로 되어있으므로 모든 요청을 받음, 아무렇게 요청해도 반드시 DispatcherServlet 으로 가게된다

- 이게 WebServlet 어노테이션이 하는 역할과 같은 역할

 

2. Dispatcher Servlet -> Controller 로 이동

Controller 클래스 ( Front Controller 클래스 아닌 뒤의 Controller 클래스)

- 프로젝트 생성시 Sample 로 HomeController 클래스가 만들어진다, 나중엔 이걸 지우고 직접 Controller 클래스들 생성

- 이 HomeController 클래스가 Front 가 아닌 뒤의 Controller 클래스임

- Controller 클래스는 여러개 만들어진다 (프로그램마다 하나씩)

 

- Controller 클래스는 상단에 @Controller 어노테이션을 붙인다, 그럼 Controller 클래스 기능을 함

+ Spring 버전이 낮았을땐 클라이언트가 어떤 패턴으로 찾아왔을때 어디로 갈지 설정해주는 Handler Mapping 을 사용해서 Controller 클래스로 찾아갔다

- 지금은 Spring 버전이 높아져서 어노테이션 기반으로 바뀌면서 Handler Mapping 대신 @RequestMapping 어노테이션으로 요청을 받는다

	@RequestMapping(value = "/", method = RequestMethod.GET)

- value 에 "/" 에 있다, 아무거나 요청해도 모든 요청을 받겠다는 의미, 나중에 수정할 것

- value 에 해당하는 요청으로 오면 아래의 메소드 home() 은 자동으로 실행해줌

+ 나중엔 이 @RequestMapping 이 여러개 들어감

+ 나중엔 @RequestMapping에 구체적으로 어느 이름으로 온 요청을 받을지 따로 따로 들어감 

 

- Sample 인 Controller 클래스를 보자

- HomeController.java

package com.myhome.springtest;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);
		
		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
		
		String formattedDate = dateFormat.format(date);
		
		model.addAttribute("serverTime", formattedDate );
		
		return "home";
	}
	
}

- home() 의 리턴자료형이 String 으로 되어있다, 나중에 String 을 리턴

- home() 안의 내용에서 Date 객체를 생성하고 DateFormat 클래스를 써서 DateFormat 을 LONG 형으로 길게 설정, 그 객체로 format() 해서 날짜를 포매팅해서 formattedDate 에 저장,

- 이걸 Model 2 에서는 공유설정했다, Spring 에서는 Model 객체에 값을 addAttribute () 메소드로 키 밸류 형태로 저장함

- Spring 에서는 Model model 을 매개변수에 추가하면 자동으로 Model 객체가 생성됨'

public String home(Locale locale, Model model){}

- LONG 형으로 길게 설정된 포매팅된 날짜 formattedDate 를 value 로 저장

- 이 값을 JSP 파일에서 EL 로 ${serverTime} 을 쓰면 포매팅된 날짜 출력됨 

- return "home" 하고 있다 -> home.jsp 파일에서 EL로 그 날짜를 꺼내서 출력함

- Model 객체는 request 객체와 비슷한 역할

- Controller -> Dispatcher Servlet -> View 로 이동함 , 바로 View인 home.jsp 로 가는게 아님

- Dispatcher Servlet 은 내부적인 처리이므로 크게 신경쓰지 않아도 된다

 

3. Controller 클래스에서 Service, DAO 와 작업을 함

4.Controller 클래스에서 ModelAndView 에 값을 저장 해서 Dispatcher Servlet 으로 돌아감

- DB연동 끝나고 돌아와서 값 가져갈땐 Model 객체 이나 ModelandView 객체에 값을 저장해서 View페이지로 이동

- 이제 Client -> Dispatcher Servlet -> Controller 클래스까지 왔다

5. Dispatcher Servlet 에서 ViewResolver 의 prefix, suffix 를 붙여서 해당 View 로 간다

 

ViewResolver

- 여기선 View파일들이 저장될 위치를 저장해야함

- 이 ViewResolver 로 View파일들이 어디에 저장될지 위치를 설정함

- 환경설정 파일 WEB-INF/spring/appServlet/servlet-contest.xml 을 열어보면 위치가 나와있다 "/WEB-INF/views/" 로 설정되어있음

- 즉 home.jsp 가 어디에 저장되어있는지 설정하는거

 

- servlet-contest.xml

- HomeController 클래스의 home() 에서 return "home" 을 썻던 이유

1) 여기서 prefix로 설정된 /WEB-INF/views/ 를 생략한것임

2) suffix로 설정된 확장자인 .jsp 도 생략해서 "home" 이라 썼던 것

- 그걸 생략해야만 jsp 파일로 찾아간다

 

- Controller 에서 리턴시 Dispatcher Servlet 으로 갔다가 ViewResolver 로 가서 prefix 와 suffix 를 붙여서 home.jsp 인 View로 찾아가는것임!!

 

6. View 에서 EL 등으로 출력

- home.jsp 에서 "serverTime" 을 EL로 출력하는 것임

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
</head>
<body>
<h1>
	Hello world!  
</h1>

<P>  The time on the server is ${serverTime}. </P>
</body>
</html>

 

흐름 정리

- 여기까지 Client -> Front Controller -> Controller(Service/DAO와 작업) -> Front Controller -> ViewResolver -> View 이다

 

Spring 환경 설정 파일 2가지

1. servlet-context.xml

2.  root-context.xml : 오라클과 연동시 DB연동과 관련된 내용이 들어감, Bean 객체가 여기 들어감

- 둘 다 자동으로 실행되지 않는다, web.xml 에 등록해서 사용해야함

- web.xml 은 Apache Tomcat 구동시 자동으로 가장 먼저 실행됨, 두 환경 설정 파일을 연쇄적으로 실행되도록 하는 것

 

- web.xml

 

web.xml 에 들어가는 주요 3가지 내용

1. DispatcherServlet 위치 설정 : <servlet-class> 태그 안에 있다

2. 환경설정 파일 2개 불러오기 : root-context.xml 과 servlet-context.xml 파일

3. 한글 인코딩 (아직 안들어가있다)


Spring DI (Dependency Injection)

- 번역 : 의존성 주입

- Spring 에서 가장 많이 사용되고 중요한 개념

 

IoC (Inversion of Control)

- 번역 : 제어의 역행

- DI 와 함께 중요한 개념

- 기존에 개발자들이 빈 객체를 관리해 오던 개념에서 빈 관리를 컨테이너에서 처리한다는 의미

 

- 클라우드의 spring/소스/ch01 zip 파일을 압축 해제 후 import

- Maven Project는 모두 이렇게 import

- import 한 프로젝트 ch01 는 현재 Web Application 이 아닌 Application 이라서 webapp 폴더가 없다

- 일반적인 자바개발을 하는 프로젝트 처럼 되어있음

- pom.xml 파일을 열어보자

- 현재 Spring 4.3.6 버전을 사용하고 있다

 

Spring DI 예제 1 (기존 시스템, 기존 자바 방식)

- src/main/java/sample01/ 안의 파일들

- Ex01.java : 메인메소드를 가진 클래스이다, MessageBeanKr 클래스로 객체 mb 생성, mb.sayHello("Spring") 호출

- MessageBeanEn.java : 매개변수로 전달된 값을 출력하는 메소드 sayHello() 가 있다

- MessageBeanKr.java : 매개변수로 전달된 값을 출력하는 메소드 sayHello() 가 있다

- 특정 클래스의 메소드를 호출하려면 가장 먼저 그 클래스로 객체를 생성해야함

- 기존 시스템, 기존 자바에서 사용하는 방식임

- MessageBeanKr 의 sayhello() 메소드 호출하고 싶으면 Ex01 클래스에서 MessageBeanKr 클래스로 객체를 생성 후 메소드 호출해야함

 

- Spring 에서는 이런방식을 쓰지 않는다

- 이 방식은 Application 에서 직접 MessageBeanKr 클래스 객체를 생성해서 메소드를 사용하므로 두 클래스 사이에 의존성이 강하다는 표현을 함

- 직접 클래스로 객체를 생성하는 것보다 의존관계를 낮출 수 있는 방법을 예제 2에서 설명

 

Spring DI 예제 2 (의존도를 조금 낮추는 방식)

- src/main/java/sample02/ 안의 파일들

- Ex01.java : 메인메소드를 가진 클래스이다, 가장 먼저 MessageBeanKr() 클래스로 객체 생성하고 왼쪽은 부모 인터페이스로 받음, 즉 업캐스팅하여 객체 mb 생성 후 mb.sayHello() 호출

- MessagBean.java : 부모 인터페이스, 추상메소드 sayHello()

- MessageBeanEn.java : MessageBean 인터페이스를 상속하고 sayHello() 를 오버라이딩, "Hello" 출력

- MessageBeanKr.java : MessageBean 인터페이스를 상속하고 sayHello() 를 오버라이딩, "안녕하세요" 출력

- MessageBean 인터페이스를 MessageBeanKr, MessageBeanEn 클래스에서 상속하고 추상메소드 sayHello() 를 메소드 오버라이딩

- 부모 인터페이스를 만들어서 추상클래스를 갖도록 하고 나머지 구현 클래스들은 그 인터페이스를 상속받고 메소드를 오버라이딩

- 이런 경우엔 결합력이 낮아지지만 여전히 결합력이 높다

- 이런 방식도 Spring 에서 쓰지 않음, 좀 더 유연한 방식을 쓰려고 한다

 

Spring DI 예제 3 (Spring 에서 사용하는 방식, IoC 제어의 역행)

- src/main/java/sample03/ 안의 파일들

- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함

- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명

- MessagBean.java : 부모 인터페이스, 추상메소드 sayHello()

- MessageBeanEn.java : MessageBean 인터페이스를 상속하는 구현클래스, sayHello() 를 오버라이딩, "Hello" 출력

- MessageBeanKr.java : MessageBean 인터페이스를  상속하는 구현클래스, sayHello() 를 오버라이딩, "안녕하세요" 출력

 

- Ex01.java

package sample03;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class Ex01 {
	public static void main(String[] args) {
		 BeanFactory bf = new XmlBeanFactory(new FileSystemResource("beans01.xml"));
//		ApplicationContext bf = new FileSystemXmlApplicationContext("beans01.xml");
		// MessageBean mb = bf.getBean("mb", MessageBean.class);
		MessageBean mb = (MessageBean) bf.getBean("mb");
		// MessageBean mb = bf.getBean(MessageBean.class);
		// MessageBean mb = (MessageBean)bf.getBean("a");
		mb.sayHello("Spring");
	}
}

- 의존도를 낮추기 위해 직접 Java 클래스끼리 객체를 생성하지 않는다

- 대신 beans01.xml 파일에서 객체를 생성하고 있다

1. "bean01.xml" 파일을 읽어와서 BeanFactory 객체 bf 를 만듬 (혹은 ApplicationContext 객체)

2. bf.getBean("mb") 로 beans01.xml 에서 "mb" 라는 id를 가진 객체를 가져와서 mb 로 리턴받음 (+ 다운캐스팅 필수)

3. mb.sayHello("Spring") 으로 출력함

 

- xml 파일들 저장되는 위치 크게 3곳인데 현재 beans01.xml 은 프로젝트 하위에 저장되어있음

- beans01.xml : Spring 의 환경설정 파일이다,

- 이 파일 또한 자동으로 실행되지 않는 파일이므로 Ex01.java 의 메인메소드에서 불러와야 사용 가능한것,

- 그래서 Ex01 의 메인 메소드에서 이 beans01.xml 을 불렀다

- beans01.xml 이 현재 프로젝트 하위에 있다 -> Ex01.java 에서 그냥 이름만으로 읽어오면 됨

		 BeanFactory bf = new XmlBeanFactory(new FileSystemResource("beans01.xml"));

 

- beans01.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- MessageBeanKr mb = new MessageBeanKr() -->
	<bean id="mb" class="sample03.MessageBeanKr" name="a"></bean>
	
	<!--  Constructor DI -->
	<bean id="mb2" class="sample04.MessageBeanImpl">
		<constructor-arg value="도깨비">
			<!-- <value>박보검</value> -->
		</constructor-arg>
		<constructor-arg value="안뇽">
			<!-- <value>Hello</value> -->
		</constructor-arg>
	</bean>
	
</beans>

- Spring 환경설정 파일

- beans 가 루트 엘리먼트

- 안에 bean 객체를 생성하는 코드가 있다, 마치 JSP 의 useBean action tag 로 객체 생성하는 것 처럼 생성

- 필요할때마다 beans 안에 bean 을 추가한다

- 이 파일의 동작 시점 : 자동 실행되지 않고, 메인메소드에서 beans01.xml 을 읽어올때 실행됨

 

객체 생성 1 (beans01.xml 부분)

	<!-- MessageBeanKr mb = new MessageBeanKr() -->
	<bean id="mb" class="sample03.MessageBeanKr" name="a"></bean>

- bean 태그에서 객체 생성

- id 속성에 객체명 작성, 이 메인메소드에서 id 값으로 이 객체를 불러온다

- class 속성에 패키지부터 클래스까지의 경로를 설정 ex) sample03.MessageBeanKr

- 위의 주석은 bean 태그와 같은 코드를 Java 로 쓴 것이다

- 이 beans01.xml을 sample03 Ex01.java의 메인메소드에서 불러와서 여기서 생성한 객체를 받으므로 sample03 Ex01.xml의 메인메소드에서 객체를 쓸 수 있다

- 즉, 객체를 생성해서 받았으므로 Ex01.java 에서 sample03.MessageBeanKr 의 메소드 사용 가능

 

흐름 설명

- Ex01 클래스 메인 메소드에서 beans01.xml 파일을 불러온다

+ beans01.xml 은 자동으로 실행되는 파일이 아니므로 메인 메소드에서 읽어오는 것

- 이 파일 beans.xml 을 읽어서 MessageBeanKr 객체를 생성함, 즉 메모리상에 새로운 기억공간을 생성됨

- Ex01 클래스에서 getBean() 메소드로 bean 의 id 값으로 해당 객체를 가져와서 받음

- 그럼 Ex01 클래스 메인메소드에서 MessageBeanKr 객체 사용 가능, 메소드 sayHello() 실행 가능

 

+ 직접 New 연산자로 객체 생성 후 메소드로 호출하는게 아니라, 객체 생성을 Spring 의 환경설정 파일에서 한다

+ xml 파일에서 먼저 처리,즉 제어가 이전과는 반대이다, 제어의 역행(IoC)이라는 말을 씀

 

- 아직 DI 가 아니다, 이게 더 발전한게 DI 이다

- 클래스의 필드값은 생성자 또는 setter 메소드로 설정한다

- Injection : 생성자로 값을 할당하는 것을 Injection 이라고 함 

- 이게 DI와 연관이 있다


DI (Dependency Injection)

- 빈 간의 의존 관계를 컨테이너 에서 설정하고 관리 한다는 개념

- Constructor DI(Dependency Injection) : 빈 간의 의존 관계를 설정하기 위해 생성자를 이용

- Setter DI(Dependency Injection) : 빈 간의 의존 관계를 설정하기 위해 setter 메소드를 이용

 

DI 쉽게 설명

- 하나의 클래스에는 필드가 있다, 필드의 접근제어자가 Private 일때 값을 설정하는 방법은 2가지 뿐

1) 생성자의 매개변수로 필드값을 초기화

- 일반적으로 객체가 생성될때 생성자가 호출됨

- 생성자를 호출해서 매개변수로 전달되는 값을 해당 필드로 할당한다, 이 개념이 Spring 에서 DI 개념으로 확장됨

- 자바에서는 할당,  Spring 에서는 주입(Injection) 이라 함

- 생성자로 주입하는 것을 Construct DI 라고 함

2) setter 메소드

- setter 메소드의 매개변수로 값을 할당(주입) 하는 것을 Setter DI 라고 함

- Spring 환경설정 파일에서 빈 객체를 생성하면서 환경설정 파일에서 Construct DI, Setter DI 처리를 함

+ 객체가 생성될때 생성자도 호출되므로

 

Spring DI 예제 4 (IoC 를 확장시킨 DI 개념)

- src/main/java/sample04/ 안의 파일들

- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함

- 또한 Spring 의 환경설정 파일에서 필드값을 초기화함

- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명

- MessagBean.java : 부모 인터페이스, 추상메소드 sayHello()

- MessageBeanImpl.java : MessageBean 인터페이스를  상속하는 구현클래스, sayHello() 메소드를 오버라이딩함, 아래에서 코드 설명

 

- MessageBeanImpl.java

package sample04;

public class MessageBeanImpl implements MessageBean {
	private String name;
	private String greet;

	public MessageBeanImpl(String name, String greet) {
		this.name = name;		// 도깨비
		this.greet = greet;		// 안뇽
	}

	public void sayHello() {
		System.out.println(name + " ! " + greet);
	}
}

이 클래스 구성요소

1) 필드가 있다

- 접근제어자 private 이므로 객체 생성 후 필드 값 설정을 생성자의 매개변수로 할당(주입)해야함

- 이 예제는 Setter 대신 생성자로 매개변수 할당하는 예제

2) 생성자 : 객체 생성시 호출되며 매개변수로 필드값 초기화

3) 메소드 : 필드를 출력하는 역할

- 할당(주입) 하는걸 이제 Java 클래스에서 하지 않고, Spring 의 환경설정 파일에서 객체 생성과 주입까지 담당

 

- Ex01.java

package sample04;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new FileSystemXmlApplicationContext("beans01.xml");
		MessageBean mb = (MessageBean) ac.getBean("mb2");
		mb.sayHello();
	}
}

- Spring 의 환경설정 파일 beans01.xml 은 여기서 읽어줘야 실행됨

- id 가 "mb2" 인 객체를 불러서 MessageBean 객체 mb 로 받는다

 

- beans01.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- MessageBeanKr mb = new MessageBeanKr() -->
	<bean id="mb" class="sample03.MessageBeanKr" name="a"></bean>
	
	<!--  Constructor DI -->
	<bean id="mb2" class="sample04.MessageBeanImpl">
		<constructor-arg value="도깨비">
			<!-- <value>박보검</value> -->
		</constructor-arg>
		<constructor-arg value="안뇽">
			<!-- <value>Hello</value> -->
		</constructor-arg>
	</bean>
	
</beans>

- 두번째 bean 객체를 보자

 

Cunstructor DI 부분 (beans01.xml 부분)

	<!--  Constructor DI -->
	<bean id="mb2" class="sample04.MessageBeanImpl">
		<constructor-arg value="도깨비">
			<!-- <value>박보검</value> -->
		</constructor-arg>
		<constructor-arg value="안뇽">
			<!-- <value>Hello</value> -->
		</constructor-arg>
	</bean>

- sample04 의 MessageBeanImpl 클래스로 bean 객체 mb2 를 생성

- 객체 생성시 생성자가 호출된다, 생성자의 매개변수를 통해서 값을 주입하는 것임

- 생성자 매개변수의 첫번째 매개변수 자리 name 이란 필드값으로  value 의 "도깨비" 값이 할당(생성자 의존성 주입) 된다

- 생성자 매개변수 두번째에 매개변수 자리 greet 란 필드값으로 value 의 "안녕" 이란 값이 할당(생성자 의존성 주입) 된다

- 이 순서는 MessageBeanImpl.java 클래스에 작성된 생성자의 매개변수 순서이다, 그 생성자는 반드시 만들어져 있어야함 

이전의 Java vs Spring

- Java 에선 생성자의 매개변수로 값을 설정했지만 이젠 여기 Spring 의 환경설정 파일에선 <constructor-arg> 사용

- Java 에선 생성자에 매개변수를 2개 이상 전달가능했지만, 여기선 따로 따로 설정해야함, 순서대로 값이 전달된다

 

- 객체 생성 및 생성자 의존성 주입(할당) 시점 : Ex01 클래스의 메인메소드에서 beans01.xml 을 읽어왔을때

 

+ 지금은 어노테이션이 안되어있으므로 일일히 bean 객체 생성하고 생성자 의존 주입하고 있다

+ 나중에는 이렇게 bean 객체 생성하지 않고 어노테이션으로 바뀌면 코드가 간결해짐

 


Spring DI 예제 5 (예제 4와 비슷)

- src/main/java/sample05/ 안의 파일들

- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함

- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명

- Vehicle.java : 부모 인터페이스, 추상메소드 ride()

- AirPlain.java : Vhicle 인터페이스를  상속하는 구현클래스, ride() 메소드를 오버라이딩함, ride() 에서 매개변수로 전달된거 출력

- Car.java : Vhicle 인터페이스를  상속하는 구현클래스, ride() 메소드를 오버라이딩함, ride() 에서 매개변수로 전달된거 출력

 

- Vehicle.java

package sample05;

public interface Vehicle {
	void ride(String name);
}

 

- Car.java

package sample05;

public class Car implements Vehicle {
	public void ride(String name) {
		System.out.println(name + "(이)가 자동차를 탄다");
	}
}

 

- Ex01.java

package sample05;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new FileSystemXmlApplicationContext("beans02.xml");
		Vehicle vh = (Vehicle) ac.getBean("vh");
		vh.ride("철수");
	}
}

- beans02.xml 파일을 읽어오고 있다

- Spring 환경설정 파일이 어디에 있는지에 따라 읽어오는 방법이 다르다 * 아래에 설명

- getBean() 메소드로 id 가 "vh" 인 Car 클래스의 객체 읽어와서 Vehicle 객체 vh 로 받고 ride() 호출시 Car 의 오버라이딩 메소드 ride() 가 실행됨

+ Car 클래스로 다운캐스팅해서 Car 클래스 객체로 받아도 되지만 결합도 낮추기 위해 인터페이스 객체로 받았다

Spring 환경설정 파일 위치에 따른 읽어오는 법

- 지금은 bean02.xml 이 현재 프로젝트 바로 하위에 있으므로 이름만으로 읽어와도 된다

- 같은 패키지안에 저장시에는 패키지명을 앞에 붙임

- resorces 디렉토리 안에 저장시에는 앞에 classpath: 를 붙여서 읽어옴

- Web Application 에서는 주로 resources 디렉토리 안에 저장되는 경우가 많음, 지금 ch01 프로젝트는 그냥 Application 임

 

- beans02.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="vh" class="sample05.Car"/>
	<!-- <bean id="vh" class="sample05.Airplane"/> -->
	
	<!-- Constructor DI -->
	<bean id="vh2" class="sample06.VehicleImpl">
		<constructor-arg value="철수"></constructor-arg>
		<constructor-arg value="자전거"></constructor-arg>
	</bean>
</beans>

 

- 첫번째 bean 객체는 sample05 의 Car 클래스로 객체 vh 를 생성

- 생성된 bean 은 싱글톤으로 만들어진다, 싱글톤으로 만들어졌이므로 공유가 됨, 다른 bean 안에서 이 객체 vh 불러서 쓸 수 있다


Spring DI 예제 6 (예제 4,5와 비슷)

- src/main/java/sample06/ 안의 파일들

- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함

- 또한 Spring 의 환경설정 파일에서 필드값을 초기화함

- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명

- Vehicle.java : 부모 인터페이스, 추상메소드 ride()

- VehicleImpl.java : Vhicle 인터페이스를 상속하는 구현클래스, ride() 메소드를 오버라이딩함, ride() 에서 매개변수로 전달된거 출력,

 

- VehicleImple.java

package sample06;

public class VehicleImpl implements Vehicle {
	private String name;
	private String rider;

	public VehicleImpl(String name, String rider) {
		this.name = name;		// 철수
		this.rider = rider;		// 자전거
	}

	public void ride() {
		System.out.println(name + "(이)가 " + rider + "(을)를 탄다");
	}
}

- VehicleImple.java 엔 필드, 생성자, ride() 메소드가 있다

- 여기에 생성자를 생성해둬야 Spring 환경설정 파일에서 할당(DI) 가능

 

- Ex01.java

package sample06;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new FileSystemXmlApplicationContext("beans02.xml");
		Vehicle vh = (Vehicle) ac.getBean("vh2");
		vh.ride();
	}
}

- 여기서 FileSystemXmlApplicationContext() 로 beans02.xml 을 읽어올때 객체가 생성되고 생성자가 호출됨

- getBean() 메소드로 id 가 "vh2" 인 Car 클래스의 객체 읽어와서 Vehicle 객체 vh 로 받고 ride() 호출시 VehicleImpl 의 오버라이딩 메소드 ride() 가 실행됨

- 즉 DI 개념은 Java 클래스인 Ex01.java에서 생성자의 매개변수를 통해 값을 주는게 아니라 값도 beans02.xml 에서 할당(의존성 주입)하는 것임

- Ex01.java 실행시

 

- beans02.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="vh" class="sample05.Car"/>
	<!-- <bean id="vh" class="sample05.Airplane"/> -->
	
	<!-- Constructor DI -->
	<bean id="vh2" class="sample06.VehicleImpl">
		<constructor-arg value="철수"></constructor-arg>
		<constructor-arg value="자전거"></constructor-arg>
	</bean>
</beans>

- 아래쪽 코드이다, 두번째 bean 객체

- simple06 의 Car 클래스로 bean 객체 "vh2" 를 만들고 있다

- 객체 vh2 생성시 생성자가 호출되는데, 생성자의 매개변수 순서대로 name 에 "철수", rider 에 "자전거" 값을 할당(주입)

- value 속성값이 주입되는 값이다

- 객체 생성 및 생성자 의존성 주입(할당) 시점 : Ex01 클래스의 메인메소드에서 beans02.xml 을 읽어왔을때

+ 어노테이션 처리시 일일히 bean 객체 생성하지 않음

 


Spring DI 예제 7 (예제 6와 비슷, Setter DI 사용)

- src/main/java/sample07/ 안의 파일들

- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함

- 또한 Spring 의 환경설정 파일에서 필드값을 초기화함

- 이번에는 생성자가 아니라 setter 메소드로 값을 할당할 것

- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명

- messageBean.java : 부모 인터페이스, 추상메소드 sayHello()

- MessageBeanImpl.java : messageBean인터페이스를 상속하는 구현클래스, sayHello() 메소드를 오버라이딩함, ride() 에서 매개변수로 전달된거 출력,

 

- MessageBeanImpl.java

package sample07;

public class MesageBeanImpl implements MessageBean {
	private String name;		// property
	private String greet;

	public void setName(String name) {
		this.name = name;		// 길동
	}

	public void setGreet(String greet) {
		this.greet = greet;		// 안녕
	}

	public void sayHello() {
		System.out.println(name + " !! " + greet);
	}
}

- 이번에는 MessageBeanImpl 클래스에 필드, setter 메소드, 일반 메소드 sayHello() 가 있다

- Setter 메소드의 매개변수로 값을 할당할 것

- 여기 Setter 메소드가 만들어져 있어야 Spring 환경설정 파일에서 Setter DI 가능

 

- Ex01.java

package sample07;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac =
				 new ClassPathXmlApplicationContext("/sample07/beans07.xml");
//				 new GenericXmlApplicationContext("/sample07/beans07.xml");
		MessageBean mb = (MessageBean) ac.getBean("mb");
		mb.sayHello();
	}
}

- 객체 생성 및 생성자 의존성 주입(할당) 시점 : Ex01 클래스의 메인메소드에서 beans07.xml 을 읽어올때 메모리에 공간 생성

- Spring 환경설정 파일이 같은 sample07 패키지에 들어있으므로 beans07.xml 앞에 패키지명을 붙여줘야 읽어올 수 있다!

ex) "/sample07/beans07.xml"

- mb 객체가 구해졌으므로 메소드 오버라이딩 되어있는 MessageBeanImpl 의 sayHello() 실행 가능

 

- beans07.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Setter DI -->
	<bean id="mb" class="sample07.MesageBeanImpl">
		<property name="name">
			<value>길동</value>
		</property>
		<property name="greet" value="안녕"/>
	</bean>
	
</beans>

- sample07 의 MessageBeanImpl 클래스로 객체 mb 를 만든다

- setter 메소드(MessageBeanImplsetName(), setGreet() 를 호출해서)로 객체의 필드값을 설정할 것

- property 태그 name 속성으로 "값이 주입될 프로퍼티명(필드명)"을 쓴다

- property 태그 value 속성값으로 또는 property 태그 안의 value 태그 안에 "주입할 값"을 쓴다

ex) name 속성에 "name" 을 쓰면 필드 name 에 value 로 설정된 "안녕" 이 할당됨

ex) name 속성에 "greet" 을 쓰면 필드 greet 에 value 로 설정된 "안녕" 이 할당됨

- Setter DI = Setter 의존성 주입 = Setter 메소드의 매개변수로 주입하는 것

+ 필드 = 프로퍼티

 

Setter DI 사용하는 곳

+ 90% 이상이 Setter DI 사용, 대부분의 환경설정 파일에서 Setter DI 를 한다

- MyBatis 환경설정파일에서 property 태그를 썼다 * 아래 사진

- servlet-context.xml 에서도 property 태그 사용함 * 아래 사진

 

정리

- Constructor DI : Constructor 로 프로퍼티를 할당하는 것 , constructor-arg 사용

- Setter DI : Setter 메소드의 매개변수로 프로퍼티를 할당, property 사용

- 둘 다 Spring 환경설정 파일에서 하는 작업이다

- 이 작업은 나중에 어노테이션 기반으로 바뀌므로 사용방법이 조금 달라지게 됨


Spring DI 예제 8 (Spring 환경설정 파일이 resources 폴더 안에 있음, Setter DI 와 Constructor DI 둘 다 사용)

- src/main/java/sample08/ 안의 파일들

- 이번에는 beans08.xml 파일이 resorces 폴더 안에 있다

- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함

- 또한 Spring 의 환경설정 파일에서 필드값을 초기화함, setter DI 와 Constructor DI 둘 다 사용

- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명

- messageBean.java : 부모 인터페이스, 추상메소드 sayHello()

- MessageBeanImpl.java : messageBean인터페이스를 상속하는 구현클래스, sayHello() 메소드를 오버라이딩함, sayHello() 에서 매개변수로 전달된거 출력,

 

- MessageBeanImpl.java

package sample08;

public class MessageBeanImpl implements MessageBean {
	private String name;
	private String greet;

	public MessageBeanImpl(String name) {
		this.name = name;	// 돌쇠
	}

	public void setGreet(String greet) {
		this.greet = greet;	// 안뇽
	}

	public void syaHello() {
		System.out.println(name + " ! " + greet);
	}
}

- 생성자도 있고 setter 메소드도 있다

- 즉 Contstructor DI 와 Setter DI 를 혼용해서 사용할 것

- 필드 name 은 생성자로 값 설정하고, 필드 greate 는 setter 메소드로 값 설정할 것

 

- Ex01.java

package sample08;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:beans08.xml");
		MessageBean mb = (MessageBean) ac.getBean("mb");
		mb.syaHello();
	}
}

-  bean08.xml 파일을 읽어서 객체 생성 및 DI 후 그 객체를 id "mb" 로 받아서 오버라이딩 된 메소드 호출

- 이번에는 beans08.xml 파일이 resorces 폴더 안에 있다

- 이때 파일명 앞에 classpath: 를 붙여줘야 읽어 올 수 있다

+ Web Application 에서는 Spring 환경설정 파일이 주로 resources 디렉토리 안에 저장되어있음

 

Spring 환경설정 파일 위치에 따른 읽어오는 법 (중복)

1. 현재 프로젝트 바로 하위에 있다면 이름만으로 읽어와도 된다

2. 같은 패키지안에 저장되어 있다면 패키지명을 앞에 붙임

3. resorces 디렉토리 안에 저장시에는 앞에 classpath: 를 붙여서 읽어옴

- Web Application 에서는 주로 resources 디렉토리 안에 저장되는 경우가 많음, 지금 ch01 프로젝트는 그냥 Application 임

 

- beans08.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="mb" class="sample08.MessageBeanImpl">
		<constructor-arg value="돌쇠"></constructor-arg>
		<property name="greet" value="안뇽"></property>
	</bean>
	
</beans>

- 프로퍼티 name 은 생성자로 값 "돌쇠" 설정, MessageBeanImpl 클래스의 생성자의 매개변수는 하나이므로 이 constructor-arg 가 그 하나의 매개변수로 들어감

- 프로퍼티 greet 은 setter 메소드로 값 "안뇽" 설정, 프로퍼티 greet의 setter 메소드인 setGreet() 가 MessageBeanImpl 클래스에 있으므로 여기서 property 태그로 name 속성에 필드값 "greet" 를 써서 값 설정

 


Spring DI 예제 9 (어려움)

- src/main/java/sample09/ 안의 파일들

- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함

- 또한 Spring 의 환경설정 파일에서 필드값을 초기화함

- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명

- messageBean.java : 부모 인터페이스, 추상메소드 sayHello()

- MessageBeanImpl.java : messageBean 인터페이스를 상속하는 구현 클래스, sayHello() 메소드를 오버라이딩함, sayHello() 에서 매개변수로 전달된거 출력

- Outputer.java : 인터페이스, 추상메소드 output()

- FileOutputer.java : Outputer 인터페이스를 상속하는 구현 클래스, output() 메소드를 오버라이딩함

- 이 구현클래스 2개로 bean 객체 따로 만듬

 

- MessageBeanImpl.java

package sample09;

public class MessageBeanImpl implements MessageBean {
	private String name;
	private String greet;
	private Outputer output;

	public void setName(String name) {
		this.name = name;					// name="홍길동"
	}

	public void setGreet(String greet) {
		this.greet = greet;					// greet="Hello !"
	}
	// Outputer output = new FileOutputer()
	public void setOutput(Outputer output) {
		this.output = output;				// output=out
	}

	public void sayHello() {
		String msg = name + "님 " + greet;
		System.out.println(msg);
		output.output(msg);
	}
}

- 필드, setter 메소드, 메소드 로 구성되어있다.

- 프로퍼티에 값을 할당하는 방법 중 Setter 메소드를 사용할 것

- 인터페이스 Outputer 객체 output 의 setter 메소드 setOutput() , 매개변수는 인터페이스 Outputer 형

- setOut() 의 매개변수는 부모 인터페이스인 Output 형이고, 여기 매개변수에 주입되는 값은 bean09.xml 에서 만들어진 자식인 FileOutputer 객체가 주입됨

+ 인터페이스로는 객체 생성을 못하므로 자식 구현 클래스로 객체를 생성해서 업캐스팅 해야함

- 그럼 프로퍼티 out 은 FileOutpter 객체를 받은 것임

- MessageBean 의 sayHello() 메소드를 여기서 오버라이딩

- sayHello() 안에서 초기화된 필드값을 출력하고, FileOutputer 객체 output 으로 FileOupter 클래스에서 오버라이딩된 메소드 output() 호출하자

 

* 잘 이해가 안되면 아래를 읽고 다시 여기 돌아와서 읽어보기

 

- FileOutputer.java

package sample09;

import java.io.FileWriter;
import java.io.IOException;


public class FileOutputer implements Outputer {
	private String fileName;

	public void setFileName(String fileName) {
		this.fileName = fileName;	// fileName="test.txt"
	}

	public void output(String msg) {
		try {
			FileWriter fw = new FileWriter(fileName);
			fw.write(msg);
			fw.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}
}

- 상속받은 인터페이스 Outputer 의 추상메소드 output() 을 오버라이딩 해야한다

- FileOutputer 클래스에서는 필드가 있고, setFileName() 이라는 setter 메소드가 있고 output() 을 오버라이딩하고 있다

- output() 에서 File 객체를 만들어서 파일을 생성하고 있다

 

- Ex01.java 실행시 "홍길동님 Hello !" 출력 후 test.txt 파일이 만들어진다

- 그 파일안에 "홍길동님 Hello !" 가 들어있음

 

- beans09.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="mb" class="sample09.MessageBeanImpl">
		<property name="name" value="홍길동"></property>
		<property name="greet" value="Hello !"></property>
		<property name="output" ref="out"></property>
	</bean>
	<bean id="out" class="sample09.FileOutputer">
		<property name="fileName" value="test.txt"></property>
	</bean>
	
</beans>

<첫번째 객체>

- 첫번째 bean 객체는 MessageBeanImpl 로 만들어진 객체 mb

+ MessageBeanImple 에는 필드 name, greet, output 이 있다, 이때 output 의 자료형은 Output

- 프로퍼티 name, greet, output 에 값 "홍길동", "Hello !", 그리고 FileOutputer 객체 out 을 Setter 메소드로 주입하고 있다

- 아래에서 자식 구현클래스로 만든 FileOutputer 의 객체 out 을 setOutput() 의 매개변수에 주입하는 것이다

- 다른 bean 의 id 값을 참조할 떄는 value 대신 ref 를 사용한다

+ ref 는 다른 bean 의 id 값만 들어감

 

<두번째 객체> : 여기 설명을 첫번째 객체 설명보다 먼저 보기

- 두번째 bean 객체는 FileOutputer 로 만들어진 객체 out

- FileOutputer 클래스로 만든 객체 out 의 프로퍼티 fileName 의 값을 "test.txt" 로 주입하는데 이때 Setter DI 사용하고 있다

 

- beans09.xml 을 메인메소드에서 불러올떄 beans09.xml 의 모든 객체가 생성되고 주입된다 (순서 상관X)

- 그러므로 첫번째 객체를 생성할때 두번째 객체 out 을 참조(불러올수)할 수 있는 것이다!

 

 

 

실행되는 순서대로 따라가보자

- Ex01.java (메인메소드)

package sample09;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Ex01 {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("/sample09/beans09.xml");
		MessageBean mb = (MessageBean) ac.getBean("mb");
		mb.sayHello();
	}
}

- sample09 패키지의 beans09.xml 을 읽어와서 ApplicationContext 객체 ac를 구함

- ac.getBean("mb") 로 beans09.xml 에서 MessageBeanImpl 클래스로 생성된 mb 객체를 구해옴

- 그럼 MessageBeanImpl 클래스의 오버라이딩 된 sayHello() 메소드 실행 가능

 

- MesageBeanImpl.java

package sample09;

public class MessageBeanImpl implements MessageBean {
	private String name;
	private String greet;
	private Outputer output;

	public void setName(String name) {
		this.name = name;					// name="홍길동"
	}

	public void setGreet(String greet) {
		this.greet = greet;					// greet="Hello !"
	}
	// Outputer output = new FileOutputer()
	public void setOutput(Outputer output) {
		this.output = output;				// output=out
	}

	public void sayHello() {
		String msg = name + "님 " + greet;
		System.out.println(msg);
		output.output(msg);
	}
}

- 객체 생성시 호출되었으므로 필드값도 초기화해야함, 초기화도 beans09.xml 에서 Setter 메소드로 해준다

- 그럼 필드가 모두 초기값을 갖게 된다

- Ex01.java 의 메인메소드에서 sayHello() 를 호출하므로 거기서 필드 name ,greet 값을 출력하고 out 객체로 output() 메소드를 실행한다

 

- 객체 out 은 Output 인터페이스를 상속받은 FileOutputer 객체를 받은 것이므로 FileOutputer 에서 오버라이딩 된 output() 메소드를 호출하는 것임

- output 메소드를 호출하며 메세지를 매개변수로 가져감

 

- FileOutputer.java

package sample09;

import java.io.FileWriter;
import java.io.IOException;


public class FileOutputer implements Outputer {
	private String fileName;

	public void setFileName(String fileName) {
		this.fileName = fileName;	// fileName="test.txt"
	}

	public void output(String msg) {
		try {
			FileWriter fw = new FileWriter(fileName);
			fw.write(msg);
			fw.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}
}

- FileWriter 클래스는 파일을 생성할때 사용하는 클래스

- 생성자 안에는 생성할 파일명을 넣어야함, 필드 fileName 은 Setter 에 의해 이미 주입된 상태이므로 fileName 을 넣으면 된다,  (fileName 은 beans09.xml 에서 "test.txt" 로 주입되었음)

- output() 의 매개변수로 전달된 메세지를 사용해서 파일을 만들고 메세지를 그 파일에 작성

- test.txt 파일이 생성되었고 test.txt 를 열어보면 메세지가 적혀있다


나중에 할 일

- 지금 하고 있는 ch01 프로젝트는 일반 Application 프로젝트이다, 그래서 webapp 폴더가 없음

- 나중에 Web Application 을 만들 땐 모든 Spring 환경설정 파일의 객체들을 한번에 root-context.xml 에서 생성함

- root-context.xml 에서 DB 관련된 내용이 들어가는데 이때 여기서 bean 들을 만들고 주입 (DI)

ex) SqlSession 객체를 구해올때도 root-context.xml 에서 bean 객체를 만들고 Setter DI 사용

- 그래서 나중엔 직접 bean 객체 생성하는 일은 하지 않음, 어노테이션으로 처리한다

- 이 root-context.xml 은 web.xml 에서 불러줘서 실행된다

- web.xml 은 서버 구동시 자동으로 실행됨

 

- 나중에 Web Application Spring 환경설정 파일은 resources 폴더안에 주로 지정된다

- beans09.xml 같은 객체를 만드는 Spring 환경설정 파일이 주로 resources 폴더 안에 들어감

- MyBatis 환경설정 파일, Mapper 파일도 resources 폴더에 저장됨

- 그래서 두 파일을 불러올 때 classpath: 를 앞에 붙여서 불러와야한다

 

- servlet-context.xml 에서는 DI 개념을 어노테이션으로 바꿔서 처리할 것 * 맛보기

<context:component-scan base-package="sample15"/>

+ Recent posts