앞으로 학습할 것
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 다운로드
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 메소드(MessageBeanImpl 의 setName(), 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"/>