Spring 프레임워크의 이해 – day2 자바지기
첫째 날 둘째 날 셋째 날 개발 환경 세팅 Spring 기본 Spring MVC 사용자 관리 시스템 개발 (실습) Spring DI Spring AOP Spring JDBC & Transaction Spring Test
Spring은 Lightweight Application Framework
Spring Core : Spring 프레임워크의 근간이 되는 IoC(또는 DI) 기능을 지원하는 영역을 담당하고 있다 Spring Core : Spring 프레임워크의 근간이 되는 IoC(또는 DI) 기능을 지원하는 영역을 담당하고 있다. BeanFactory를 기반으로 Bean 클래스들을 제어할 수 있는 기능을 지원한다. Spring Context : Spring Core 바로 위에 있으면서 Spring Core에서 지원하는 기능외에 추가적인 기능들과 좀 더 쉬운 개발이 가능하도록 지원하고 있다. 또한 JNDI, EJB등을 위한 Adaptor들을 포함하고 있다.
Spring DAO : 지금까지 우리들이 일반적으로 많이 사용해왔던 JDBC 기반하의 DAO개발을 좀 더 쉽고, 일관된 방법으로 개발하는 것이 가능하도록 지원하고 있다. Spring DAO를 이용할 경우 지금까지 개발하던 DAO보다 적은 코드와 쉬운 방법으로 DAO를 개발하는 것이 가능하다. Spring ORM : Object Relation Mapping 프레임워크인 Hibernate, IBatis, JDO와의 결합을 지원하기 위한 기능이다. Spring ORM을 이용할 경우 Hibernate, IBatis, JDO 프레임워크와 쉽게 통합하는 것이 가능하다.
Spring AOP : Spring 프레임워크에 Aspect Oriented Programming을 지원하는 기능이다 Spring AOP : Spring 프레임워크에 Aspect Oriented Programming을 지원하는 기능이다. 이 기능은 AOP Alliance 기반하에서 개발되었다. Spring Web : Web Application 개발에 필요한 Web Application Context와 Multipart Request등의 기능을 지원한다. 또한 Struts, Webwork와 같은 프레임워크의 통합을 지원하는 부분을 담당한다. Spring Web MVC : Spring 프레임워크에서 독립적으로 Web UI Layer에 Model-View-Controller를 지원하기 위한 기능이다. 지금까지 Struts, Webwork가 담당했던 기능들을 Spring Web MVC를 이용하여 대체하는 것이 가능하다. 또한 Velocity, Excel, PDF와 같은 다양한 UI 기술들을 사용하기 위한 API를 제공하고 있다.
첫째 날 둘째 날 셋째 날 개발 환경 세팅 Spring 기본 Spring MVC 사용자 관리 시스템 개발 (실습) Spring DI Spring AOP Spring JDBC & Transaction Spring Test
Spring DI
Inversion of Control? Dependency Injection?
요구사항 하나의 Interface 기반하에서 “Hello World!”와 “Hi World!” 메시지를 출력해야 한다. 출력하는 메시지를 생성하는 부분과 생성된 메시지를 Rendering하는 부분이 분리되어야 한다.
요구사항 변경 “Hello World!”와 “Hi World!!” 메시지외에 “안녕 World!”를 출력해야 한다. 출력하는 메시지를 생성하는 부분과 생성된 메시지를 Rendering하는 부분이 분리되어야 한다. 생성된 메시지를 단순히 출력하는 기능과 인자로 전달된 “Name”을 더하여 가공한 메시지를 출력해야 한다.
Factory Pattern
Factory Pattern
Spring
요구사항 전달하는 메시지가 “How”이면 “How are you?”를 출력한다. 전달하는 메시지가 “What”이면 “What’s your name?”를 출력한다. 전달하는 메시지가 “How”또는 “What”이 아니면 IllegalArgumentException을 Throw한다.
How?
(ApplicationContext) 클래스 의존관계에 대한 Metadata (XML 또는 Property) Spring Framework (ApplicationContext)
Inversion of Control (IoC)
DP IoC Setter Inj DI Constructor Inj Method Inj IoC : Inversion of Control DI : Dependency Injection DP : Dependency Pull EJB Spring DP IoC Setter Inj DI Constructor Inj Spring PicoContainer Method Inj
Servlet Container Servlet A Servlet B Servlet C Servlet D Service Init Create Destory Servlet Container Servlet A Servlet B Servlet C Servlet D
EJB Container EJB A EJB B EJB C EJB D Service Init Create Destory
IoC(DI) Container POJO A POJO B POJO C POJO D Service Init Create Destory IoC(또는 DI) Container POJO A POJO B POJO C POJO D
Spring Bean Scope
Singleton?, Non Singleton?
Singleton Pattern을 활용한 Singleton
지금까지의 Singleton 구현 방법 public class MessageService { private static MessageService instance; private MessageService() {} public static MessageService getInstance() { if(instance == null ) { instance = new MessageService(); } return instance;
Spring 기반하의 Singleton
Spring 프레임워크의 Singleton 구현 방법 <bean id="annyoungWorldMessageProvider" class="net.javajigi.ioc.AnnyoungWorldMessageProvider" scope=“singleton” /> <bean id="helloWorldMessageProvider" class="net.javajigi.ioc.HelloworldMessageProvider"/>
<bean id="renderer" class="net. javajigi. ioc <bean id="renderer" class="net.javajigi.ioc.DefaultMessageRenderer"> <property name="messageProvider"> <ref local=“hiWorldMessageProvider" /> </property> </bean> <bean id="hiWorldMessageProvider" class="net.javajigi.ioc.HiworldMessageProvider" /> ApplicationContext 키(key) 값(value) “renderer " DefaultMessageRenderer 인스턴스 " hiWorldMessageProvider” HiWorldMessageProvider 인스턴스
웹 애플리케이션 ServletContext(sigle instance) JVM 키(key) 값(value) “org.springframework.web.context. WebApplicationContext.ROOT" WebApplicationContext 인스턴스 JVM
Spring 프레임워크의 Non Singleton 구현 방법 <bean id="annyoungWorldMessageProvider" class="net.javajigi.ioc.AnnyoungWorldMessageProvider" scope=“prototype” /> <bean id="helloWorldMessageProvider" class="net.javajigi.ioc.HelloworldMessageProvider“
Singleton 인스턴스를 사용해야 할 때
Singleton Instance Thread A Thread B name = null public class Person { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; new Person(); Thread A new Person(); Thread B
person.setName(“예은”); Singleton Instance name = “예은” public class Person { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; person.setName(“예은”); Thread A
person.setName(“주한”); Singleton Instance name = “주한” public class Person { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; person.setName(“주한”); Thread B
person.getName(); => 주한 Singleton Instance name = “주한” public class Person { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; person.getName(); => 주한 Thread A
Static을 사용한 Singleton의 문제점 Dependency가 높아진다. Singleton 클래스마다 서로 다른 Configuration 가질 수 있다. interface-unfriendly => Test의 어려움. 상속하기 힘들다. Runtime시에 Singleton의 상태를 변경할 수 없다. OOP적으로 개발하는데 한계가 있다.
Spring Bean Instance 생성
new <bean id="messageProvider" class="net.javajigi.di.NewMessageProvider"/>
<bean id="messageProvider" Factory method <bean id="messageProvider" class="net.javajigi.di.SingletonMessageProvider" factory-method="getInstance" />
FactoryBean Interface <bean id="messageProvider" class="net.javajigi.di.MessageProviderFactoryBean"> <property name="type"> <util:constant static-field="net.javajigi.di.MessageProviderFactoryBean.ANNYOUNG_WORLD_PROVIDER"/> </property> </bean>
<bean id="userService" class="net. javajigi. user. service <bean id="userService" class="net.javajigi.user.service.UserServiceImpl"> <property name="userDAO"> <ref local="userDAO" /> </property> </bean> ApplicationContext context = new ClassPathXmlApplicationContext(paths); UserService userService = context.getBean(“userService”);
<bean id="userService" class="org. springframework. aop. framework <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref local="userServiceTarget" /> </property> <property name="interceptorNames"> <list> <value>loggingAdvice</value> <value>emailNotificationThrowsAdvice</value> </list> </bean> ApplicationContext context = new ClassPathXmlApplicationContext(paths); ProxyFactoryBean factoryBean = context.getBean(“userService”);
<bean id="userService" class="org. springframework. aop. framework <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref local="userServiceTarget" /> </property> <property name="interceptorNames"> <list> <value>loggingAdvice</value> <value>emailNotificationThrowsAdvice</value> </list> </bean> ApplicationContext context = new ClassPathXmlApplicationContext(paths); UserService userService = context.getBean(“userService”);
첫째 날 둘째 날 셋째 날 개발 환경 세팅 Spring 기본 Spring MVC 사용자 관리 시스템 개발 (실습) Spring DI Spring AOP Spring JDBC & Transaction Spring Test
Spring JDBC
Spring JDBC를 이용할 때 개발자들이 구현할 부분 작업 Spring JDBC 개발자 Connection 관리 O X SQL Statement 관리 ResultSet 관리 Row 데이터 추출 패러미터 선언 패러미터 Setting 트랜잭션 관리
Template Method Pattern
public abstract class AbstractClass { public void templateMethod() { // .. 비지니스 로직 구현 operation1(); operation2(); } protected abstract void operation1(); protected abstract void operation2();
public class ConcreteClassA extends AbstractClass { protected void operation1() { // TODO Auto-generated method stub } protected void operation2() {
Template Method = IoC
Callback Class Callback Method
public interface RowCallbackHandler { void processRow(ResultSet rs) throws SQLException; }
public void query(String sql, RowCallbackHandler callbackHandler) throws JdbcSqlException { Connection con = null; PreparedStatement ps = null; ResultSet rs = null; try { con = <code to get connection> ps = con.prepareStatement (sql); rs = ps.executeQuery(); while (rs.next()) { callbackHandler.processRow(rs); } rs.close(); ps.close(); } catch (SQLException ex) { throw new JdbcSqlException("Couldn't run query [" + sql + "]", ex); finally { DataSourceUtils.closeConnectionIfNecessary(this.dataSource, con);
class StringHandler implements JdbcTemplate.RowCallbackHandler { private List 1 = new LinkedList(); public void processRow(ResultSet rs)throws SQLException { 1.add(rs.getString(1)); } public String[] getStrings() { return (String[]) 1.toArray(new String[1.size()]); StringHandler sh = new StringHandler(); jdbcTemplate.query("SELECT FORENAME FROM CUSTMR", sh); String[] forenames = sh.getStrings();
Spring Transaction
Where? Persistence Layer Business Layer
TXa TXa TXa
TXa TXa TXa TXa TXb TXb
격리 레벨(Isolation Level) 상세 설명 TransactionDefinition.ISOLATION_DEFAULT 개별적인 PlatformTransactionManager를 위한 디폴트 격리레벨 TransactionDefinition.ISOLATION_READ_UNCOMMITTED 격리레벨중 가장 낮은 격리 레벨이다. 이 격리레벨은 다른 Commit되지 않은 트랜잭션에 의해 변경된 데이터를 볼 수 있기 때문에 거의 트랜잭션의 기능을 수행하지 않는다. TransactionDefinition.ISOLATION_READ_COMMITTED 대개의 데이터베이스에서의 디폴트로 지원하는 격리 레벨이다. 이 격리 레벨은 다른 트랜잭션에 의해 Commit되지 않은 데이터는 다른 트랜잭션에서 볼 수 없도록 한다. 그러나 개발자들은 다른 트랜잭션에 의해 입력되거나 수정된 데이터는 조회할 수는 있다. TransactionDefinition.ISOLATION_REPEATABLE_READ ISOLATION_READ_COMMITED 보다는 다소 조금 더 엄격한 격리레벨이다. 이 격리레벨은 다른 트랜잭션이 새로운 데이터를 입력했다면, 새롭게 입력된 데이터를 조회할 수 있다는 것을 의미한다. TransactionDefinition.ISOLATION_SERIALIZABLE 가장 많은 비용이 들지만 실뢰할만한 격리레벨을 제공하는 것이 가능하다. 이 격리레벨은 하나의 트랜잭션이 완료된 후에 다른 트랜잭션이 실행하는 것처럼 지원한다.
TXa TXb
? TXa TXb TXa SELECT CATEGORYNAME FROM ORDER WHERE ORDERID = 1 “BOOK” Result “BOOK” UPDATE ORDER SET CATEGORYNAME=“MUSIC” WHERE ORDERID = 1 TXb TXa SELECT CATEGORYNAME FROM ORDER WHERE ORDERID = 1 Result ?
Dirty Read TXa SELECT CATEGORYNAME FROM ORDER WHERE ORDERID = 1 Result “BOOK” UPDATE ORDER SET CATEGORYNAME=“MUSIC” WHERE ORDERID = 1 TXb TXa SELECT CATEGORYNAME FROM ORDER WHERE ORDERID = 1 Result “MUSIC”
NonRepeatable Read(Fuzzy Read) – Update, Delete TXa SELECT CATEGORYNAME FROM ORDER WHERE ORDERID = 1 Result “BOOK” UPDATE ORDER SET CATEGORYNAME=“MUSIC” WHERE ORDERID = 1 TXb TXa SELECT CATEGORYNAME FROM ORDER WHERE ORDERID = 1 Result “BOOK”
NonRepeatable Read(Fuzzy Read) – Update, Delete Commit TXb TXa SELECT CATEGORYNAME FROM ORDER WHERE ORDERID = 1 Result “MUSIC”
Phantom Read - Insert TXa TXb TXa SELECT COUNT(*) FROM ORDER Result 10 INSERT INTO ORDER (ORDERID, CATEGORYNAME) VALUES ( 11, “GAME” TXb TXa SELECT COUNT(*) FROM ORDER Result 10
Phantom Read - Insert TXb TXa Commit TXb TXa SELECT COUNT(*) FROM ORDER Result 11
Isolation Level Dirty Read NonRepetable Read Phantom Read Read uncommitted (ISOLATION_READ_UNCOMMITTED) Possible Read committed (ISOLATION_READ_COMMITTED) Not Possible Repetable read (ISOLATION_REPEATABLE_READ) Serializable (ISOLATION_SERIALIZABLE)
전달 행위(Propagation Behavior) 상세설명 TransactionDefinition.PROPAGATION_REQUIRED 이미 하나의 트랜잭션이 존재한다면 그 트랜잭션을 지원하고, 트랜잭션이 없다면, 새로운 트랜잭션을 시작한다. TransactionDefinition.PROPAGATION_SUPPORTS 이미 트랜잭션이 존재한다면 그 트랜잭션을 지원하고, 트랜잭션이 없다면 비-트랜잭션 형태로 수행한다. TransactionDefinition.PROPAGATION_MANDATORY 이미 트랜잭션이 존재한다면 그 트랜잭션을 지원하고 활성화된 트랜잭션이 없다면 예외를 던진다. TransactionDefinition.PROPAGATION_REQUIRES_NEW 언제나 새로운 트랜잭션을 시작한다. 이미 활성화된 트랜잭션이 있다면, 일시정지한다. TransactionDefinition.PROPAGATION_NOT_SUPPORTED 활성화된 트랜잭션을 가진 수행을 지원하지 않는다. 언제나 비-트랜잭션적으로 수행하고 존재하는 트랜잭션은 일시정지한다. TransactionDefinition.PROPAGATION_NEVER 활성화된 트랜잭션이 존재하더라도 비-트랜잭션적으로 수행한다. 활성화된 트랜잭션이 존재한다면 예외를 던진다. TransactionDefinition.PROPAGATION_NESTED 활성화된 트랜잭션이 존재한다면 내포된 트랜잭션으로 수행된다. 작업수행은 TransactionDefinition.PROPAGATION_REQUIRED으로 셋팅된것처럼 수행된다.
PROPAGATION_REQUIRED PROPAGATION_REQUIRED TXa TXa TXa TXa PROPAGATION_REQUIRED
PROPAGATION_REQUIRED PROPAGATION_REQUIRES_NEW TXa TXa TXb TXb PROPAGATION_REQUIRES_NEW
PROPAGATION_REQUIRED PROPAGATION_SUPPORTS TXa TXa TXa TXa PROPAGATION_SUPPORTS
PROPAGATION_SUPPORTS
PROPAGATION_REQUIRED PROPAGATION_NOT_SUPPORTED TXa TXa PROPAGATION_NOT_SUPPORTED
PROPAGATION_REQUIRED PROPAGATION_MADATORY TXa TXa TXa TXa PROPAGATION_MADATORY
PROPAGATION_MADATORY Throw Exception PROPAGATION_MADATORY
PROPAGATION_REQUIRED TXa TXa Throw Exception PROPAGATION_NEVER
전달 행위 : 필수 값으로서 Spring 프레임워크 전달행위 중의 하나를 사용할 수 있다. (필수) readOnly 유무 (선택) PROPAGATION, ISOLATION, readOnly, -Exceptions, +Exceptions 격리 레벨 (선택) Rollback 규칙 (선택) 전달 행위 : 필수 값으로서 Spring 프레임워크 전달행위 중의 하나를 사용할 수 있다. 격리 레벨 : 선택 값으로 Spring 프레임워크 격리 레벨 중의 하나를 사용할 수 있다. readOnly : 선택 값으로 실행하는 트랜잭션이 읽기 전용일 경우에 사용가능하다. 일반적으로 트랜잭션 내에서 SELECT 쿼리만을 실행하는 경우에 이 속성을 사용한다. Rollback 규칙 : Spring 프레임워크 트랜잭션의 디폴트 설정은 RuntimeException이 발생할 경우에는 Rollback, CheckedException이 발생하는 경우에는 Commit되도록 하고 있다. 그러나 트랜잭션의 속성을 지정할 때 Rollback 규칙을 이용하여 디폴트 설정을 변경하는 것이 가능하다. [그림 5-2]의 Rollback 규칙에서 마이너스(-)로 시작하는 Exception에 대해서는 무조건 Rollback, 플러스(+)로 시작하는 Exception은 무조건 Commit되도록 규칙을 변경할 수 있다.
PROPAGATION_REQUIRED, readOnly, timeout_0180, -ApplicationException
첫째 날 둘째 날 셋째 날 개발 환경 세팅 Spring 기본 Spring MVC 사용자 관리 시스템 개발 (실습) Spring DI Spring AOP Spring JDBC & Transaction Spring Test
학습 목표 기존 테스트 방식의 문제점 Spring 프레임워크에서 제시하는 해결 방법은?
컨테이너 기반 테스트 Cycle Test Development Server 재시작 Build Deploy
컨테이너 기반 테스트 Cycle의 문제점 한 Cycle에 소요되는 시간이 너무 길다. 최소 1분에서 5분 테스트를 자동화하기 힘들다.
Junit 기반 테스트 Cycle Test Development
org.springframework.test
기존의 테스트 방식 public class StandardOutMessageRendererTest { private MessageRenderer renderer; @Before public void setUp() throws Exception { ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "net/javajigi/helloworld/HelloWorld.xml"); renderer = (MessageRenderer) applicationContext.getBean("renderer"); } @Test public void testRender() { renderer.render();
AbstractDependencyInjectionSpringContextTests public class StandardOutMessageRendererWithDITest extends AbstractDependencyInjectionSpringContextTests { private MessageRenderer renderer; public void setRenderer(MessageRenderer renderer) { this.renderer = renderer; } @Override protected String[] getConfigLocations() { return new String[] { "classpath:net/javajigi/helloworld/HelloWorld.xml" }; public void testRender() { renderer.render();
테스트를 자동화할 수 없다. 데이터베이스 테스트의 문제점 테스트시 같은 데이터를 추가할 경우 Duplicate Exception 발생. 데이터의 상태를 변경으로 인해 반복 테스트가 힘들다. 테스트를 위한 쓰레기 데이터가 쌓인다. 테스트를 자동화할 수 없다.
테스트를 위해 추가적인 작업이 필요함 개발 생산성 저하 지금까지의 해결방법 테스트 종료시 추가한 데이터를 삭제한다. 테스트 종료시 이전 상태로 원복한다. 테스트를 위해 추가적인 작업이 필요함 개발 생산성 저하
테스트시 변경된 데이터를 자동 Rollback AbstractTransactionalSpringContextTests 테스트시 변경된 데이터를 자동 Rollback
beginTx Tx Business Logic Success 예 Commit 아니오 Rollback
beginTx Tx Unit Test Default 자동 Rollback
AbstractTransactionalDataSourceSpringContextTests 테스트를 위한 Query 구현이 가능
Spring Mock Object Mock Object ?
MockHttpServletRequest MockHttpServletResponse
Thank you.