1강 Framework 개념
1. SW 재사용 방안들
복사(Copy) & 붙이기(Paste)
- 초보적인 재사용 방식으로 비슷한 예제를 다른 Source에서 복사해서 사용함.
GregorianCalendar date = (GregorianCalender)Calendar.getInstance();
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
String date = df format(date);
- 예를 들어, A라는 클래스에서 Date타입을 String 타입으로 변환하는 코딩을 하고, 클래스 B에서 동일한 로직이 필요하여 복사했다고 가정한 경우
- JDK 버전이 바뀌어 동일한 기능을 제공하는 향상된 인터페이스가 나오면 위의 코드를 사용한 A, B 클래스를 모두 변경해야 한다.
메서드 호출
- 자주 사용되고, 유사한 기능들을 모아 메서드로 정의하여 재사용함.
public class DateUtility{
public static String toStringToday(String format){
GregorianCalendar date = (GregorianCalender)Calendar.getInstance();
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
String date = df format(date);
}
}
String sdate = DateUtility.toStringToday("yyyyMMdd");
- JDK 버전이 바뀌거나 메서드의 내용이 수정되더라도 해당 클래스를 모두 수정할 필요 없이 toStringToday() 메서드의 내용만 수정하면 된다.
- toStringToday() 메서드의 Signature를 변경하면 이 메서드를 사용하는 모든 클래스에 영향을 준다.
- 메서드 재사용 방법은 '복사 & 붙이기'보다는 진보된 방식이지만, 작업 영역간의 결합도(Coupling) 문제는 여전히 존재한다.
클래스 재사용 (상속)
- 자주 사용되고, 유사한 기능들을 모아 메서드로 정의하여 재사용함.
public class Person{
public String printBirthDate(String format){
DateUtility.toStringToday(birthDate, format);
}
}
- Person을 상속받은 모든 클래스들은 자동적으로 변경된 printBirthDate() 메서드를 사용하게 된다.
- DateUtility 클래스의 메서드가 변경되더라도 printBirthDate()메서드의 인터페이스가 변하지 않으면 나머지 클래스들은 영향을 받지 않는다.
AOP(Aspect Oriented Programming)
- 관심의 분리(Seperatrion of Concerns)
- AOP가 핵심관심모듈의 코드를 직접 건드리지 않고 필요한 기능이 동작하도록 하는 데는 위빙(Weaving)이라고 하는 특수한 작업이 필요하다. 즉, AOP에서 위빙 작업을 통해 핵심모듈 사이사이에 필요한 횡단 관심 코드가 동작하도록 엮어지게 만든다.
- AOP는 OOP를 더욱 OOP 답게 만들어 줄 수 있다.
- AOP는 OOP 뿐만 아니라 기존의 절차적 프로그래밍에도 적용될 수 있다.
2. 디자인 패턴과 프레임워크의 관련성
디자인패턴의 정의
- 프로그램 개발에서 자주 나타나는 과제를 해결하기 위한 방법 중 하나로, 소프트웨어 개발과정에서 발견된 Know-How를 축적하여 이름을 붙여 이후에 재사용하기 좋은 형태로 특정 규약을 묶어서 정리한 것
- 이 용어를 소프트웨어 개발 영역에서 구체적으로 처음 제시한 곳은, GoF(Gang of Four)라 불리는 네 명의 컴퓨터 과학 연구자들이 쓴 서적 'Design Patterns: Elements of Reusable Object-Oriented Software'(재사용 가능한 객체지향 소프트웨어의 요소 - 디자인 패턴)이다.
- 디자인 패턴을 사용하는 이유
요구사항은 수시로 변경 → 요구사항 변경에 대한 Source Code 변경을 최소화
여러 사람이 같이하는 팀 프로젝트 진행 → 범용적인 코딩 스타일을 적용
상황에 따라 인수 인계하는 경우도 발생 → 직관적인 코드를 사용
프레임워크의 정의
- 비기능적(Non-Funtional) 요구사항(성능, 보안, 확장성, 안정성 등)을 만족하는 구조와 구현된 기능을 안정적으로 실행하도록 제어해주는 잘 만들어진 구조의 라이브러리의 덩어리
- 프레임워크는 애플리케이션들의 최소한의 공통점을 찾아 하부 구조를 제공함으로써 개발자들로 하여금 시스템의 하부 구조를 구현하는데 들어가는 노력을 절감하게 해줌
- 프레임워크를 사용하는 이유
비기능적인 요소들을 초기 개발 단계마다 구현해야 하는 불합리함을 극복해준다.
기능적인(Functional) 요구사항에 집중할 수 있도록 해준다.
디자인 패턴과 마찬가지로 반복적으로 발견되는 문제를 해결하기 위한 특화된 Solution을 제공한다.
디자인패턴과 프레임워크의 관련성
- 디자인 패턴은 프레임워크의 핵심적인 특징이고, 프레임워크를 사용하는 애플리케이션에 그 패턴이 적용된다는 특징을 가지고 있다. 하지만 프레임워크는 디자인 패턴이 아니다.
- 디자인 패턴은 애플리케이션을 설계할 때 필요한 구조적인 가이드라인이 되어 줄 수는 있지만 구체적으로 구현된 기반코드를 제공하지 않는다.
- 프레임워크는 디자인 패턴과 함께 패턴이 적용 된 기반 클래스 라이브러리를 제공해서 프레임워크를 사용하는 구조적인 틀과 구현코드를 함께 제공한다.
- 개발자는 프레임워크의 기반코드를 확장하여 사용하면서 자연스럽게 그 프레임워크에서 사용된 패턴을 적용할 수 있게 된다.
3. 프레임워크의 구성요소와 종류
IoC(Inversion of Control)
- IoC란 "제어의 역전" 즉, 인스턴스 생성부터 소멸까지의 인스턴스 생명주기 관리를 개발자가 아닌 컨테이너가 대신 해준다는 뜻임. 즉, 컨테이너 역할을 해주는 프레임워크에게 제어하는 권한을 넘겨서 개발자의 코드가 신경 써야 할 것을 줄이는 전략이다.
- 프레임워크의 동작원리를 제어흐름이 일반적인 프로그램 흐름과 반대로 동작하므로 IoC라고 설명함.
- Spring 컨테이너는 IoC를 지원하며, 메타데이터(XML 설정)를 통해 beans를 관리하고 어플리케이션의 중요부분을 형성함.
- Spring 컨테이너는 관리되는 bean들을 의존성주입(Dependency Injection)을 통해 IoC를 지원함.
클래스 라이브러리(Class Library)
- 프레임워크는 특정 부분의 기술적인 구현을 라이브러리 형태로 제공한다.
- Class Library라는 구성요소는 프레임워크의 정의 중 하나인 "Semi Complete(반제품)" 이다. 라고 해석하게 만들었다.
특징 |
프레임워크 |
라이브러리 |
유저코드의 작성 |
프레임워크 클래스를 서브클래싱 해서 작성 |
독립적으로 작성 |
호출흐름 |
프레임워크코드가 유저코드를 호출 |
유저코드가 라이브러리를 호출 |
실행흐름 |
프레임워크가 제어 |
유저코드가 제어 |
객체의 연동 |
구조프레임워크가 정의 |
독자적으로 정의 |
- 라이브러리와 프레임워크의 차이점
프레임워크와 라이브러리를 구분하는 방법은 실행제어가 어디서 일어나는 가에 달려있다.
라이브러리는 개발자가 만든 클래스에서 직접 호출하여 사용하므로 실행의 흐름에 대한 제어를 개발자의 코드가 관장하고 있다.
프레임워크는 반대로 프레임워크에서 개발자가 만든 클래스를 호출하여 실행의 흐름에 대한 제어를 담당한다.
디자인 패턴
- 디자인 패턴 + 라이브러리 = 프레임워크
- 프레임워크는 디자인 패턴과 그것이 적용된 기반 라이브러리의 결합으로 이해할 수 있다.
- 프레임워크의 라이브러리를 살펴볼 때도 적용된 패턴을 주목해서 살펴 본다면 그 구성을 이해하기 쉽다.
- 특히 프레임워크를 확장하거나 커스터마이징 할 때는 프레임워크에 적용된 패턴에 대한 이해가 꼭 필요하다.
프레임워크 종류
- 아키텍쳐 결정 = 사용하는 프레임워크의 종류 + 사용전략
기능 |
프레임워크 종류 |
웹(MVC) |
Spring MVC, Struts2, Webwork, PlayFramework |
OR(Object-Relational) 매핑 |
MyBatis, Hibernate, JPA, Spring JDBC |
AOP(Aspect Oriented Programming) |
Spring AOP, AspectJ, JBoss AOP |
DI(Dependency Injection) |
Spring DI, Google Guicd |
Build와 Library관리 |
Ant+lvy, Maven, Gradle |
단위 테스트 |
jUnit, TestNG, Cactus |
JavaScript |
jQuery, AngularJS, Node.js |
학습정리 SW 재사용 방안들 : Copy&Paste, Method, Inheritance, AOP 디자인 패턴과 프레임워크 관련성 : 개발자는 프레임워크의 기반코드를 확장하여 사용하면서 자연스럽게 프레임워크에서 사용된 패턴들을 적용할 수 있게 된다. 프레임워크의 구성요소와 종류 : IoC(Inversion of Control), Design Pattern, Class Library |
2강 환경설정
1. JDK(Java Development Kit) 8 설치 및 API 문서
Java SE Development Kit 8 downloads
- URL http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
Java SE Development Kit 8 API
- URL https://docs.oracle.com/javase/8/docs/api/
2. STS(Spring Tool Suite) 3.7.3 설치 및 Spring API 문서
STS downloads
- URL https://spring.io/tools/sts
SpringSource Site
- URL http://spring.io/
SpringFramwork 3.2.17 release API 문서
- URL https://docs.spring.io/spring/docs/3.2.17.RELEASE/javadoc-api/
3. Tomcat 8 설치
Tomcat8.0 downloads
- URL https://tomcat.apache.org/download-80.cgi
4. Oracle 11g XE(eXpress Edition) 설치
- URL http://www.oracle.com/technetwork/database/database-technologies/express-edition/downloads/index.html
학습정리 JDK 8 설치 및 API문서 - JDK(Java Development Kit) 1.8 다운로드 및 설치 - Java SE API 문서 찾기 STS 3.7.3 설치 및 Spring API 문서 - STS(Spring Tool Suit) 3.7.3 다운로드 및 설치 - SpringFramework 3.2.17 Release API 문서 찾기 Tomcat 8 설치 - Tomcat 8.0 다운로드 및 설치 Oracle 11g XE 설치 - Oracle 11g XE 버전 다운로드 및 설치 |
3강 Spring Framework 개요
1. Spring Framework 개요
Spring Framework란?
- Java 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크이다.
- 애플리케이션 프레임워크
특정 계층이나 기술, 업무 분야에 국한되지 않고 애플리케이션의 전 영역을 포괄하는 범용적인 프레임워크를 말한다.
- 경략급 프레임워크
단순한 웹컨테이너에서도 엔터프라이즈 개발의 고급기술을 대부분 사용할 수 있다.
- 엔터프라이즈 개발 용이
개발자가 복잡하고 실수하기 쉬운 Low Level에 많이 신경쓰지 않으면서 Business Logic 개발에 전념할 수 있도록 해준다. 여기서 Low Level이란 보안, 인증 Transaction처리이다. 이 Low Level를 프레임워크가 해결해준다.
- 오픈소스
Spring은 OpenSource의 장점을 충분히 취하면서 동시에 OpenSource 제품의 단점과 한계를 잘 극복한다.
Spring Framework 전략
- Spring 삼각형
엔터프라이즈 개발의 복잡함을 상대하는 Spring 의 전략
→ Portable Service Abstraction, Dependency Injection, AOP, POJO + BLUEPRINTS
Portable Service Abstraction(서비스 추상화)
- 트랜잭션 추상화, OXM 추상화, 데이터 액세스의 Exception 변환기능 등 기술적인 복잡함은 추상화를 통해 Low Level의 기술구현 부분과 기술을 사용하는 인터페이스로 분리한다.
(OXM : Object XML Mapping)
객체지향과 DI (Dependency Injection, DI는 의존관계주입의 약자)
- Spring은 객체지향에 충실한 설계가 가능하도록 단순한 객체 형태로 개발할 수 있고, DI는 유연하게 확장 가능한 객체를 만들어 두고 그 관계는 외부에서 다이나믹하게 설정해준다.
AOP(Aspect Oriented Programming 관점지향프로그래밍)
- AOP는 애플리케이션 로직을 담당하는 코드에 남아 있는 기술관련 코드를 분리해서 별도의 모듈로 관리하게 해주는 강력한 기술이다.
POJO(Plain Old Java Object 일반적인 JAVA Object 프로그램)
- POJO는 객체지향 원리에 충실하면서, 특정 환경이나 규약에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 객체이다. JVM만 있으면 돌아가는 프로그램
2. Spring Framework 특징
- 컨테이너 역할
Spring 컨테이너는 Java 객체의 LifeCycle을 관리하며, Spring 컨테이너로 부터 필요한 객체를 가져와 사용할 수 있다.
- DI(Dependency Injection) 지원
Spring은 설정 파일이나(XML)이나 어노테이션(@comment)을 통해서 객체 간의 의존관계를 설정할 수 있도록 하고 있다.
- AOP(Aspect Oriented Programming) 지원 [관점지향프로그래밍]
Spring은 트랜잭션이나 로깅, 보안과 같이 공통적으로 필요로 하는 모듈들을 실제 핵심 모듈에서 분리해서 적용할 수 있다.
- POJO(Plain Old Java Object) 지원
Spring 컨테이너에 저장되는 Java객체는 특정한 인터페이스를 구현하거나, 특정 클래스를 상속받지 않아도 된다.
- 트랜잭션 처리를 위한 일관된 방법을 지원
JDBC, JTA 등 어떤 트랜잭션을 사용하던 설정을 통해 정보를 관리하므로 트랜잭션 구현에 상관없이 동일한 코드 사용가능
- 영속성(Persistence)과 관련된 다양한 API 지원
Spring은 MyBatis, Hibernate 등 데이터베이스 처리를 위한 ORM(Object Relational Mapping) 프레임워크들과의 연동 지원
3. Spring Framework 기능요소
- Spring 프레임워크를 구성하는 기능 요소
Spring AOP |
Spring ORM |
Spring Web |
Spring Web MVC |
Spring DAO |
Spring Context | ||
Spring Core |
Spring Core (Core 컨테이너)
- Spring 프레임워크의 기본기능을 제공한다.
- 이 모듈에 있는 BeanFactory는 Spring의 기본 컨테이너이면서 스프링 DI의 기반이다.
Spring AOP (AOP)
- AOP 모듈을 통해 Aspect 지향 프로그래밍을 지원한다.
- AOP 모듈은 스프링 애플리케이션에서 Aspect를 개발할 수 있는 기반을 지원한다.
Spring ORM (ORM)
- MyBatis, Hibernate, JPA 등 널리 사용되는 ORM 프레임워크와의 연결고리를 제공한다.
- ORM 제품들을 Spring의 기능과 조합해서 사용할 수 있도록 해준다.
Spring DAO (DAO)
- JDBC에 대한 추상화 계층으로 JDBC 코딩이나 예외처리 하는 부분을 간편화 시켰으며, AOP 모듈을 이용해 트랜잭션 관리 서비스도 제공한다.
Spring Web (Web)
- 일반적인 웹애플리케이션 개발에 필요한 기본기능을 제공한다.
- Webwork나 Struts와 같은 다른 웹애플리케이션 프레임워크와의 통합을 지원한다.
Spring Context (Context)
- Context 모듈은 BeanFactory의 개념을 확장한 것으로 국제화(|18N) 메시지, 애플리케이션 생명주기 이벤트, 유효성 검증 등을 지원한다.
Spring Web MVC (WebMVC(Model/View/Controller)
- 사용자 인터페이스가 애플리케이션 로직과 분리되는 웹 어플리케이션을 만드는 경우에 일반적으로 사용되는 패러다임이다.
학습정리 Spring Framework 정의 : Java 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크이다. Spring Framework 전략 : Portable Service Abstraction, DI, AOP, POJO Spring Framework 특징 : 컨테이너, DI와 AOP, POJO 지원, 일관된 트랜잭션 처리방법 다양한 ORM과의 연동 Spring Framework 기능요소 : Core 컨테이너, Context, DAO, ORM, AOP, Web, WebMVC |
4강 Spring Project 시작하기
1. STS 소개 및 제공하는 기능
STS(SpringSource Tool Suite) 소개
- Spring 개발업체인 SpringSource가 직접 만들어 제공하는 이클립스의 확장판으로 최신 이클립스를 기반으로 주요한 Spring 지원 플러그인과 관련된 도구를 모아서 Spring 개발에 최적화되도록 만들어진 IDE이다.
STS가 제공하는 기능
- Bean 클래스 이름 자동완성 : 현재 프로젝트의 모든 Source와 라이브러리, JDK안의 모든 클래스 중에서 첫 글자가 SDD로 시작하는 클래스를 자동으로 보여줌
- 설정파일 생성 위저드 : Bean 설정파일 생성 위저드 중 사용할 Namespace와 Schema 버전을 선택하는 화면 (설정파일은 XML에서 하는 작업이다.)
- Bean 의존관계 그래프 : Spring IDE는 XML 설정파일을 읽어서 자동으로 그래프 그려줌, 각 Bean이 어떻게 참조되고, 어떤 Property를 갖는지 알 수 있음
- AOP 적용 대상 표시 : Spring IDE의 XML 설정파일 편집기를 이용하면 AOP의 적용 대상을 손쉽게 확인할 수 있다.
2. Maven과 Library 관리
Maven이란?
- URL http://maven.apache.org
- 라이브러리 관리 + 빌드 툴
Maven을 사용하는 이유
- 편리한 Dependent Library 관리 - Dependency Management
- 여러 프로젝트에서 프로젝트 정보나 jar 파일들을 공유하기 쉬움
- 모든 프로젝트의 빌드 프로세스를 일관되게 가져갈 수 있음.
Maven이전의 Library 관리 방법
- 과거의 의존성 라이브러리 관리
라이브러리 사이트 접속 → 라이브러리 다운로드 → 압축 해제 → 프로젝트에 라이브러리 복사 → 플래스 패스에 추가 → 라이브러리 사이트 접속 ...
- Maven에서 의존성 라이브러리 관리
Pom.xml 파일 수정 → 빌드 → Pom.xml 파일 수정 ...
pom.xml
- Maven 프로젝트를 생성하면 pom.xml 파일이 생성된다.
- pom.xml 파일은 Project Object Model 정보를 담고 있다.
pom.xml 의존관계(dependency) 추가
- Spring 프레임워크 설치
http://mvnrepository.com 접근한다.
org.springframework로 검색한다.
spring-jdbc 모듈과 spring-web 모듈을 추가한다.
- Eclipse 제공 : Maven Repositories View
Window → Show View → Other → Maven → Maven Repositories
3. Spring Project 작성하기
STS시작하기
다운받은 Spring-toll-suite-3.7.3.RELEASE의 압축을 풀어서 STS.exe를 실행한다.
Spring Project 생성 및 Spring Module 설치
Java Project → Convert to Maven Project → Add Spring Project Nature
pom.xml 파일에 dependency 추가 : https://mvnrepository.com에서 spring context module 검색
학습정리 STS 소개 및 제공하는 기능 : Spring 개발에 최적화된 IDE, 클래스 자동완성기능, 설정파일 생성 위저드 Maven과 Library 관리 : 라이브러리 관리 + 빌드 툴, pom.xml, 편리한 Dependent Library 관리 기능 Spring Project 시작하기 : Java Project → Convert to Maven Project → Add Spring Project Nature |
5강 IoC와 DI
1. IoC(Inversion of Control)
IoC의 개념
- IoC(제어권의 역전)이란, 객체의 생성, 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀌었다는 것을 의미한다.
- 컴포넌트 의존관계 결정(component dependency resolution), 설정(configuration) 및 생명주기(lifecycle)를 해결하기 위한 디자인 패턴(Design Pattern)
IoC 컨테이너
- 스프링 프레임워크도 객체에 대한 생성 및 생명주기를 관리할 수 있는 기능을 제공하고 있음. 즉, IoC 컨테이너 기능을 제공한다.
- IoC 컨테이너는 객체의 생성을 책임지고, 의존성을 관리한다.
- POJO의 생성, 초기화, 서비스, 소멸에 대한 권한을 가진다.
- 개발자들이 직접 POJO를 생성할 수 있지만 컨테이너에게 맡긴다.
IoC의 분류
- IoC (Inversion of Control) 안에 DL과 DI가 있음
- DL (Dependency Lookup) : EJB, SPring
- DI (Dependency Injection) : Spring PicoContainer
- Setter Injetion
- Constructor Injection
- Method Injection
DL(Dependency Lookup) 과 DI(Dependency Injection)
DL(Dependency Lookup) 의존성 검색 : 저장소에 저장되어 있는 Bean에 접근하기 위해 컨테이너가 제공하는 API를 이용하여 Bean을 Lookup 하는 것
DI(Dependency Injection) 의존성 주입 : 각 클래스간의 의존관계를 빈 설정(Bean Definition) 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것
DL 사용시 컨테이너 종속성이 증가하여, 주로 DI를 사용함
Setter Injetion
Constructor Injection
Method Injection
2. DI(Dependency Injection)
DI의 개념
각 클래스간의 의존관계를 빈 설정 (Bean Definition) 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것을 말함
- 개발자들은 단지 빈 설정파일에서 의존관계가 필요하다는 정보를 추가하면 된다.
- 객체 레퍼런스를 컨테이너로부터 주입 받아서, 실행 시에 동적으로 의존관계가 생성된다.
- 컨테이너가 흐름의 주체가 되어 애플리케이션 코드에 의존관계를 주입해 주는 것이다.
- DI의 장점
코드가 단순해진다.
컴포넌트 간의 결합도가 제거된다.
DI의 유형
- Setter Injetion : Setter 메서드를 이용한 의존성 삽입
의존성을 입력 받는 setter 메서드를 만들고 이를 통해 의존성을 주입한다.
- Constructor Injection : 생성자를 이용한 의존성 삽입
필요한 의존성을 포함하는 클래스의 생성자를 만들고 이를 통해 의존성을 주입힌다.
- Method Injection : 일반 메서드를 이용한 의존성 삽입
의존성을 입력 받는 일반 메서드를 만들고 이를 통해 의존성을 주입한다.
클래스 |
→ |
사용 |
→ |
인터페이스 |
↑ |
|
↑ | ||
의존성삽입 |
구현 | |||
↑ |
↑ | |||
조립기 |
→ |
생성 |
→ |
구현클래스 |
클래스가 구현클래스를 사용한다고 했을 때, DI를 이용하면 반드시 구현클래스를 상위 인터페이스를 하나 만들고 클래스가 구현클래스를 바로 의존하는 것이 아니라, 클래스는 인터페이스만 사용하게 된다. 그리고 구현클래스에 대한 정보는 XML(설정파일)에 개발자가 기술을 해놓는다. 그러면 프레임워크 측 컨테이너가 설정파일 정보를 읽어서 구현객체(클래스)를 생성해주고, 클래스와 구현클래스 사이에 의존성 관계가 있다고, 의존성 삽입은 컨테이너가 해주는 방식이 DI를 이용한 클래스 호출방식이다.
- Setter Injection
Hello |
→ |
<interface> printer |
|
|
↗ |
↖ | |
beans.xml |
String Printer |
|
Console Printer |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
package bean;
import java. util.List;
public class Hello {
String name;
Printer printer;
public Hello( ){
}
public void setName(String name) {
this.name = name;
}
public void setPrinter(Printer printer) {
this.printer = printer;
}
} |
cs |
1
2
3
4
5
6 |
<bean id="hello" class="bean.Hello">
<property name="name" value="Spring" />
<property name="name" value="Spring" />
</bean>
<bean id="printer" class="bean.StringPrinter" />
<bean id="consolePrinter" class="bean.ConsolePrinter" /> |
cs |
Setter Injection방법을 사용하면
Hello가 String Printer를 의존하게 된다.
그리고 소스코드에는 new String Printer라는 객체생성코드는 없이 상위 인터페이스의 멤버변수를 하나 호출을 한 후, setter 메소드를 선언을 한다.
Hello와 String Printer가 의존관계가 있다는 것을 설정파일에 작성하면 된다.
여기서 bean이라는 것은 spring이 관리해주는 관리 객체라는 의미로, container에게 관리를 부탁하는 객체는 bean이 된다.
bean 안에 있는 프로퍼티와 Java에 맵핑되는 setter의 이름이 같게 정의를 해주면 spring이 의존관계가 있다는 것을 알 수 있다.
Setter Injection은 한 번에 한 개씩만 의존관계를 주입받을 수 있다.
한 번에 한개씩 의존관계 주입방법을 개선한것이 Constructor Injection이다.
- Constructor Injection
Hello |
→ |
<interface> printer |
|
|
↗ |
↖ | |
beans.xml |
String Printer |
|
Console Printer |
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
package bean;
import java. util.List;
public class Hello {
String name;
Printer printer;
public Hello( ){
}
public Hello(String name, Printer printer){
this.name = name;
this.printer = printer;
}
} |
cs |
1
2
3
4
5
6 |
<bean id="hello" class="bean.Hello">
<constructor-arg index="0" value="Spring" />
<constructor-arg index="1" ref="printer" />
</bean>
<bean id="printer" class="bean.StringPrinter" />
<bean id="consolePrinter" class="bean.ConsolePrinter" /> |
cs |
Constructor Injection방법을 사용하면 인자를 두개를 받는 생성자를 만든 후
설정파일에 construntor-arg라는 태그에 index, value로 작성하여 의존관계가 있다고 설정한다.
3. Spring DI 컨테이너
Spring DI 컨테이너의 개념
- Spring DI 컨테이너가 관리하는 객체를 빈(bean)이라고 하고, 이 빈(bean)들을 관리한다는 의미로 컨테이너를 빈 팩토리(BeanFactory)라고 부른다.
- 객체의 생성과 객체 사이의 런타임(run-time) 관계를 DI 관점에서 볼 때는 컨테이너를 BeanFactory라고 한다.
- Bean Factory에 여러 가지 컨테이너 기능을 추가하여 애플리케이션 컨텍스(Application Context)라고 부른다.
<interface> BeanFactory |
← |
<interface> ApplicationContext |
BeanFactory와 Application Context
- BeanFactory
Bean을 등록, 생성, 조회, 반환 관리함
보통은 BeanFactory를 바로 사용하지 않고, 이를 확장한 ApplicationContext를 사용함
getBean() 메서드가 정의되어 있음
- Application Context
Bean을 등록, 생성, 조회, 반환 관리하는 기능은 BeanFactory와 같음
Spring의 각종 부가 서비스를 추가로 제공함
Spring이 제공하는 ApplicationContext 구현 클래스가 여러 가지 종류가 있음
학습정리 IoC(Inversion of Control) : 제어의 역전, IoC 컨테이너, DL, DI DI(Dependency Injection) : 클래스 간의 의존관계를 컨테이너가 주입, Setter Injection, Constructor Injection Spring DI 컨테이너 : BeanFactory, ApplicationContext |
6강 DI 애플리케이션 작성(1)
1. POJO 클래스 작성
POJO 클래스 다이어그램
Hello | → | <interface> printer |
|
| ↗ | ↖ | |
beans.xml | String Printer |
| Console Printer |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring</groupId> <artifactId>spring</artifactId> <version>0.0.1-SNAPSHOT</version> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.2.17.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.2.17.RELEASE</version> </dependency> </dependencies> </project> | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package myspring.di.xml; public class Hello { private String name; private Printer printer; public Hello() {} public void setName(String name) { this.name = name; } public void setPrinter(Printer printer) { this.printer = printer; } public String sayHello() { return "Hello " + name; } public void print() { this.printer.print(sayHello()); } } | cs |
1 2 3 4 5 6 | package myspring.di.xml; public interface Printer { public void print(String message); } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package myspring.di.xml; public class StringPrinter implements Printer { private StringBuffer buffer = new StringBuffer(); @Override public void print(String message) { // TODO Auto-generated method stub buffer.append(message); } public String toString() { return buffer.toString(); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 | package myspring.di.xml; public class ConsolePrinter implements Printer{ @Override public void print(String message) { System.out.println(message); } } | cs |
2. 설정 메타정보 XML 작성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?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="hello" class="myspring.di.xml.Hello"> <!-- setName(name) --> <property name="name" value="Spirng"/> <!-- setPrinter(Printer) --> <property name="printer" ref="printer"/> </bean> <bean id="printer" class="myspring.di.xml.StringPrinter" /> <bean id="consolePrinter" class="myspring.di.xml.ConsolePrinter" /> </beans> | cs |
3. DI 테스트 클래스 작성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package myspring.di.xml.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import myspring.di.xml.Hello; import myspring.di.xml.Printer; public class HelloBeanTest { public static void main(String[] args) { // TODO Auto-generated method stub // 1.IoC 컨테이너 생성 ApplicationContext context = new GenericXmlApplicationContext("config/beans.xml"); // 2.Hello Bean 가져오기 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.sayHello()); hello.print(); // 3.StringPrinter Bean 가져오기 Printer printer = context.getBean("printer", Printer.class); System.out.println(printer.toString()); Hello hello2 = context.getBean("hello", Hello.class); System.out.println(hello == hello2); } } | cs |
학습정리 Spinrg DI 용어 : 빈(Bean), 빈 팩토리(BeanFactory), 어플리케이션 컨텍스트(ApplicationContext), 설정 메타정보(Configuration Metadata) POJO 클래스 작성 : 의존관계가 있는 Java 클래스 작성 : Hello.java, Printer.java, StringPrinter.java, ConsolePrinter.java 설정 메타정보 XML 작성 : 빈 설정(Bean Configuration) XML 파일작성 - beans.xml DI테스트 클래스 작성 : DI컨테이너(ApplicationContext)를 사용한 테스트 클래스 작성 |
7강 DI 애플리케이션 작성(2)
1. jUnit의 개요와 특징
jUnit의 특징
- TDD의 창시자인 Kent Beck과 디자인 패턴 책의 저자인 Erich Gamma가 작성했다.
- 단정(assert) 메서드로 테스트 케이스의 수행 결과를 판별한다. 예) assertEquals(예상 값, 실제 값)
- jUnit4부터는 테스트를 지원하는 어노테이션을 제공한다. @Test @Before @After
- 각 @Test 메서드가 호출할 때 마다 새로운 인스턴스를 생성하여 독립적인 테스트가 이루어지도록 한다.
2. jUnit을 사용한 DI 테스트 클래스
jUnit 라이브러리 설치
- http://mvnrepository.com에 접근한다.
- jUnit으로 검색한다.
- jUnit 4.12버전을 pom.xml에 추가한다.
jUnit에서 테스트를 지원하는 어노테이션(Annotation)
- @Test
@Test가 선언된 메서드는 테스트를 수행하는 메소드가 된다.
jUnit은 각각의 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 원칙으로 하므로 @Test마다 객체를 생성한다.
- @Ignore
@Ignore가 선언된 메서드는 테스트를 실행하지 않게 한다.
- @Before
@Before가 선언된 메서드는 @Test 메소드가 실행되기 전에 반드시 실행되어 진다.
@Test 메소드에서 공통으로 사용하는 코드를 @Before 메소드에 선언하여 사용하면 된다.
- @After
@After가 선언된 메서드는 @Test 메소드가 실행된 후 실행된다.
- @BeforeClass
@BeforeClass 어노테이션은 @Test 메소드 보다 먼저 한번만 수행되어야 할 경우에 사용하면 된다.
- @AfterClass
@AfterClass 어노테이션은 @Test 메소드 보다 나중에 한번만 수행되어야 할 경우에 사용하면 된다.
테스트 결과를 확인하는 단정(assert) 메서드
- org.junit.Assert
+assertEquals(expected[A], actual[B])
객체 A와 B가 일치함을 확인한다.
+assertArrayEquals(expected[A], actual[B])
배열 A와 B가 일치함을 확인한다.
+asserSame(expected[A], actual[B])
객체 A와 B가 같은 객체임을 확인한다.
assertEquals 메서드는 두 객체의 값이 같은지 확인하고, assertSame 메서드는 두 객체의 레퍼런스가 동일한가를 확인한다.(==연산자)
+assertTrue(object[A])
조건 A가 참인가를 확인한다.
+assertNotNull(object[A])
객체 A가 null이 아님을 확인한다.
- 이외에도 다양한 assert 메서드가 존재함.
http://junit.sourceforge.net/javadoc/org/junit/Assert.html
jUnit을 사용한 DI 테스트 클래스(HelloBeanJunitTest.java) 작성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | package myspring.di.xml.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import myspring.di.xml.Hello; import myspring.di.xml.Printer; public class HelloBeanJUnitTest { private ApplicationContext context; @Before public void init() { context = new GenericXmlApplicationContext("config/beans.xml"); } @Test public void bean1() { Hello hello = (Hello) context.getBean("hello"); assertEquals("Hello Spring", hello.sayHello()); hello.print(); Printer printer = (Printer) context.getBean("printer"); assertEquals("Hello Spring", printer.toString()); } @Test public void bean2() { Printer printer = (Printer) context.getBean("printer"); Printer printer2 = context.getBean("printer", Printer.class); assertSame(printer, printer2); } } | cs |
3. Sprint-Test를 사용한 DI테스트 클래스
- http://mvnrepository.com에 접근한다.
- spring-test로 검색한다.
- Spring-test 3.2.17 버전을 pom.xml에 추가한다.
Spring-Test에서 테스트를 지원하는 어노테이션
- @RunWith(SpringJUnit4CLassRunner.class)
@RunWith는 jUnit 프레임워크의 테스트 실행방법을 확장할 때 사용하는 어노테이션이다.
SpringjUnit4ClassRunner라는 클래스를 지정해주면 jUnit이 테스트를 진행하는 중에 ApplicationContext를 만들고 관리하는 작업을 진행해 준다.
@RunWith 어노테이션은 각각의 테스트 별로 객체가 생성되더라도 싱글톤(Singleton)의 ApplicationContext를 보장한다.
- @ContextConfiguration
스프링 빈(Bean) 설정 파일의 위치를 지정할 때 사용되는 어노테이션이다.
- @Autowired
스프링DI에서 사용되는 특별한 어노테이션이다.
해당 변수에 자동으로 빈(Bean)을 매핑 해준다.
스프링 빈(Bean) 설정 파일을 읽기 위해 굳이 GenericXmlApplicationContext를 사용할 필요가 없다.
Spring-Test를 사용한 DI 테스트 클래스(HelloBeanSpringTest.java) 작성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package myspring.di.xml.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import myspring.di.xml.Hello; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:config/beans.xml") public class HelloBeanSpringTest { @Autowired private ApplicationContext context; @Test public void bean1() { Hello hello = (Hello) context.getBean("hello"); assertEquals("Hello Spring", hello.sayHello()); hello.print(); assertEquals(context.getBean("printer").toString(), "Hello Spring"); Hello hello2 = context.getBean("hello", Hello.class); hello2.print(); assertSame(hello, hello2); } } | cs |
학습정리 jUnit의 개요와 특징 - 단위테스트를 지원하는 프레임워크, assert 메서드를 사용하여 테스트 결과 확인 jUnit을 사용한 DI 테스트 클래스 - jUnit 설치, asset 메서드를 사용하여 테스트 결과 확인 - @Test, @Before 어노테이션 사용 Spring-Test를 사용한 DI 테스트 클래스 - Spring-Test 설치 - @RunWith(SpringJUnit4ClassRunner.class), @ContextConfiguration, @Autowired 어노테이션 사용 |
8강 DI 애플리케이션 작성(3)
1. Bean 의존관계 설정 방법
Setter Injection : <property> 태그
- Setter 메서드를 통해 의존관계가 있는 Bean을 주입하려면 <property> 태그를 사용할 수 있다.
- ref 속성은 사용하면 Bean 이름을 이용해 주입할 Bean을 찾는다.
- value 속성은 단순 값 또는 Bean이 아닌 객체를 주입할 때 사용한다.
Constructor Injection : <constructor-arg> 태그
- Constructor를 통해 의존관계가 있는 Bean을 주입하려면 <constructor-arg> 태그를 사용할 수 있다.
- Constructor 주입방식은 생성자의 파라미터를 이용하기 때문에 한번에 여러 개의 객체를 주입할 수 있다.
- 생성자 주입을 위한 설정 : ①index 지정, ②파라미터 이름 지정
POJO 클래스 수정 및 Bean 설정 파일 수정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package myspring.di.xml; public class Hello { private String name; private Printer printer; public Hello() { } public Hello(String name, Printer printer) { super(); this.name = name; this.printer = printer; } public void setName(String name) { this.name = name; } public void setPrinter(Printer printer) { this.printer = printer; } public String sayHello() { return "Hello " + name; } public void print() { this.printer.print(sayHello()); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?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="hello2" class="myspring.di.xml.Hello"> <constructor-arg index="0" value="Spring"/> <constructor-arg index="1" ref="printer"/> </bean> <bean id="hello" class="myspring.di.xml.Hello"> <!-- setName(name) --> <property name="name" value="Spring"/> <!-- setPrinter(Printer) --> <property name="printer" ref="printer"/> </bean> <bean id="printer" class="myspring.di.xml.StringPrinter" /> <bean id="consolePrinter" class="myspring.di.xml.ConsolePrinter" /> </beans> | cs |
2. 프로퍼티(Property) 값 설정 방법
- 단순 값(문자열이나 숫자)의 주입(Injection)
- Setter 메서드를 통해 Bean의 레퍼런스가 아니라 단순 값을 주입하려고 할 때는 <property> 태그의 value 속성을 사용한다.
- Spring은 List, Set, Map, Properties 와 같은 컬렉션 타입을 XML로 작성해서 프로퍼티에 주입하는 방법을 제공한다.
- List와 Set 타입 : <list>와 <value> 태그를 이용
- 프로퍼티가 Set 타입이면 <list> 대신에 <set>을 사용하면된다.
- 프로퍼티가 Map 타입이면 <map>과 <entry>태그를 이용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | package myspring.di.xml; import java.util.List; public class Hello { private String name; private Printer printer; private List<String> names; public Hello() { } public Hello(String name, Printer printer) { super(); this.name = name; this.printer = printer; } public List<String> getNames() { return names; } public void setNames(List<String> names) { this.names = names; } public void setName(String name) { this.name = name; } public void setPrinter(Printer printer) { this.printer = printer; } public String sayHello() { return "Hello " + name; } public void print() { this.printer.print(sayHello()); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <?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="hello2" class="myspring.di.xml.Hello"> <constructor-arg index="0" value="Spring"/> <constructor-arg index="1" ref="printer"/> <property name="names"> <list> <value>AOP</value> <value>Spring</value> <value>DI</value> </list> </property> </bean> <bean id="hello" class="myspring.di.xml.Hello"> <!-- setName(name) --> <property name="name" value="Spring"/> <!-- setPrinter(Printer) --> <property name="printer" ref="printer"/> </bean> <bean id="printer" class="myspring.di.xml.StringPrinter" /> <bean id="consolePrinter" class="myspring.di.xml.ConsolePrinter" /> </beans> | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package myspring.di.xml.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import java.util.List; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import jdk.management.resource.internal.inst.SocketOutputStreamRMHooks; import myspring.di.xml.Hello; import myspring.di.xml.Printer; public class HelloBeanJUnitTest { private ApplicationContext context; @Before public void init() { //IoC 컨테이너를 생성 //1.ApplicationContext 객체 생성 context = new GenericXmlApplicationContext("config/beans.xml"); } @Test public void bean1() { //2. getBean() 호출 Hello hello = (Hello) context.getBean("hello2"); //3.Hello의 sayHello() 호출 assertEquals("Hello Spring", hello.sayHello()); //3.Hello의 printer() 호출 hello.print(); assertEquals(3, hello.getNames().size()); List<String> list = hello.getNames(); for (String value : list) { System.out.println(value); } //StringPrinter getBean() Printer printer = (Printer) context.getBean("printer"); assertEquals("Hello Spring", printer.toString()); } @Test @Ignore public void bean2() { Printer printer = (Printer) context.getBean("printer"); Printer printer2 = context.getBean("printer", Printer.class); assertSame(printer, printer2); } } | cs |
3. 프로퍼티(Property) 파일을 이용한 설정 방법
환경에 따라 자주 변경되는 내용의 분리
- XML의 Bean 설정 메타정보는 애플리케이션 구조가 바뀌지 않으면 자주 변경되지 않는다.
- 반면에 프로퍼티 값으로 제공되는 일부 설정정보 (예-DataSource Bean이 사용하는 DB 연결정보)는 애플리케이션이 동작하는 환경(개발, 테스트, 스테이징, 운영)에 따라서 자주 바뀔 수 있다.
- 변경되는 이유와 시점이 다르다면 분리하는 것이 객체지향 설계의 기본 원칙이다. 설정에도 동일한 원칙을 적용할 수 있다.
- 환경에 따라 자주 변경될 수 있는 내용은 properties 파일로 분리하는 것이 가장 깔끔하다 XML 처럼 복잡한 구성이 필요 없고 키와 값의 쌍(key=value)으로 구성하면 된다.
환경에 따라 자주 변경되는 내용의 분리의 예시(1)
- value 속성에 설정된 값들은 환경에 따라 변경될 수 있는 내용이다.
- 자주 변경되는 값들은 properties 파일에 넣어 분리하는 것이 좋다.
1 2 3 4 5 6 | <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="com.sql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/testdb" /> <property name="username" value="spring" /> <property name="password" value="book" /> </bean> | cs |
- 프로퍼티 파일ㄹ로 분리한 정보는 ${}(프로퍼티 치환자)을 이용하여 설정한다.
- ${} 값을 치환해주는 기능은 <context:property-placeholder> 태그에 의해 자동으로 등록되는 PropertyPlaceHolderConfigurer Bean이 담당한다.
1 2 3 4 5 6 7 | <context:property-placeholder location="classpath:config/database.properties" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="${db.driverCLass}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean> | cs |
Bean 설정 파일 수정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?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-3.2.xsd"> <context:property-placeholder location="classpath:config/value.properties" /> <bean id="hello2" class="myspring.di.xml.Hello"> <constructor-arg index="0" value="${myname}" /> <constructor-arg index="1" ref="${myprinter}" /> <property name="names"> <list> <value>${value1}</value> <value>${value2}</value> <value>${value3}</value> </list> </property> </bean> <!-- <context:property-placeholder location="classpath:config/database.properties" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="${db.driverCLass}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="com.sql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/testdb" /> <property name="username" value="spring" /> <property name="password" value="book" /> </bean> --> <bean id="hello" class="myspring.di.xml.Hello"> <!-- setName(name) --> <property name="name" value="Spring" /> <!-- setPrinter(Printer) --> <property name="printer" ref="printer" /> </bean> <bean id="printer" class="myspring.di.xml.StringPrinter" /> <bean id="consolePrinter" class="myspring.di.xml.ConsolePrinter" /> </beans> | cs |
학습정리 Bean 의존관계 설정 방법 - <property>, <constructor-arg> 태그 프로퍼티(property) 값 설정 방법 - <property> 태그의 value 속성 - <list>, <set>, <map> 프로퍼티(property) 파일을 이용한 값 설정방법 - Properties 파일 작성, ${} 치환자 사용, <context:property-placeholder> |
9강 DI 애플리케이션 작성(4)
1. Bean 등록 메타정보 구성 전략
전략(1) XML 단독 사용
- 모든 Bean을 명시적으로 XML에 등록하는 방법이다.
- 생성되는 모든 Bean을 XML에서 확인할 수 있다는 장점이 있으나 Bean의 개수가 많아지면 XML 파일을 관리하기 번거로울 수 있다.
- 여러 개발자가 같은 설정파일을 공유해서 개발하다 보면 설정파일을 동시에 수정하다가 충돌이 일어나는 경우도 적지 않다.
- DI에 필요한 적절한 setter 메서드 또는 constructor가 코드 내에 반드시 존재해야 한다.
- 개발 중에는 어노테이션 설정방법을 사용했지만, 운영 중에는 관리의 편의성을 위해 XML설정으로 변경하는 전략을 쓸 수도 있다.
전략(2) XML과 빈 스캐닝(Bean Scanning)의 혼용
- Bean으로 사용될 클래스에 특별한 어노테이션(Annotation)을 부여해주면 이런 클래스를 자동으로 찾아서 Bean으로 등록한다.
- 특정 어노테이션이 붙은 클래스를 자동으로 찾아서 Bean으로 등록해주는 방식을 빈 스캐닝(Bean Scanning)을 통한 자동인식 Bean 등록기능이라고 한다.
- 어노테이션을 부여하고 자동 스캔으로 빈을 등록하면 XML문서 생성과 관리에 따른 수고를 덜어주고 개발 속도를 향상시킬 수 있다.
- 애플리케이션에 등록될 Bean이 어떤 것들이 있고, Bean들 간의 의존관계가 어떻게 되는지를 한눈에 파악할 수 없다는 단점이 있다.
2. Bean 등록 및 의존관계 설정 Annotation
Bean 등록 Annotation
- @Component
컴포넌트를 나타내는 일반적인 스테레오 타입으로 <bean>태그와 동일한 역할을 함
- @Repository
퍼시스턴스(persistence) 레이어, 영속성을 가지는 속성(파일, 데이터베이스)을 가진 클래스
- @Service
서비스 레이어, 비즈니스 로직을 가진 클래스
- @Controller
프리젠테이션 레이어, 웹 어플리케이션에서 웹 요청과 응답을 처리하는 클래스
- @Repository, @Service, @Controller는 더 특정한 유즈케이스에 대한 @Component의 구체화된 형태이다.
Bean 의존관계 주입 Annotation
- @Autowired
정밀한 의존관계 주입(Dependency Injection)이 필요한 경우에 유용하다.
@Autowired는 프로퍼티, setter 메서드, 생성자, 일반메서드에 적용 가능하다.
의존하는 객체를 주입할 때 주로 Type을 이용하게 된다.
@Autowired는 <property>,<constructor-arg> 태그와 동일한 역할을 한다.
- @Resource
어플리케이션에서 필요로 하는 자원을 자동 연결할 때 사용된다.
@Resource는 프로퍼티, setter 메서드에 적용 가능하다.
의존하는 객체를 주입할 때 주로 Name을 이용하게 된다.
- @Autowired는 타입으로, @Resource는 이름으로 연결한다는 점이 다르다.
- @Value
단순한 값을 주입할 때 사용되는 어노테이션이다.
@Value("Spring")은 <property ... value="Spring" /> 와 동일한 역할을 한다.
- @Qualifier
@Qualifier는 @Autowired 어노테이션과 같이 사용되어 진다.
@Autowired는 타입으로 찾아서 주입하므로, 동일한 타입의 Bean 객체가 여러 개 존재할 때 특정 Bean을 찾기 위해서는 @Qualifier를 같이 사용해야 한다.
Component Scan을 지원하는 태그
- <context:component-scan> 태그
@Component를 통해 자동으로 Bean을 등록하고, @Autowired로 의존관계를 주입받는 어노테이션을 클래스에서 선언하여 사용했을 경우에는 해당 클래스가 위치한 특정 패키지를 Scan하기 위한 설정을 XML에 해주어야 한다.
<context:component-scan base-package="myspring.di.annot" />
<context:include-filter> 태그와 <context:exclude-filter> 태그를 같이 사용하면 자동 스캔 대상에 포함시킬 클래스와 포함시키지 않을 클래스를 구체적으로 명시할 수 있다.
어노테이션을 사용한 POJO 클래스 작성
Hello | → | <interface> printer |
|
| ↗ | ↖ | |
annot.xml | String Printer |
| Console Printer |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package myspring.di.annot; import org.springframework.stereotype.Component; @Component("stringPrinter") public class StringPrinter implements Printer { private StringBuffer buffer = new StringBuffer(); @Override public void print(String message) { // TODO Auto-generated method stub buffer.append(message); } public String toString() { return buffer.toString(); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package myspring.di.annot; import org.springframework.stereotype.Component; @Component("consolePrinter") public class ConsolePrinter implements Printer{ @Override public void print(String message) { System.out.println(message); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | package myspring.di.annot; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class Hello { @Value("Spring") private String name; @Autowired @Qualifier("stringPrinter") private Printer printer; private List<String> names; public Hello() { } public Hello(String name, Printer printer) { super(); this.name = name; this.printer = printer; } public List<String> getNames() { return names; } public void setNames(List<String> names) { this.names = names; } // public void setName(String name) { // this.name = name; // } // // public void setPrinter(Printer printer) { // this.printer = printer; // } public String sayHello() { return "Hello " + name; } public void print() { this.printer.print(sayHello()); } } | cs |
1 2 3 4 5 6 7 8 9 | <?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-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <context:component-scan base-package="myspring.di.annot" /> </beans> | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package myspring.di.annot.test; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import myspring.di.annot.Hello; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:config/annot.xml") public class HelloBeanAnnotTest { @Autowired ApplicationContext context; @Test public void test() { Hello hello = context.getBean("hello",Hello.class); assertEquals("Hello Spring",hello.sayHello()); } } | cs |
3. 프로퍼티(Property) 파일을 이용한 설정 방법
Properties 파일 및 Bean 설정파일 작성
1 2 3 4 5 6 7 | myname=Spring myprinter=printer value1=AOP value2=Spring value3=DI printer1=stringPrinter printer2=consolePrinter | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | package myspring.di.annot; import java.util.List; import javax.annotation.Resource; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class Hello { @Value("${myname}") private String name; // @Autowired // @Qualifier("stringPrinter") @Resource(name = "${printer1}") private Printer printer; private List<String> names; public Hello() { } public Hello(String name, Printer printer) { super(); this.name = name; this.printer = printer; } public List<String> getNames() { return names; } public void setNames(List<String> names) { this.names = names; } // public void setName(String name) { // this.name = name; // } // // public void setPrinter(Printer printer) { // this.printer = printer; // } public String sayHello() { return "Hello " + name; } public void print() { this.printer.print(sayHello()); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 | <?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-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <context:component-scan base-package="myspring.di.annot" /> <context:property-placeholder location="classpath:config/value.properties"/> </beans> | cs |
학습정리 Bean 등록 메타정보 구성 전략 - XML 단독사용, XMl과 빈 스캐닝의 혼용 Bean 등록 및 의존관계 설정 Annotation - @Component, @Repository, @Service, @Controller - @Autowired, @Qualifier, @Value, @Resource 프로퍼티(property) 파일을 이용한 값 설정방법 - Properties 파일 작성, ${} 치환자 사용, <context:property-placeholder> |
10강 사용자 관리 프로젝트
1. 사용자 관리 프로젝트 아키텍처
아키텍처 개요
- 대부분의 중, 대규모 웹 애플리케이션은 효율적인 개발 및 유지보수를 위하여 계층화(Layering)하여 개발하는 것이 일반적이다.
- 사용자관리 프로젝트 아키텍쳐에서 기본적으로 가지는 계층은 프리젠테이션 계층(Presentation Layer), 서비스 계층(Service Layer), 데이터액세스 계층(Data Access Layer) 3계층과 모든 계층에서 사용되는 도메인 모델 클래스로 구성되어 있다.
- 각각의 계층은 계층마다 독립적으로 분리하여 구현하는 것이 가능해야 하며, 각 계층에서 담당해야 할 기능들이 있다.
프리젠테이션 계층 |
↔ |
서비스 계층 | ↔ | 데이터액세스 계층 |
← 도메인 모델 클래스 → |
- 위의 세 가지 계층은 독립적으로 분리할 수 있도록 구현해야 하며, 일반적으로 각 계층 사이에서는 인터페이스(Interface)를 이용하여 통신하는 것이 일반적이다.
- 프리젠테이션 계층
브라우저상의 웹크라이언트의 요청 및 응답을 처리
상위계층(서비스계층, 데이터 액세스계층)에서 발생하는 Exception에 대한 처리
최종 UI에서 표현해야 할 도메인 모델을 사용
최종 UI에서 입력한 데이터에 대한 유효성 검증(Validation) 기능을 제공
비즈니스 로직과 최종 UI를 분리하기 위한 컨트롤러 기능을 제공
@Controller 어노테이션을 사용하여 작성된 Controller 클래스가 이 계층에 속함
- 서비스 계층
애플리케이션 비즈니스 로직 처리와 비즈니스와 관련된 도메인 모델의 적합성 검증
트랜잭션(Transaction) 처리
프리젠테이션 계층과 데이터 액세스 계층 사이를 연결하는 역할로서 두 계층이 직접적으로 통신하지 않게 하여 애플리케이션의 유연성을 증가
다른 계층들과 통신하기 위한 인터페이스를 제공
Service 인터페이스와 @Service 어노테이션을 사용하여 작성된 Service구현 클래스가 이 계층에 속함
- 데이터 액세스 계층
영구 저장소(관계형 데이터베이스)의 데이터를 조작하는 데이터 액세스 로직을 객체화
영구 저장소의 데이터를 조회, 등록, 수정, 삭제 함
ORM(Object Relational Mapping) 프레임워크(MyBatis, Hibernate) 를 주로 사용하는 계층
DAO 인터페이스와 @Repository 어노테이션을 사용하여 작성된 DAO 구현 클래스가 이 계층에 속함
- 도메인 모델 클래스
관계형 데이터 베이스의 엔티티와 비슷한 개념을 가지는 것으로 실제 VO(Value Object) 혹은 DTO(Data Transfer Object) 객체에 해당
도메인 모델 클래스는 3개의 계층 전체에 걸쳐 사용
private으로 선언된 멤버변수가 있고, 그 변수에 대한 getter와 setter 메서드를 가진 클래스를 말함
2. 사용자 관리 프로젝트 클래스 설계
클래스의 역할
- 프리젠테이션 계층
UserController 클래스
UI계층과 서비스 게층을 연결하는 역할을 하는 클래스
JSP에서 UserController를 통해서 서비스 계층의 UserService를 사용하게 된다.
서비스 계층의 UserService 인터페이스를 구현하나 객체를 IoC 컨테이너가 주입해준다.
- 서비스 계층
UserService 인터페이스
서비스 계층에 속한 상위 인터페이스
UserServiceImpl 클래스
UserService 인터페이스를 구현한 클래스
복잡한 업무 로직이 있을 경우에는 이 클래스에서 업무 로직을 구현하면 된다.
데이터 액세스 계층의 UserDao 인터페이스를 구현한 객체를 IoC 컨테이너가 주입해준다.
- 데이터 액세스 계층
UserDao 인터페이스
데이터 액세스 계층에 속한 상위 인터페이스
UserDaoImplJDBC클래스
UserDao 인터페이스를 구현한 클래스로 이 클래스에서는 데이터 액세스 로직을 구현하면 된다.
SpringJDBC를 사용하는 경우에는 DataSource를 IoC 컨테이너가 주입해준다.
MyBatis를 사용하는 경우에는 SqlSession을 IoC 컨테이너가 주입해준다.
3. 사용자 관리 프로젝트 클래스 Code
학습정리 사용자 관리 프로젝트 아키텍쳐 - 프리젠테이션 계층, 서비스 계층, 데이터액세스 계층, 도메인 클래스 사용자 관리 프로젝트 클래스 설계 - 클래스 다이어그램, 각 클래스들의 역할 사용자 관리 프로젝트 클래스 Code - 각 클래스들의 Code 살펴보기 |
11강 Spring JDBC 개요
1. 데이터 액세스 공통 개념
DAO(Data Access Object) 패턴
- 데이터 액세스 계층은 DAO 패턴을 적용하여 비즈니스 로직과 데이터 액세스 로직을 분리하는 것이 원칙이다.
- 비즈니스 로직이 없거나 단순하면 DAO와 서비스 계층을 통합 할 수도 있지만 의미 있는 비즈니스 로직을 가진 엔터프라이즈 애플리케이션이라면 데이터 액세스 계층을 DAO 패턴으로 분리해야 한다.
- DAO 패턴은 서비스계층에 영향을 주지 않고 데이터 액세스 기술을 변경할 수 있는 장점을 가지고 있다.
컨넥션 풀링을 지원하는 DataSource
- 컨넥션 풀링은 미리 정해진 개수만큼의 DB 컨넥션을 풀(Pool)에 준비해두고, 애플리케이션이 요청할 때마다 Pool에서 꺼내서 하나씩 할당해주고 다시 돌려받아서 Pool에 넣는 식의 기법이다.
- 다중 사용자를 갖는 엔터프라이즈 시스템에서라면 반드시 DB컨넥션 풀링 기능을 지원하는 DataSource를 사용해야 한다.
- Spring에서는 DataSource를 공유 가능한 Spring Bean으로 등록해 주어 사용할 수 있도록 해준다.
DataSource 구현 클래스 종류
- 테스트 환경을 위한 DataSource
SimpleDriverDataSource
- Spring이 제공하는 가장 단순한 DataSource 구현 클래스이다.
- getConnection()을 호출할 때마다 매번 DB 컨넥션을 새로 만들고 따로 풀(pool)을 관리하지 않으므로 단순한 테스트용으로만 사용해야 한다.
SingleConnectionDriverDataSource
- 순차적으로 진행되는 통합 테스트에서는 사용 가능하다.
- 매번 DB 커넥션을 생성하지 않기 때문에 SimpleDriverDataSource 보다 빠르게 동작한다.
DataSOurce 종류
- 오픈소스 DataSource
Apache Commons DBCP
- 가장 유명한 오픈소스 DB커넥션 풀(pool) 라이브러리이다.
- Apache의 Commons 프로젝트(http://commons.apache.org/dbcp/
c3p0 JDBC/DataSource Resource Pool
- c3p0는 JDBC 3.0스펙을 준수하는 Connection과 Statement 풀(pool)을 제공하는 라이브러리이다.
- c3p0 웹 사이트 (http://www.mchange.com/projects/c3p0/)
두 가지 모두 수정자(setter) 메서드를 제공하므로 Spring Bean으로 등록해서 사용하기 편리하다.
2. Spring JDBC 설치 및 DataSource 설정
- JDBC란?
JDBC는 모든 자바의 데이터 액세스 기술의 근간이 된다.
엔티티 클래스와 애노테이션을 이용하는 최신 ORM 기술도 내부적으로는 DB와의 연동을 위해 JDBC를 이용한다.
안정적이고 유연한 기술이지만, 로우 레벨 기술로 인식되고 있다.
간단한 SQL을 실행하는 데도 중복된 코드가 반복적으로 사용되며, DB에 따라 일관성 없는 정보를 가진 채로 Checked Exception으로 처리한다.
장점 : 대부분의 개발자가 잘 알고 있는 친숙한 데이터 액세스 기술로 별도의 학습 없이 개발이 가능하다
단점 : Connection과 같은 공유 리소스를 제대로 릴리즈 해주지 않으면 시스템의 자원이 바닥나는 버그를 발생시킨다.
- Spring JDBC란?
JDBC의 장점과 단순성을 그대로 유지하면서도 기존 JDBC의 단점을 극복할 수 있게 해주고, 간결한 형태의 API 사용법을 제공하며, JDBC API에서 지원되지 않는 편리한 기능을 제공한다.
Spring JDBC는 반복적으로 해야 하는 많은 작업들을 대신 해준다.
Spring JDBC를 사용할 때는 실행할 SQL과 바인딩 할 파라미터를 넘겨 주거나, 쿼리의 실행 결과를 어떤 객체에 넘겨 받을지를 지정하는 것만 하면 된다.
Spring JDBC를 사용하려면 먼저, DB 컨넥션을 가져오는 DataSource를 Bean으로 등록해야 한다.
- Spring JDBC가 해주는 작업
Connection 열기와 닫기
- Connection과 관련된 모든 작업을 Spring JDBC가 필요한 시점에서 알아서 진행한다.
- 진행 중에 예외가 발생했을 때도 열린 모든 Connection 객체를 닫아준다.
Statement 준비와 닫기
- SQL 정보가 담긴 Statement 또는 PreparedStatement를 생성하고 필요한 준비 작업을 해주는 것도 Spring JDBC가 한다.
- Statement도 Connection과 마찬가지로 사용이 끝나고 나면 Spring JDBC가 알아서 객체를 닫아준다.
Statement 실행
- SQL 담긴 Statement를 실행하는 것도 Spring JDBC가 해준다.
- Statement의 실행결과는 다양한 형태로 가져올 수 있다.
ResultSet Loop 처리
- ResultSet에 담긴 쿼리 실행 결과가 한 건 이상이면 ResultSet 루프를 만들어서 반복해주는 것도 Spring JDBC가 해주는 작업이다.
Exception 처리와 반환
- JDBC 작업 중 발생하는 모든 예외는 Spring JDBC 예외 변환기가 처리한다.
- 체크 예외(Checked Exception)인 SQLException을 런타임 예외(Runtime Exception)인 DataAccessException 타입으로 변환한다.
Transaction 처리
- Spring JDBC를 사용하면 transaction과 관련된 모든 작업에 대해서는 신경 쓰지 않아도 된다.
3. Spring JDBC의 JdbcTemplate 클래스
- JdbcTemplate 클래스
Spring JDBC가 제공하는 클래스 중 JdbcTemplate은 JDBC의 모든 기능을 최대한 활용할 수 있는 유연성을 제공하는 클래스이다.
JdbcTemplate이 제공하는 기능은 실행, 조회, 배치의 세 가지 작업이다.
- 실행 : Insert나 Update같이 DB의 데이터에 변경이 일어나는 쿼리를 수행하는 작업
- 조회 : Select를 이용해 데이터를 조회하는 작업
- 배치 : 여러 개의 쿼리를 한 번에 수행해야 하는 작업
- JdbcTemplate 클래스 생성
JdbcTemplate은 DataSource를 파라미터로 받아서 아래와 같이 생성할 수 있다.
JdbcTemplate template =new JdbcTemplate(dataSource);
DataSource는 보통 Bean으로 등록해서 사용하므로 JdbcTemplate이 필요한 DAO 클래스에서 DataSource Bean을 DI(의존관계 주입) 받아서 JdbcTemplate을 생성할 때 인자로 넘겨주면 된다.
JdbcTemplate은 멀티스레드 환경에서도 안전하게 공유해서 쓸 수 있기 때문에 DAO 클래스의 인스턴스 변수에 저장해 두고 사용할 수 있다.
- JdbcTemplate 클래스 생성 Code
아래의 코드는 일반적으로 사용되는 DAO 클래스의 기본구조이다.
DataSource에 대한 수정자 메서드에서 직접 JdbcTemplate객체를 생성해준다.
1 2 3 4 5 6 7 8 9 | public class UserDAOJdbc { JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource) { jdbTemplate = new JdbcTemplate(dataSource); } ... } | cs |
- JdbcTemplate 클래스의 update() 메서드
INSERT, UPDATE, DELETE와 같은 SQL을 실행할 때는 JdbcTemplate의 update() 메서드를 사용한다.
int update(String sql, [SQL 파라미터])
update() 메서드를 호출할 때는 SQL과 함께 바인딩 할 파라미터는 Object 타입 가변인자 (Object ... args)를 사용할 수 있다.
update() 메소드의 리턴되는 값은 SQL 실행으로 영향을 받은 레코드의 개수를 리턴한다.
- JdbcTemplate 클래스의 update() 메서드 Code
1 2 3 4 5 6 7 8 9 10 | public int update(User user) { StringBuffer updateQuery = new StringBuffer(); updateQuery.append("UPDATE USERS SET "); updateQuery.append("password=?, name=? "); updateQuery.append("WHERE id=?"); int result = this.jdbcTemplate.update(updateQuery.toString(), user.getName(), user.getPassword(), user.getId() ); return result; } | cs |
- JdbcTemplate 클래스의 queryForObject() 메서드
SELECT SQL을 실행하여 하나의 Row를 가져올 때는 JdbcTemplate의 queryForObject() 메서드를 사용ㅎ나다.
<T> T queryForObject(String sql, pSQL 파라미터], RowMapper<T> rm)
SQL 실행 결과는 여러 개의 칼럼(Column)을 가진 하나의 로우(Row)
T는 VO 객체의 타입에 해당된다.
SQL 실행 결과로 돌아온 여러 개의 column을 가진 한 개의 Row를 RowMapper 콜백을 이용해 VO객체로 매핑 해준다.
- JdbcTemplate 클래스의 queryForObject() 메서드 Code
1 2 3 4 5 6 7 8 9 10 11 12 13 | public User findUser (String id) { return this.jdbcTemplate.queryForObject( "select * from users where id=?", new Object[] {id}, new RowMapper<User>() { public User mapRow(ResultSet rs, int rowNum) throws SQLException{ User user = new User(); user.setId(rs.getString("id")); user.setName(rs.getString("name")); user.setPassword(rs.getString("password")); return user; } }//RowMapper );//queryForObject }//findUser | cs |
- JdbcTemplate 클래스의 query() 메서드
SELECT SQL을 실행하여 여러 개의 Row를 가져올 떄는 JdbcTemplate의 query() 메서드를 사용한다.
<T> List<T> query(String sql, [SQL 파라미터], RowMapper<T> rm)
SQL 실행 결과로 돌아온 여러 개의 column을 가진 여러 개의 Row를 RowMapper 콜백을 이용해 VO 객체로 매핑 해준다.
결과 값은 매핑 한 VO 객체를 포함하고 있는 List 형태로 받는다. List의 각 요소가 하나의 Row에 해당된다.
학습정리 데이터 액세스 공통개념 - DAO 패턴, 컨넥션 풀링, DataSource Spring JDBC 개요 - JDBC 개요 및 장단점 - Spring JDBC 개요 및 역할 Spring JDBC의 JdbcTemplate 클래스 - JdbcTemplate 클래스의 update(), queryForObject(), query() 메서드 |
12강 Spring JDBC
1. DB 설정 및 JDBC Driver 설치
DBA 권한으로 접속
- SQL Command 실행
- DBA권한으로 접속
conn sys as sysdba; 입력
password는 설치할 때 지정한 oracle11
- scott 계정 생성
create user scott identified by tiger default tablespace users temporary tablespace temp;
- scott 계정에 권한 주기
grant connect, resource to scott;
DB users 테이블 생성
- scott 계정으로 접속 후 users 테이블 생성
conn scott/tiger
start c:\springframework\user.sql;
Oracle Jdbc Driver 라이브러리 검색 및 설치
- http://mvnrepository.com에 접근한다.
- oracle ojdbc6로 검색한다.
- oracle jdbc 12.1.0.1 버전을 pom.xml에 추가한다.
2. Spring JDBC 설치 및 DataSource 설정
Spring JDBC 설치
- http://mvnrepository.com에 접근한다.
- spring jdbc로 검색한다.
- oracle jdbc 3.2.17 버전을 pom.xml에 추가한다.
DataSource를 Spring Bean으로 등록하여 사용할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 | myname=Spring myprinter=printer value1=AOP value2=Spring value3=DI printer1=stringPrinter printer2=consolePrinter db.driverClass=oracle.jdbc.OracleDriver db.url=jdbc:oracle:thin:@127.0.0.1:1521:xe db.username=scott db.password=tiger | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <?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-3.2.xsd"> <context:property-placeholder location="classpath:config/value.properties" /> <!-- DataSource 설정 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="${db.driverClass}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean> <bean id="hello2" class="myspring.di.xml.Hello"> <constructor-arg index="0" value="${myname}" /> <constructor-arg index="1" ref="${myprinter}" /> <property name="names"> <list> <value>${value1}</value> <value>${value2}</value> <value>${value3}</value> </list> </property> </bean> <!-- <context:property-placeholder location="classpath:config/database.properties" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="${db.driverCLass}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="com.sql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/testdb" /> <property name="username" value="spring" /> <property name="password" value="book" /> </bean> --> <bean id="hello" class="myspring.di.xml.Hello"> <!-- setName(name) --> <property name="name" value="Spring" /> <!-- setPrinter(Printer) --> <property name="printer" ref="printer" /> </bean> <bean id="printer" class="myspring.di.xml.StringPrinter" /> <bean id="consolePrinter" class="myspring.di.xml.ConsolePrinter" /> </beans> | cs |
3. 사용자관리 프로젝트 실행
사용자관리 프로젝트의 Bean 등록 및 의존관계 설정
- <context:component-scan> 태그 사용
- @Service, @Repository 어노테이션을 선언한 클래스들과 @Autowired 어노테이션을 선언하여 의존관계를 설정한 클래스들이 위치한 패키지를 Scan 하기 위한 설정을 XML에 해주어야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <?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-3.2.xsd"> <context:property-placeholder location="classpath:config/value.properties" /> <!-- component scan 설정 --> <context:component-scan base-package="myspring.user" /> <!-- DataSource 설정 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="${db.driverClass}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean> <bean id="hello2" class="myspring.di.xml.Hello"> <constructor-arg index="0" value="${myname}" /> <constructor-arg index="1" ref="${myprinter}" /> <property name="names"> <list> <value>${value1}</value> <value>${value2}</value> <value>${value3}</value> </list> </property> </bean> <!-- <context:property-placeholder location="classpath:config/database.properties" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="${db.driverCLass}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.username}" /> <property name="password" value="${db.password}" /> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="com.sql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/testdb" /> <property name="username" value="spring" /> <property name="password" value="book" /> </bean> --> <bean id="hello" class="myspring.di.xml.Hello"> <!-- setName(name) --> <property name="name" value="Spring" /> <!-- setPrinter(Printer) --> <property name="printer" ref="printer" /> </bean> <bean id="printer" class="myspring.di.xml.StringPrinter" /> <bean id="consolePrinter" class="myspring.di.xml.ConsolePrinter" /> </beans> | cs |
DataSource 설정 테스트
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | package myspring.user.test; import static org.junit.Assert.assertEquals; import java.sql.SQLException; import javax.sql.DataSource; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import myspring.user.service.UserService; import myspring.user.vo.UserVO; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:config/beans.xml") public class UserClient { @Autowired ApplicationContext context; @Autowired UserService service; @Test @Ignore public void updateUserTest() { service.updateUser(new UserVO("gildong", "홍길동2", "남2", "서울2")); UserVO user = service.getUser("gildong"); System.out.println(user); } @Test @Ignore public void insertUserTest() { service.insertUser(new UserVO("dooly", "둘리", "남", "경기")); for (UserVO user : service.getUserList()) { System.out.println(user); } } @Test @Ignore public void getUserTest() { UserVO user = service.getUser("gildong"); System.out.println(user); assertEquals("홍길동", user.getName()); } @Test @Ignore public void dataSourceTest() { DataSource ds = (DataSource) context.getBean("dataSource"); try { System.out.println(ds.getConnection()); } catch (SQLException e) { e.printStackTrace(); } } @Test @Ignore public void deleteUserTest() { service.deleteUser("dooly"); for (UserVO user : service.getUserList()) { System.out.println(user); } } } | cs |
학습정리 DB 설정 및 JDBC Driver 설치 - DB 접속 계정 생성 및 테이블 생성 - Oracle JDBC Driver 설치 Spring JDBC 설치 및 DataSource 설정 - Spring JDBC 라이브러리 설치 - DataSource 설정 사용자관리 프로젝트 테스트 - 사용자 관리 프로젝트 실행 - 사용자 조회, 등록, 목록조회, 수정, 삭제 테스트 |
'Legend 개발자 > T아카데미' 카테고리의 다른 글
모바일 GUI 디자인 (0) | 2017.09.05 |
---|---|
UX/UI 기획 (0) | 2017.09.04 |
jQuery (JavaScript) (0) | 2017.09.02 |
컴퓨터 알고리즘 중급 (0) | 2017.08.31 |
JavaScript (0) | 2017.08.30 |