- 스프링 프레임워크 시작하기
1.1. 개발환경 구축
1.1.1. JDK 설치 및 환경 변수 설정
- Java SE Development Kit 8
- JAVA_HOME 환경 변수 설정
- Path 설정
1.1.2. 이클립스(Eclipse) 설치
- 최신 Java EE 버전
1.1.3. 톰캣(Tomcat) 서버 설치 및 이클립스 연동
- 톰캣(Tomcat) 8.5 또는 9
- 이클립스 Encoding UTF-8로 변경
- 서버 등록 및 확인
1.1.4. 데이터베이스(DBMS) 서버 설치
- H2DataBase http://h2database.com/html/main.html bin/h2.bat
- Oracle, MS-SQL, PostgreSQL
- MySql, MariaDB
USERS.sql
CREATE TABLE `users` (
`ID` varchar2(8) PRIMARY KEY,
`PASSWORD` varchar2(8),
`NAME` varchar2(20),
`ROLE` varchar2(5)
);
CREATE TABLE `users` (
`ID` varchar(8) NOT NULL,
`PASSWORD` varchar(8) NOT NULL,
`NAME` varchar(20) NOT NULL,
`ROLE` varchar(5) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO USERS VALUES('admin','1111', 'admin','Admin');
INSERT INTO USERS VALUES('hong','1234','홍길동','User');
SELECT * FROM USERS ORDER BY ID;
BOARD.sql
CREATE TABLE BOARD(
SEQ NUMBER(5) PRIMARY KEY,
TITLE VARCHAR2(200),
WRITER VARCHAR2(20),
CONTENT VARCHAR2(2000),
REGDATE DATE DEFAULT SYSDATE,
CNT NUMBER(5) DEFAULT 0
);
CREATE TABLE BOARD(
SEQ int PRIMARY KEY,
TITLE VARCHAR(200),
WRITER VARCHAR(20),
CONTENT VARCHAR(2000),
REGDATE datetime DEFAULT current_timestamp,
CNT int DEFAULT 0
);
INSERT INTO BOARD(SEQ, TITLE, WRITER, CONTENT) VALUES(1,'title1','writer1','content1');
INSERT INTO BOARD(SEQ, TITLE, WRITER, CONTENT) VALUES(2,'title2','writer2','content2');
SELECT * FROM BOARD ORDER BY SEQ;
1.1.5 STS(Spring Tool Suite)
- Eclipse 플러그인 설치 : Help > Eclipse Marketplace > Spring Tool Suite 검색 > Spring Tools 3 Add-On 3.9.6.RELEASE install
- https://spring.io/ Spring Tool Suite™ 3 Download
- Perspective Spring으로 변경
1.2. 프로젝트 생성
1.2.1 프로젝트 생성
- File > New > Spring Legacy Project
- Project Name : spring-basic
- Templates : Spring MVC Project
- 패키지 : com.spring.basic
1.2.2 프로젝트 설정 변경
- BoardWeb > Properties > Project Facets > Java 1.8 선택
- src > main > resource > log4j.xml 외 기본 설치 파일 삭제
- src > main > webapp > WEB-INF > spring, views 폴더 삭제
- src > main > webapp > WEB-INF > web.xml >
내용 삭제 - pom.xml Spring 4.3.25 로 변경(2021-01 현 정부프레임워크 버전)
<org.springframework-version>4.3.25.RELEASE</org.springframework-version>
메이븐레포지토리 최신 버전
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.30.RELEASE</version>
</dependency>
- Java Sresources > Libraries > Maven Dependencies 확인
- https://www.egovframe.go.kr/EgovRunEnvReleaseNote.jsp?menu=3&submenu=1&leftsub=1
- 스프링 프레임워크 개요
2.1 프레임워크
2.1.1 개념
- 사전적 의미 : 뼈대 또는 틀
- 소프트웨어 관점 : 아키텍처에 해당 하는 골격 코드
- 전체 애플리케이션의 구조를 결정하는 골력 코드를 프레임워크가 제공
2.1.2 장점
- 애플리케이션에 대한 분석, 설계, 구현에서 재사용성 증가
- 비즈니스 로직만 구현 빠른 구현 시간
- 유지보수에 인력과 시간을 줄일 수 있음
- 개발자들의 역량 획일화
- 검증된 아키텍처의 재사용과 일관성 유지
2.1.3 자바 기반의 프레임워크
Presentation 영역
- Struts, Spring(MVC)
Business 영역
- Spring(IoC, AOP)
Persistence 영역
- Hibernate : 완벽한 ORM(Object Relation Mapping)
- JPA : ORM의 공통 인터페이스를 제공하는 자바 표준 API
- Ibatis or Mybatis : SQL 명령어와 VO(DTO) 를 매핑해 주는 기능 제공
2.2. 스프링 프레임워크
2.2.1 탄생 배경
- 엔터프라이즈 개발 용 EJB(Enterprise Java Beans)의 여러가지 문제점
- 스펙이 복잡해서 학습 커브가 높고 개발 및 유지보수가 복잡
- POJO(Plain Old Java Object0를 사용하면서도 EJB 기능 지원
- EJB 보다 매우 간단
2.2.2 특징
경량
- 몇 개의 jar 파일로 개발 가능
제어의 역행(Inversion of Control, IoC)
- 객체 간의 느슨한 결합 유지
- 객체와 객체간의 의존관계를 코드가 아닌 컨테이너가 처리
관점지향 프로그래밍(Aspect Oriented Programming, AOP)
- 핵심 비지니스 로직과 공통 로직을 분리
- 공통으로 사용하는 기능들을 외부의 독립된 클래스로 분리
- 해당 기능을 프로그램 코드에 직접 명시하지 않고 선언적으로 처리
- 유지보수를 향상 시킴
컨테이너(Container)
- 특정 객체의 생성과 관리를 담당
- 객체 운영에 필요한 다양한 기능 제공
- 서블릿 컨테이너 : 서블릿 객체를 생성하고 관리, 톰캣 서버
- EJB 컨테이너 : EJB 객체를 생성하고 관리
- 스프링 : 애플리케이션 운용에 필요한 객체 생성 및 의존관계 관리
2.3 IoC(Inversion Of Control) 컨테이너
- 컨테이너가 객체 생성 및 의존관계 처리로 결합도 낮은 컴포넌트 구현 가능
- 서블릿 컨테이너 동작 순서
1) WEB-INF/web.xml 파일을 로딩하여 구동
2) 브라우저로부터 /hello.do
3) hello.HelloServlet 클래스를 찾아 객체를 생성하고 doGet() 메서드 호출
4) doGet() 메서드 실행 결과를 클라이언트 브라우저로 전송
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet>
<description></description>
<display-name>hello</display-name>
<servlet-name>hello</servlet-name>
<servlet-class>com.springbook.biz.HelloService</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
</web-app>
- HelloService.java
package com.springbook.biz;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloService extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
}
}
- 실행(브라우저 요청)
http://localhost:8080/biz/hello.do
2.3.1 결합도(Coupling) 높은 프로그램
- 결합도 : 하나의 클래스가 다른 클래스와 얼마나 많이 연결되었는 지 나타냄
- 결합도가 높으면 유지보수가 어려움
SamsungTV.java
package polymorphism;
public class SamsungTV {
public void powerOn() {
System.out.println("SS - Power On");
}
public void powerOff() {
System.out.println("SS - Power Off");
}
public void volumeUp() {
System.out.println("SS - Volume Up");
}
public void volumeDown() {
System.out.println("SS - Volume Down");
}
}
LgTV.java
package polymorphism;
public class LgTV {
public void turnOn() {
System.out.println("LG - Turn On");
}
public void turnOff() {
System.out.println("LG - Turn Off");
}
public void soundUp() {
System.out.println("LG - Sound Up");
}
public void soundDown() {
System.out.println("LG - Sound Down");
}
}
TVUser.java
package polymorphism;
public class TVUser {
public static void main(String[] args) {
SamsungTV tv = new SamsungTV();
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
}
}
- LgTV 를 시청하려면 대부분 수정해야 함
- 같은 메소드 사용을 강제할 수단이 없음
- TVUser 같은 클라이언트가 늘어나면 유지보수가 힘들어 짐
TVUser.java
package polymorphism;
public class TVUser {
public static void main(String[] args) {
LgTV tv = new LgTV();
tv.turnOn();
tv.turnOff();
tv.soundDown();
tv.soundUp();
}
}
2.3.2 다형성 이용하기
- 결합도를 낮추는 가장 쉬운 방법
- 상속, Overriding, 형변환 필요
- interface 사용으로 메소드 구현 강제
TV.java
package polymorphism;
public interface TV {
public void powerOn();
public void powerOff();
public void volumeUp();
public void volumeDown();
}
SamsungTV2.java
package polymorphism;
public class SamsungTV2 implements TV{
public void powerOn() {
System.out.println("SS - Power On");
}
public void powerOff() {
System.out.println("SS - Power Off");
}
public void volumeUp() {
System.out.println("SS - Volume Up");
}
public void volumeDown() {
System.out.println("SS - Volume Down");
}
}
LgTV2.java
package polymorphism;
public class LgTV2 implements TV{
public void powerOn() {
System.out.println("LG - Power On");
}
public void powerOff() {
System.out.println("LG - Power Off");
}
public void volumeUp() {
System.out.println("LG - Volume Up");
}
public void volumeDown() {
System.out.println("LG - Volume Down");
}
}
TVUser2.java
package polymorphism;
public class TVUser2 {
public static void main(String[] args) {
TV tv = new SamsungTV2(); // LgTV2() 만 변경(소수 수정 필요)
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
}
}
2.3.3 디자인 패턴 이용하기
- TV 교체 시 TVUser2.java 를 수정하지 않기 위해서 Factory 패턴 적용
- Factory 패턴은 TV와 TVUser 사이를 느슨하게 결합 시킴(캡슐화 사용)
BeanFactory.java
package polymorphism;
public class BeanFactory {
public Object getBean(String beanName) {
if(beanName.equals("samsung")) {
return new SamsungTV2();
}
else if(beanName.equals("lg")){
return new LgTV2();
}
return null;
}
}
TVUser3.java
package polymorphism;
public class TVUser3 {
public static void main(String[] args) {
BeanFactory factory = new BeanFactory();
TV tv = (TV)factory.getBean(args[0]); // 소스 수정 없음
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
}
}
- 명령행 매개변수만 변경 하면 됨
- TVUser가 객체를 요청하면 BeanFactory는 적절한 객체를 생성해서 리턴
스프링 컨테이너 및 설정 파일
대부분 IoC 컨테이너는 컨터이너에서 관리할 객체들을 위한 별도 설정 파일이 있음
- Servlet 컨테이너 : web.xml
3.1 스프링 IoC
3.1.1 스프링 설정 파일 생성
- src/main/resource > New > Other > Spring Bean Configuration File > applicationContext.xml
- SamsungTV 설정 파일에 등록
<?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="tv" class="polymorphism.SamsungTV2" />
</beans>
3.1.2 스프링 컨테이너 구동 및 테스트
- applicationContext.xml 로딩 메세지
- GenericXmlApplicationContext 구동 메세지
TVUser4.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser4 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나인 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
}
}
INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [applicationContext.xml]
INFO : org.springframework.context.support.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@6c9f5c0d: startup date [Tue Jan 26 14:06:30 KST 2021]; root of context hierarchy
SamsungTV 객체 생성 시점 확인을 위한 SamsungTV.java 기본 생성자 추가
public SamsungTV() { System.out.println("SamsungTV() 생성"); }
TV 객체 요청
TVUser4.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser4 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청(Lookup)
TV tv = (TV)factory.getBean("tv"); // <bean id="tv" ...>
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
// 3. Spring 컨테이너 종료
factory.close();
}
}
- TV를 Lg TV로 변경하려면 xml 파일만 변경 하면 됨
3.1.3 스프링 컨테이너 종류
- BeanFactory 와 이를 상속한 ApplicationContext 컨테이너
BeanFactory 컨테이너
- 스프링 설정 파일에 등록된
객체를 생성하고 관리하는 가장 기본적인 기능 제공
ApplicationContext 컨테이너
객체 관리 기능외에 트랜잭션 관리등 다양한 기능 제공 - GenericXmlApplicationContext : 파일시스템이나 클래스 경로에 있는 XML 설정 파일 로딩 및 구동
- XmlWebApplicationContext : 웹기반 스프링 애플리케이션 개발시 사용(Spring MVC 시 사용)
3.2 스프링 XML 설정
3.2.1
- 스프링 컨테이너는
<bean>
의 생명 주기 관리 및 여러가지 서비스 제공 - 설정파일을 이름은 상관없으나
<beans>
루트 엘리먼트 사용 해야 함 <bean>, <description>, <alias>, <import>
자식 엘리먼트 사용
3.2.2
- 기능별 XML 파일로 나누어 설정시 하나로 통합 할 때 사용
context-datasource.xml
<beans>
DataSource 관련 설정
</beans>
context-transaction.xml
<beans>
Transaction 관련 설정
</beans>
applicationContext.xml
<beans>
<import resource="context-datasource.xml" />
<import resource="context-transaction.xml" />
</beans>
3.2.3
- 클래스 등록
- id 속성 값은 유일한 값이어야 함
- id 속성 값은 숫자로 시작 하거나 공백, 특수 문자 포함 되면 안됨
- 특수문자 사용시는 name 속성 값 사용
3.2.4
- Servlet 클래스 객체 생성 시 디폴트 생성자만 호출(멤버변수 초기화 불가)
(1) init-method 속성
- 객체 생성 후 멤버변수 초기화 작업시 사용
SamsungTV2.java
public void initMethod() {
System.out.println("initMethod() 호출");
}
applicationContext.xml
<bean name="tv" class="polymorphism.SamsungTV2" init-method="initMethod" />
(2) destroy-method 속성
- 스프링 컨테이너가 클래스 객체 삭제 직전 호출
SamsungTV2.java
public void destroyMethod() {
System.out.println("destroyMethod() 호출");
}
applicationContext.xml
<bean name="tv" class="polymorphism.SamsungTV2" init-method="initMethod" destroy-method="destroyMethod" />
(3) lazy-init 속성
- ApplicationContext 컨테이너는 구동시
들을 즉시 로딩(pre-loading) - 메모리 관리를 위해 클라이언트 요청 시점에 생성 시 사용
<bean name="tv" class="polymorphism.SamsungTV2" init-method="initMethod" destroy-method="destroyMethod" lazy-init="true" />
(4) scope 속성
- 객체 생성시 싱글톤(singleton pattern)이 디폴트
- 매번 객체 생성을 위해서는 prototype 속성값 사용
- 싱글톤 : scope="singleton"
<bean name="tv" class="polymorphism.SamsungTV2" init-method="initMethod" destroy-method="destroyMethod" lazy-init="true" scope="prototype" />
TVUser4.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser4 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청(Lookup)
TV tv = (TV)factory.getBean("tv");
TV tv1 = (TV)factory.getBean("tv");
TV tv2 = (TV)factory.getBean("tv");
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
// 3. Spring 컨테이너 종료
factory.close();
}
}
- 의존성 주입(Dependency Injection)
4.1 의존성 관리
4.1.1 스프링의 의존성 관리 방법
- 컨테이너가 객체의 생성 및 의존관계를 자동으로 관리
- Dependency Lookup
- Dependency Injection > Setter Injection
- Dependency Injection > Constructor Injection
Dependency Lookup
- 컨테이너가 객체를 생성하고 클라이언트는 생성한 객체 검색해서 사용하는 방식
- 실 개발에는 대대분 Dependency Injection 사용
Dependency Injection
- 객체사이의 의존관계를 스프링 설정 파일에 등록된 정보를 바탕으로 컨테이너가 자동 처리
- 의존관계 변경시 설정파일만 변경으로 유지보수 향상
4.1.2 의존성 관계
의존성 관계
- 객체와 객체의 결합 관계
- 하나의 객체에서 다른 객체의 변수나 메소드 사용시 다른 객체의 생성 및 참조 레퍼런스 필요
SamsungTV에서 사용할 SonySpeaker 클래스 추가
package polymorphism;
public class SonySpeaker {
public SonySpeaker() {
System.out.println("SonySpeaker() 생성");
}
public void volumeUp() {
System.out.println("SonySpeaker volumeUp() ");
}
public void volumeDown() {
System.out.println("SonySpeaker volumeDown() ");
}
}
SamsungTV 볼륨기능 클래스 추가
package polymorphism;
public class SamsungTV3 implements TV{
private SonySpeaker speaker = null;
public SamsungTV3() {
System.out.println("SamsungTV() 생성");
}
public void powerOn() {
System.out.println("Power On");
}
public void powerOff() {
System.out.println("Power Off");
}
public void volumeUp() {
speaker = new SonySpeaker();
speaker.volumeUp();
}
public void volumeDown() {
speaker = new SonySpeaker();
speaker.volumeDown();
}
}
테스트
applicationContext.xml
<bean name="tv3" class="polymorphism.SamsungTV3" />
TVUser5.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser5 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청(Lookup)
TV tv = (TV)factory.getBean("tv3");
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
// 3. Spring 컨테이너 종료
factory.close();
}
}
- SonySpeaker 객체 2개나 생성됨
- 다른 스피커로 변경 시 소스를 변경 해야함
4.2 생성자 인젝션 사용
- XML 설정 파일에 매개변수를 가지는 생성자 호출 설정
SamsungTV 생성자에 SonySpeaker 매개변수 추가
SamsungTV4.java
package polymorphism;
public class SamsungTV4 implements TV{
private SonySpeaker speaker = null;
public SamsungTV4() {
System.out.println("SamsungTV() 생성");
}
public SamsungTV4(SonySpeaker speaker) {
System.out.println("SamsungTV(SonySpeaker speaker) 생성");
this.speaker = speaker;
}
public void powerOn() {
System.out.println("Power On");
}
public void powerOff() {
System.out.println("Power Off");
}
public void volumeUp() {
speaker.volumeUp();
}
public void volumeDown() {
speaker.volumeDown();
}
}
테스트
applicationContext.xml
<bean id="tv4" class="polymorphism.SamsungTV4">
<constructor-arg ref="sony" />
</bean>
<bean id="sony" class="polymorphism.SonySpeaker" />
TVUser5.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser5 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청(Lookup)
TV tv = (TV)factory.getBean("tv4");
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
// 3. Spring 컨테이너 종료
factory.close();
}
}
- SonySpeaker 객체가 먼저 생성 SamsungTV 객체 오버로딩된 생성자로 생성됨
4.2.1 다중 변수 매핑
으로 등록 하는 객체는 ref 속성 사용 - 기본형 데이터는 value 속성 사용
맴버변수 추가 및 생성자 오버로딩
SamsungTV5.java
package polymorphism;
public class SamsungTV5 implements TV{
private SonySpeaker speaker = null;
private int price;
public SamsungTV5() {
System.out.println("SamsungTV5() 생성");
}
public SamsungTV5(SonySpeaker speaker) {
System.out.println("SamsungTV5() 생성");
this.speaker = speaker;
}
public SamsungTV5(SonySpeaker speaker, int price) {
System.out.println("SamsungTV5() 생성");
this.speaker = speaker;
this.price = price;
System.out.println("price="+this.price);
}
public void powerOn() {
System.out.println("Power On");
}
public void powerOff() {
System.out.println("Power Off");
}
public void volumeUp() {
speaker.volumeUp();
}
public void volumeDown() {
speaker.volumeDown();
}
}
applicationContext.xml
<bean id="tv5" class="polymorphism.SamsungTV5">
<constructor-arg ref="sony" />
<constructor-arg value="1000000" />
</bean>
<bean id="sony" class="polymorphism.SonySpeaker" />
테스트 TVUser5.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser5 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청(Lookup)
TV tv = (TV)factory.getBean("tv5");
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
// 3. Spring 컨테이너 종료
factory.close();
}
}
4.2.2. 의존관계 변경
- 인터페이스 추가
Speaker 인터페이스 추가
Speaker.java
package polymorphism;
public interface Speaker {
void volumeUp();
void volumeDown();
}
SonySpeaker 에 인터페이스 구현
SonySpeaker.java
package polymorphism;
public class SonySpeaker implements Speaker{
public SonySpeaker() {
System.out.println("SonySpeaker() 생성");
}
@Override
public void volumeUp() {
System.out.println("SonySpeaker volumeUp() ");
}
@Override
public void volumeDown() {
System.out.println("SonySpeaker volumeDown() ");
}
}
SonySpeaker 참조 Speaker 참조로 소스 수정
SamsungTV6.java
package polymorphism;
public class SamsungTV6 implements TV{
private Speaker speaker = null;
private int price;
public SamsungTV6() {
System.out.println("SamsungTV6() 생성");
}
public SamsungTV6(Speaker speaker) {
System.out.println("SamsungTV6() 생성");
this.speaker = speaker;
}
public SamsungTV6(Speaker speaker, int price) {
System.out.println("SamsungTV6() 생성");
this.speaker = speaker;
this.price = price;
System.out.println("price="+this.price);
}
public void powerOn() {
System.out.println("Power On");
}
public void powerOff() {
System.out.println("Power Off");
}
public void volumeUp() {
speaker.volumeUp();
}
public void volumeDown() {
speaker.volumeDown();
}
}
AppleSpeaker 추가 AppleSpeaker.java
package polymorphism;
public class AppleSpeaker implements Speaker{
public AppleSpeaker() {
System.out.println("AppleSpeaker() 생성");
}
@Override
public void volumeUp() {
System.out.println("AppleSpeaker volumeUp() ");
}
@Override
public void volumeDown() {
System.out.println("AppleSpeaker volumeDown() ");
}
}
테스트
- apple 스피커로 설정 변경
applicationContext.xml
<bean id="tv6" class="polymorphism.SamsungTV5">
<constructor-arg ref="apple" />
<constructor-arg value="1000000" />
</bean>
<bean id="sony" class="polymorphism.SonySpeaker" />
<bean id="apple" class="polymorphism.AppleSpeaker" />
TVUser6.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser6 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청(Lookup)
TV tv = (TV)factory.getBean("tv6");
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
// 3. Spring 컨테이너 종료
factory.close();
}
}
4.3 Setter 인젝션 사용하기
4.3.1. Setter 인젝션 기본
SamsungTV에 Setter 메소드 추가
SamsungTV7.java
package polymorphism;
public class SamsungTV7 implements TV{
private Speaker speaker = null;
private int price;
public SamsungTV7() {
System.out.println("SamsungTV7() 생성");
}
public void setSpeaker(Speaker speaker) {
System.out.println("setSpeaker()");
this.speaker = speaker;
}
public void setPrice(int price) {
System.out.println("setPrice()");
this.price = price;
}
public void powerOn() {
System.out.println("Power On");
}
public void powerOff() {
System.out.println("Power Off");
}
public void volumeUp() {
speaker.volumeUp();
}
public void volumeDown() {
speaker.volumeDown();
}
}
테스트
applicationContext.xml
엘리먼트 사용 - name 속성값이 set붙여 호출할 메소드 명(setSpeaker(), setPrice())
<bean id="tv7" class="polymorphism.SamsungTV7">
<property name="speaker" ref="apple"></property>
<property name="price" value="1000000"></property>
</bean>
<bean id="sony" class="polymorphism.SonySpeaker" />
<bean id="apple" class="polymorphism.AppleSpeaker" />
TVUser7.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser7 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청(Lookup)
TV tv = (TV)factory.getBean("tv7");
tv.powerOn();
tv.volumeDown();
tv.volumeUp();
tv.powerOff();
// 3. Spring 컨테이너 종료
factory.close();
}
}
4.3.2 p 네임스페이스 사용하기
- Setter 인젝션 사용시
없이 의존성 주입 가능 에 xmlns:p="http://springframework.org/schema/p" 추가 - [Namespaces] 에서 선택
- p:변수명-ref="참조객체id"
- p:변수명="설정값"
applicationContext.xml
<bean id="tv7" class="polymorphism.SamsungTV7" p:speaker-ref="sony" p:price="1000000" />
<bean id="sony" class="polymorphism.SonySpeaker" />
<bean id="apple" class="polymorphism.AppleSpeaker" />
4.4 컬렉션(Collection) 객체 설정
- java.util.List, 배열 :
- 엘리먼트
- java.util.Set :
엘리먼트 - java.util.Map :
- java.util.Properties :
엘리먼트
4.4.1 List 타입 매핑
CollectionBean.java
package com.springbook.ioc.injection;
import java.util.List;
public class CollectionBean {
private List<String> addressList;
public List<String> getAddressList() {
return addressList;
}
public void setAddressList(List<String> addressList) {
this.addressList = addressList;
}
}
테스트
applicationContext.xml
<bean id="collectionBean" class="kr.thecoding.spring.ioc.indection.CollectionBean">
<property name="addressList">
<list>
<value>서울시</value>
<value>경기도</value>
<value>제주도</value>
</list>
</property>
</bean>
CollectionBeanTest.java
package kr.thecoding.spring.ioc.indection;
import java.util.List;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class CollectionBeanTest {
public static void main(String[] args) {
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean)factory.getBean("collectionBean");
List<String> addressList = bean.getAddressList();
for(String address : addressList) {
System.out.println(address);
}
factory.close();
}
}
4.4.2 Set 타입 매핑
CollectionBean.java
package kr.thecoding.spring.ioc.indection;
import java.util.List;
import java.util.Set;
public class CollectionBean {
private List<String> addressList;
private Set<String> addressSet;
public List<String> getAddressList() {
return addressList;
}
public void setAddressList(List<String> addressList) {
this.addressList = addressList;
}
public Set<String> getAddressSet() {
return addressSet;
}
public void setAddressSet(Set<String> addressSet) {
this.addressSet = addressSet;
}
}
테스트
applicationContext.xml
<bean id="collectionBean" class="kr.thecoding.spring.ioc.indection.CollectionBean">
<property name="addressList">
<list>
<value>서울시</value>
<value>경기도</value>
<value>제주도</value>
</list>
</property>
<property name="addressSet">
<set>
<value>서울시</value>
<value>경기도</value>
<value>서울시</value>
</set>
</property>
</bean>
CollectionBeanTest.java
package kr.thecoding.spring.ioc.indection;
import java.util.List;
import java.util.Set;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class CollectionBeanTest {
public static void main(String[] args) {
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean)factory.getBean("collectionBean");
List<String> addressList = bean.getAddressList();
for(String address : addressList) {
System.out.println(address);
}
Set<String> addressSet = bean.getAddressSet();
for(String address : addressSet) {
System.out.println(address);
}
factory.close();
}
}
4.4.3 Map 타입 매핑
CollectionBean.java
package kr.thecoding.spring.ioc.indection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CollectionBean {
private List<String> addressList;
private Set<String> addressSet;
private Map<String, String> addressMap;
public List<String> getAddressList() {
return addressList;
}
public void setAddressList(List<String> addressList) {
this.addressList = addressList;
}
public Set<String> getAddressSet() {
return addressSet;
}
public void setAddressSet(Set<String> addressSet) {
this.addressSet = addressSet;
}
public Map<String, String> getAddressMap() {
return addressMap;
}
public void setAddressMap(Map<String, String> addressMap) {
this.addressMap = addressMap;
}
}
테스트
applicationContext.xml
<bean id="collectionBean" class="kr.thecoding.spring.ioc.indection.CollectionBean">
<property name="addressList">
<list>
<value>서울시</value>
<value>경기도</value>
<value>제주도</value>
</list>
</property>
<property name="addressSet">
<set>
<value>서울시</value>
<value>경기도</value>
<value>서울시</value>
</set>
</property>
<property name="addressMap">
<map>
<entry>
<key><value>고구려</value></key>
<value>평양</value>
</entry>
<entry>
<key><value>신라</value></key>
<value>경주</value>
</entry>
<entry>
<key><value>백제</value></key>
<value>부여</value>
</entry>
</map>
</property>
</bean>
CollectionBeanTest.java
package kr.thecoding.spring.ioc.indection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class CollectionBeanTest {
public static void main(String[] args) {
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean)factory.getBean("collectionBean");
List<String> addressList = bean.getAddressList();
for(String address : addressList) {
System.out.println(address);
}
Set<String> addressSet = bean.getAddressSet();
for(String address : addressSet) {
System.out.println(address);
}
Map<String, String> addressMap = bean.getAddressMap();
System.out.println(addressMap.get("고구려"));
Set<Entry<String, String>> set = addressMap.entrySet();
Iterator<Entry<String, String>> it = set.iterator();
while(it.hasNext()) {
Map.Entry<String, String> e = (Map.Entry<String, String>)it.next();
System.out.println("나라 : "+ e.getKey() + ", 수도 : " + e.getValue());
}
factory.close();
}
}
참고 : Map 사용법
4.4.4 Properties 타입 매핑
CollectionBean.java
package kr.thecoding.spring.ioc.indection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CollectionBean {
private List<String> addressList;
private Set<String> addressSet;
private Map<String, String> addressMap;
private Properties addressProp;
public List<String> getAddressList() {
return addressList;
}
public void setAddressList(List<String> addressList) {
this.addressList = addressList;
}
public Set<String> getAddressSet() {
return addressSet;
}
public void setAddressSet(Set<String> addressSet) {
this.addressSet = addressSet;
}
public Map<String, String> getAddressMap() {
return addressMap;
}
public void setAddressMap(Map<String, String> addressMap) {
this.addressMap = addressMap;
}
public Properties getAddressProp() {
return addressProp;
}
public void setAddressProp(Properties addressProp) {
this.addressProp = addressProp;
}
public Properties getAddressProp() {
return addressProp;
}
public void setAddressProp(Properties addressProp) {
this.addressProp = addressProp;
}
}
테스트
applicationContext.xml
<bean id="collectionBean" class="kr.thecoding.spring.ioc.indection.CollectionBean">
<property name="addressList">
<list>
<value>서울시</value>
<value>경기도</value>
<value>제주도</value>
</list>
</property>
<property name="addressSet">
<set>
<value>서울시</value>
<value>경기도</value>
<value>서울시</value>
</set>
</property>
<property name="addressMap">
<map>
<entry>
<key><value>고구려</value></key>
<value>평양</value>
</entry>
<entry>
<key><value>신라</value></key>
<value>경주</value>
</entry>
<entry>
<key><value>백제</value></key>
<value>부여</value>
</entry>
</map>
</property>
<property name="addressProp">
<props>
<prop key="고려">개성</prop>
<prop key="조선">한양</prop>
<prop key="대한민국">서울</prop>
</props>
</property>
</bean>
CollectionBeanTest.java
package kr.thecoding.spring.ioc.indection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class CollectionBeanTest {
public static void main(String[] args) {
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean)factory.getBean("collectionBean");
List<String> addressList = bean.getAddressList();
for(String address : addressList) {
System.out.println(address);
}
Set<String> addressSet = bean.getAddressSet();
for(String address : addressSet) {
System.out.println(address);
}
Map<String, String> addressMap = bean.getAddressMap();
System.out.println(addressMap.get("고구려"));
Set<Entry<String, String>> set = addressMap.entrySet();
Iterator<Entry<String, String>> it = set.iterator();
while(it.hasNext()) {
Map.Entry<String, String> e = (Map.Entry<String, String>)it.next();
System.out.println("나라 : "+ e.getKey() + ", 수도 : " + e.getValue());
}
Properties addressProp = bean.getAddressProp();
System.out.println(addressProp.getProperty("고구려"));
Enumeration<?> e = addressProp.propertyNames();
while(e.hasMoreElements()) {
String country = (String)e.nextElement();
System.out.println("나라 : "+ country + ", 수도 : " + addressProp.getProperty(country));
}
Iterator<?> eit = (Iterator<?>)addressProp.propertyNames();
while(eit.hasNext()) {
String country = (String)eit.next();
System.out.println("나라 : "+ country + ", 수도 : " + addressProp.getProperty(country));
}
factory.close();
}
}
05 어노테이션 기반 설정
5.1 어노테이션 설정
5.1.1 Context 네임스페이스 추가
- applicationContext.xml > Namespaces > context 체크
- 어노테이션 설정을 위해
에 context 네임스페이스 항목 추가
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.2.xsd"
5.1.2 컴포넌트 스캔(component-scan) 설정
- 설정 파일에 base-package 속성에 스캔할 패키지 설정(하위 패키지 포함 자동 스캔됨)
<bean>
에 등록하지 않고 객체 자동 생성- 클래스파일에 @Component("id") 설정
- @Component("id") 설정된 클래스들을 자동으로 객체 생성
- id 생략시 클래스 이름의 첫글자를 소문자로 변경 함
applicationContext.xml
<context:component-scan base-package="polymorphism"></context:component-scan>
LgTV.java
package polymorphism;
import org.springframework.stereotype.Component;
@Component("tv")
public class LgTV implements TV{
@Override
public void powerOn() {
// TODO Auto-generated method stub
}
@Override
public void powerOff() {
// TODO Auto-generated method stub
}
@Override
public void volumeUp() {
// TODO Auto-generated method stub
}
@Override
public void volumeDown() {
// TODO Auto-generated method stub
}
}
TVUser7.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser7 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청
TV tv = (TV)factory.getBean("tv");
// 3. Spring 컨테이너 종료
factory.close();
}
}
5.2 의존성 주입 설정
5.2.1 의존성 주입 어노테이션
- @Autowired : 주로 변수 위에 설정하여 해당 타입의 객체를 찾아서 자동으로 할당
- @Qualifier : 특정 객체의 이름을 이용하여 의존성 주입할 때
- @Inject : @Autowired와 동일한 기능을 제공
- @Resource : @Autowired와 @Qualifier 기능을 결합한 어노테이션
5.2.2 @Autowired
- 생성자, 메소드, 멤버변수 위에서 사용
- @Autowired 확인 순간 부터 해당 변수 타입 체크
- 메모리에 존재 시 객체를 변수에 주입
LgTV에 Speaker 멤버변수 와 @Autowired 추가 LgTV.java
package polymorphism;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("tv")
public class LgTV implements TV{
@Autowired
private Speaker speaker;
@Override
public void powerOn() {
// TODO Auto-generated method stub
}
@Override
public void powerOff() {
// TODO Auto-generated method stub
}
@Override
public void volumeUp() {
}
@Override
public void volumeDown() {
// TODO Auto-generated method stub
}
}
테스트
- Speaker 객체 생성 되지 않아 오류 남
TVUser7.java
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser7 {
public static void main(String[] args) {
// 1. Spring 컨테이너 중 하나아니 GenericXmlApplicationContext 구동
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
// 2. Spring 컨테이너로부터 필요한 객체 요청
TV tv = (TV)factory.getBean("tv");
// 3. Spring 컨테이너 종료
factory.close();
}
}
Speaker 객체 생성(sony) 추가 후 재 테스트
- @Component("sony") SonySpeaker.java
package polymorphism;
import org.springframework.stereotype.Component;
@Component("sony")
public class SonySpeaker implements Speaker{
public SonySpeaker() {
System.out.println("SonySpeaker() 생성");
}
public void volumeUp() {
System.out.println("SonySpeaker volumeUp() ");
}
public void volumeDown() {
System.out.println("SonySpeaker volumeDown() ");
}
}
Speaker 객체 생성(apple) 추가 후 재 테스트
- @Component("apple")
- 의존성 주입 대상이 2개이상이라 오류 발생
AppleSpeaker.java
package polymorphism;
import org.springframework.stereotype.Component;
@Component("apple")
public class AppleSpeaker implements Speaker{
public AppleSpeaker() {
System.out.println("AppleSpeaker() 생성");
}
public void volumeUp() {
System.out.println("AppleSpeaker volumeUp() ");
}
public void volumeDown() {
System.out.println("AppleSpeaker volumeDown() ");
}
}
5.2.3 @Qualifier
- @Autowired 의존성 주입 대상이 2개 이상일 때 오류 발생 발생
- SonySpeaker, AppleSpeaker
- @Qualifier("id") 로 하나 지정
LgTV.java
package polymorphism;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("tv")
public class LgTV implements TV{
@Autowired
@Qualifier("apple")
private Speaker speaker;
public LgTV() {
System.out.println("LgTV() 생성");
}
@Override
public void powerOn() {
// TODO Auto-generated method stub
}
@Override
public void powerOff() {
// TODO Auto-generated method stub
}
@Override
public void volumeUp() {
// TODO Auto-generated method stub
}
@Override
public void volumeDown() {
// TODO Auto-generated method stub
}
}
5.2.3 @Resource
- name 속성에 객체의 이름을 할당해서 주입
- import javax.annotation.Resource;
- @Resource 안나오면 pom.xml에 dependncy 추가 pom.xml
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
LgTV.java
//@Autowired
//@Qualifier("apple")
@Resource(name="apple")
private Speaker speaker;
5.2.5 어노테이션과 XML 설정 병행하여 사용
- 변경되지 않는 객체는 어노테이션으로 설정(Speaker)
- 변경될 가능성이 있는 객체는 XML 설정(Speaker의 종류)
- 라이브러리로 제공하는 클래스는 XML로만 사용할 수 있음(소스 변경 컴파일 불가)
LgTv.java
@Autowired
private Speaker speaker;
xml
<bean id="sony" class="polymorphism.SonySpeaker" />
5.3 추가 어노테이션
5.3.1 @Component를 상속하여 추가된 어노테이션
- 클래스 역할 분류 뿐만 아니라 각 클래스의 역할에 따른 예외 처리등이 추가 되어 있음
- @Service : XXXServiceImpl : 비즈니스 로직을 처리하는 Service 클래스
- @Repository : XXXDAO : 데이터베이스 연동을 처리하는 DAO 클래스
- @Controller : XXXController : 사용자 요청을 제어하는 Controller 클래스