MVNO패키지 개발 Wiki

 

Spring

Page history last edited by Anonymous 3 yrs ago
  • Spring framework Workbook 스터디하면서 두서없이 정리한 내용입니다. 조금씩 정리하도록 하겠습니다.

 

Spring framework

 

  • 프로젝트 초기에 기업 내 시스템 통합과 수월한 유지보수 등 많은 고민거리를 해결할 수 있는 framework 에 대한 선정을 두고 많은 고민을 하곤 합니다. 예전부터 각 대기업이나 중소 SI업체에서 framework를 자체 개발하여 프로젝트에 적용하고 있으나 framework 자체가 특정 부분에 특화되었거나 투입된 프로젝트에서 커스텀마이징되면서 상이한 버젼의 관리가 힘들정도로 어려운 사항에 처한 경우도 발생하였습니다. 이 같은 실정에서 interface12.com 이라는 컨설팅 회사에서 개발한 Spring framework 는 위와 같은 이슈를 해결할 수 있는 능력을 보여주면서부터 전세계 자바 관련 프로젝트에서 많이 사용되고 있습니다. 국내 프로젝트에서 적용된 사례는 아직 미비하지만 현재 많은 개발자들이 이 훌륭한 framework의 존재를 인지하고 있으며 앞으로 많이 사용될 것으로 추측됩니다.

 

  • 또다른 사항은 최근 풍부한 오픈소스로 인해 기업에서 손수 framework를 개발할 필요가 없게 됨에 따라 프로젝트에 적용할 오픈소스를 어떻게 조합할 것인가? 에 대한 새로운 문제상황이 발생하였습니다. Spring framework는 그 자체로도 훌륭한 모듈(AOP, Spring MVC, Transaction, JDBC...)을 포함하고 있을 뿐 아니라 다양한 여러 오픈 소스(Struts, webwork, jsf, hibernate, ibatis ...)를 결합할 수 있는 유연한 설정 및 추상 클래스를 제공하고 있습니다. 또한 기존의 OOP 개발 방식에 횡단적 개발 방식인 AOP 개념을 포함하여 프로젝트 중간에 정책(로깅, 트랜젝션 설정 등)이 변경되거나 추가되었을 때 소스 수정없이 설정만으로 변경 가능하게 구현되어 있습니다. 단, 개발 초기에 위 사항을 고려하여 변경가능성이 있는 정책에 관한 설정을 소스코드에서 분리하는 방안 등을 고려하여 개발하여야 합니다.

 

EJB 와 Lightweight Container(Non EJB)

 

  • EJB architecture 의 장점 :
  1. 정형화된 비즈니스 계층을 제공
  2. 선언적인 트랜젝션 관리를 EJB container 가 제공(ejb deploy descriptor 파일 필요)
  3. 분산 기능을 지원(business 객체를 여러서버에 분산시켜 서비스 가능)

 

  • EJB architecture 의 단점 :
  1. 분산 환경의 서비스 호출로 인한 객체 직렬화 과정으로 실행속도 저하
  2. 개발 사이클, 소스 수정, 빌드, 배포, 테스트의 과정이 복잡하므로 생산성 저하
  3. EJB container에 종속적이므로(ejb deploy descriptor 파일이 벤더에 따라 다름) application의 이식성이 떨어짐

 

 

  • Lightweight Container(Non EJB) architecture 의 장점 :
  1. 경량 container 에 POJO(Plain Old Java Object)로 개발되므로 생산성과 실행속도 향상, 테스트 용이
  2. EJB에 비해 배우기 쉬우며, descriptor 설정 방법도 쉬움
  3. servlet container 에서 실행하는 것이 가능하므로 이식성 뛰어남
  4. EJB container 가 지원하는 transaction 처리, Security, bean life cycle 관리 기능을 지원

 

  • Ligntweight Container(Non EJB) architecture 의 단점 :
  1. 분산 기능을 지원하지 않음. 단, 분산 기능 필요 시 웹서비스 또는 EJB 와 연동
  2. Lightweight container에 대한 표준 없음
  3. Lightweight container architecture를 새로 배워야 하는 부담감 존재

 



Spring IoC (Inversion of Control)

 

  • Inversion of Control 이란 제어의 역행, 역제어라는 뜻
  • 초창기 자바 프로그램에서는 객체 생성 및 의존관계에 대한 모든 제어권이 개발자에게 있었으나 최근 Servlet, EJB의 등장하면서 객체의 생성 및 의존관계, Life Cycle 에 대한 제어권이 Container가 전담하게 됨
  • EJB Container는 EJB를 관리, Servlet Container는 Servlet을 관리, Spring에서는 POJO(Plain Old Java Object)의 생성 및 Life cycle을 관리함. 이를 IoC Container 라 일컬음
  • Spring Container 에서는 일부 POJO. 즉, 각 Layer의 인터페이스를 구현한 클래스에 대한 제어권을 가지며 내부에서 사용하는 클래스의 생성에 대한 일부 제어권은 개발자에게 있음

 

  • IoC Container 분류체계

 

IoC
|-------> DL (EJB, Spring)
|-------> DI (Spring)
|-----------> Setter Injection
|-----------> Constructor Injection
|-----------> Method Injection
- IoC : Inversion of Control
- DL  : Dependency Lookup
- DI  : Dependency Injection


DL(Dependency Lookup)

 

  • 모든 IoC Container는 관리해야 하는 객체들을 관리하기 위한 별도의 repository를 가짐
  • Servlet Container 는 web.xml 에서 Servlet을 관리하며 EJB Container는 ejb-jar.xml에 설정된 정보들이 JNDI repository에 저장되어 관리됨
  • Spring 에는 POJO를 관리하기 위해 xml 또는 properties 파일을 이용함
  • 위처럼 repository 에 저장된 Bean 에 접근하기 위하여 해당 Container에서 제공하는 API를 이용하여 Bean을 Lookup하는 것을 DL(Dependency Lookup) 이라 함

 

  • EJB의 경우 다음과 같이 사용

 

Context ctx = new InitialContext();
UserServiceHome home = (UserServiceHome)ctx.lookup("JNDI_BEAN_NAME");
UserService service = home.create();

 

  • Spring의 경우 다음과 같이 사용

 

WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletCtx);
UserService service = (UserService)wac.getBean("KEY_BEAN_NAME");

 

  • DL을 사용할 경우 Container 의 API를 사용해야하는 의존관계가 발생하므로 이를 해결하기 위해 DI(Dependency Injection)을 사용하여 Container 의 종속성을 줄일수 있음

 


DI (Dependency Injection)

 

  • DI는 각 클래스 사이의 의존관계를 설정파일에 정의된 빈 설정정보를 토대로 Container가 자동으로 연결해 주는 것을 의미하며 이로써 Container API 에 대한 종속성을 줄일 수 있음
  • 개발자는 단지 빈 설정파일에 의존관계에 대한 정보를 추가해주기만 하면 됨
  • Spring 에서 클래스 사이의 의존관계를 관리하기 위한 방법으로 Setter Injection, Constructor Injection, Method Injection 의 세가지 유형으로 처리함

 

Setter Injection

 

  • 클래스 사이의 의존관계를 연결시키기 위하여 setter 메소드를 이용

 

 

public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public int addUser(User user) {
...
return userDAO.insert(user);
}
...
}

 

  • applicationContext.xml 의 내용

 

<beans>
<bean id="userDAO"
class="net.javajigi.user.dao.MySQLUserDAO" />
<bean id="userService"
class="net.javajigi.user.service.UserServiceImpl" >
<property name="userDAO" >
<ref local="userDAO" />
</property>
</bean>
...
</beans>

 

 

 

Constructor Injection

 

  • 클래스 사이의 의존관계를 연결시키기 위하여 Constructor를 이용

 

 

public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public UserServiceImpl(UserDAO userDAO) {
this.userDAO = userDAO;
}
public int addUser(User user) {
...
return userDAO.insert(user);
}
...
}

 

 

  • applicationContext.xml 의 내용

 

<beans>
<bean id="userDAO"
class="net.javajigi.user.dao.MySQLUserDAO" />
<bean id="userService"
class="net.javajigi.user.service.UserServiceImpl" >
<constructor-arg>
<ref local="userDAO" />
</constructor-arg>
</bean>
...
</beans>

 

 

 

Method Injection

 

  • Setter Injection, Constructor Injection의 한계점을 극복하기 위하여 Spring 1.1 부터 지원된 DI의 한 종류임
  • Spring에서 기본적으로 빈의 인스턴스를 관리하는 방식은 Singleton 인데 Non Singleton 과의 의존관계를 연결할 필요성 발생
  • Method Injection은 Singleton 인스턴스와 Non Singleton 인스턴스의 의존관계를 연결시키기 위해서 사용하는 DI 임

 

  • reference site :

http://springframework.org/docs/reference/beans.html#beans-factory-method-injection

http://openframework.or.kr/framework_reference/spring/ver1.2.2/html/beans.html#beans-factory-method-injection

 

 


Spring framework의 초기화 및 Bean의 life cycle

 

  • Spring framework이 빈 설정파일 정보를 바탕으로 POJO 빈의 life cycle을 관리하는 과정은 다음과 같음

 

 

  1. 빈 설정파일의 XML DTD 유효성 체크
  2. 빈 인스턴스 생성하면서 의존관계에 있는 빈이 존재하는지 여부 체크, 존재하지 않는다면 빈의 초기화가 실패
  3. 의존관계에 있는 빈의 체크가 완료되면 Dependency Injection 빈을 초기화함
  4. 생성한 빈이 BeanNameAware 인스턴스이면 setBeanName() 메소드 호출
  5. 생성한 빈이 BeanFactoryAware 인스턴스이면 setBeanFactory() 메소드 호출
  6. 생성한 빈이 ApplicationContextAware 인스턴스이면 setApplicationContext() 메소드 호출
  7. 생성한 빈이 InitializingBean 인스턴스이면 afterProperties() 메소드 호출
  8. 생성한 빈의 설정에 init-method 가 설정되어 있다면 init-method 에 정의된 메소드 호출
  9. 초기화 과정이 끝나면 빈은 사용 가능한 준비 상태가 되고 BeanFactory의 getBean() 메소드를 이용하여 접근하는 것이 가능
  10. BeanFactory의 getBean() 메소드를 이용하여 접근할 때 Non Singleton 빈이라면 getBean()메소드가 호출될 때마다 위 그림의 2~8까지의 빈 초기화 과정을 거치게 됨
  11. Application 이 종료될 때 빈의 종료메소드가 호출되면서 소멸됨
  12. 생성한 빈이 DisposableBean 인스턴스이면 destory() 메소드 호출
  13. 생성한 빈의 설정 파일에 destroy-method 가 설정되어 있다면 destory-method 에 정의된 메소드 호출

 

 

  • POJO의 초기화 지원 방법은 두가지로 InitializingBean 상속 후 afterPropertiesSet() 구현하거나 POJO에 메소드 구현 후 bean태그의 init-method="메소드" 속성 추가함
  • POJO의 소멸화 지원 방법 두가지로 DisposableBean 상속 후 destory() 구현하거나 POJO에 메소드 구현 후, bean태그의 destroy-method="메소드" 속성 추가함
  • Spring 관리 하의 POJO 빈은 디폴트로 Singleton 이지만 Non Singlton 으로 설정가능함 (bean태그의 singleton="false" 로 설정) 단, 위에서 설명하였듯이 매번 인스턴스를 초기화하면서 의존 상태 검사 등의 로드가 걸림
  • Spring 내 Singleton 사용하는 경우 :
  1. 생성되는 인스턴스 내 공유되는 상태가 없는 경우(예: member variables)
  2. 생성되는 인스턴스 내 read-only 상태만 있는 경우
  3. 생성되는 인스턴스 내 공유되는 상태가 있더라도 동기화가 보장되는 경우

 

 

ApplicationContextAware 인터페이스를 구현하여 메시지 처리

 

 

 
public class UserServiceImpl
implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
this.ctx = ctx;
}
...
public boolean login(String userId, String password)
throws PassworkdMismatchException, UserNotFoundException {
...
User user = findUser(userId);
if(!user.isMatchPassword(password)) {
throw new PasswordMismatchException(
ctx.getMessage("password.mismatch.exception", new Object[] { userId }, null));
}
return true;
}
...
}

 

 

<beans>
<bean id="messageSource" 
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>Messages</value>
</list>
</property>
</bean>
...
</beans>

 

 

 

MessageSourceAccessor클래스를 이용한 메시지 리소스 사용

 

  • MessageSourceAccessor를 빈 설정파일에 설정해두고 Setter Injection 을 통해 해당 POJO 빈에서 사용하게 구현함

 

 

 
public class UserServiceImpl
implements UserService {
private MessageSourceAccessor msAccessor;
public void setMessageSourceAccessor(MessageSourceAccessor msAccessor) {
this.msAccessor = msAccessor;
}
...
public boolean login(String userId, String password)
throws PassworkdMismatchException, UserNotFoundException {
...
User user = findUser(userId);
if(!user.isMatchPassword(password)) {
throw new PasswordMismatchException(
msAccessor.getMessage("password.mismatch.exception", new Object[] { userId }, null));
}
return true;
}
...
}

 

 

<beans>
...
<bean id="messageSource" 
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>Messages</value>
</list>
</property>
</bean>
<bean id="messageSourceAccessor"
class="org.springframework.context.support.MessageSourceAccessor">
<constructor-arg>
<ref local="messageSource" />
</constructor-arg>  
</bean>
<bean id="userService"
class="net.javajigi.user.service.UserServiceImpl" >
<property name="userDAO" >
<ref local="userDAO" />
</property>
<property name="messageSourceAccessor" >
<ref local="messageSourceAccessor" />
</property>
</bean>
...
</beans>

 

  • 한글 Messages.properties 를 사용하기 위해서는 native2ascii utility 를 사용하여 ascii코드로 변경하여야 함(ant의 사용)

 

 

Spring 기동을 위한 web.xml 설정

 

  • spring 기동을 위하여 다음과 같이 web.xml 설정함 (listener 로 ContextLoaderListener 를 설정)

 

<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
...
</web-app>

 

 

  1. XmlWebApplicationContext : 웹 애플리케이션에서 일반적으로 사용됨
  2. FileSystemXmlApplicationContext : 일반 애플리케이션 및 테스트 클래스 작성 시 많이 사용
  3. ClassPathXmlApplicationContext : 일반 애플리케이션 및 테스트 클래스 작성 시 많이 사용

 

 

String[] path = {"/WEB-INF/applicationContext.xml"};
ApplicationContext ctx = new new ClassPathXmlApplicationContext(paths);
UserDAO userDAO = (UserDAO)ctx.getBean("userDAO");

 

 

String[] path = {"project/web/WEB-INF/applicationContext.xml"};
ApplicationContext ctx = new new FileSystemXmlApplicationContext(paths);
UserDAO userDAO = (UserDAO)ctx.getBean("userDAO");

 

 


Spring AOP(Aspect Oriented Programming)

 

AOP(Aspect Oriented Programming) 개념

 

  • OOP의 종단적인 것이 아니라 횡단적인 개념의 적용
  • business 인스턴스의 로직에서 핵심로직을 제외한 Logging, Exception 처리를 분리
  • 분리된 Core, Logging, Exception 을 설정파일로 통합

 

 

AOP에서 사용되는 용어

 

  • JoinPoint : 클래스의 인스턴스 생성 시점, 메소드를 호출하는 시점, Exception 이 발생하는 시점과 같이 애플리케이션을 실행할 때 특정 작업이 실행되는 시점을 의미
  • Advice : Logging 과 SendEmail 과 같이 core logic 이외 추가적인 기능을 제공하는 것
  • Pointcut : 분리된 기능들을 결합시키기 위한 규칙(패턴)
  • Aspect : Advice + Pointcut 합쳐서 Aspect 라 함. 즉, 일정한 패턴을 가지는 클래스에 Advice를 적용하도록 지원할수 있는 것을 Aspect 라 함
  • Weaving : 분리된 UserServiceCore 와 Loggin, SendEmail 을 결합시키는 것을 의미
  • Target : core logic 을 구현한 UserServiceCore 클래스를 의미. 즉, 핵심 비지니스 로직을 구현하는 클래스를 의미함

 

 

Target클래스와 Advice 클래스와의 weaving(결합)

 

  • Spring에서는 Target 클래스와 Advice 클래스와의 결합(weaving)을 지원하기 위하여 ProxyFactory API 지원

- ProxyFactory는 디플트로 Pointcut 을 지원하기 때문에 target클래스의 모든 메소드가 실행될 때 Advice를 적용하도록 지원

 

ProxyFactory pf = new ProxyFactory();
pf.addAdvice(new SUserLoggingAdvice());
pf.addAdvice(new EmailNotificationThrowsAdvice());
pf.setTarget(targetObject);
UserService userService = (UserService)pf.getProxy();
...

 

 

다섯가지 Advice 개념 :

  • MethodBeforeAdvice : target 클래스의 메소드가 실행되기 전에 기능 추가를 위해 before() 메소드를 구현, 단 target 클래스의 메소드에 대한 직접적인 제어는 할수 없음
  • AfterReturningAdvice : afterReturning() 메소드를 구현하여 target 클래스의 메소드 실행이 완료된 다음 후처리 작업을 수행. 즉, target 클래스의 메소드 실행 후 반환되는 결과 값에 대한 접근이 가능함
  • AroundAdvice : method interceptor, target 클래스의 특정 메소드 접근, 전, 후처리까지 모두 가능, org.aopalliance.intercept.MethodInterceptor 의 invoke() 메소드를 구현
  • ThrowsAdvice : Target 클래스의 메소드 내 Exception 이 발생하였을 때 적용되는 Advice, catch 할 필요가 있는 Exception 에 따라 여러 개의 afterThrowing() 메소드를 가질수 있음
  • IntroductionAdvice : target 클래스에 완전히 새로운 기능을 추가하는 것이 가능하도록 지원. spring reference 참조

 

 

Pointcut 과 Advisor

 

  • Pointcut은 target 클래스와 Advice 클래스가 결합(weaving)될 때 둘 사이의 결합 규칙을 정의. 예를 들어 UserServiceImplUserLoggingAdvice 가 적용될 때 addUser() 메소드만 적용하고 나머지 메소드는 제외해야 하는 요구사항이 발생할 경우 이를 정의하는 것이 Pointcut 임

 

public class UserLogginPointcut
extends StaticMethodMatcherPointcut {
public boolean matches(Method method, Class cls) {
if("addUser".equals(method.getName()) {
return true;
}
return false;
}
public ClassFilter getClassFilter() {
return new ClassFilter() {
public boolean matches(Class cls) {
return (cls == UserServiceImpl.class || cls == MySQLUserDAO.class);
}
}
}
}

 

  • Advisor는 AOP의 Aspect 와 같은 개념을 가지는 것으로 Pointcut 과 Advice 를 결합된 것을 하나의 Advisor 라고 함

 

ProxyFactory pf = new ProxyFactory();
Pointcut logginPC = new UserLogginPointcut();
Advisor loggingAdvisor = new DefaultPointcutAdvisor(loggingPC
, new UserLoggingAdvice());
pf.addAdvisor(loggingAdvisor);
Pointcut emailPC = new EmailNotificationPointcut();
Advisor emailAdvisor = new DefaultPointcutAdvisor(emailPC
, new EmailNotificationThrowsAdvice());
pf.addAdvisor(emailAdvisor);
pf.setTarget(target);
UserService userService = (UserService)pf.getProxy();

 

 

Spring에서 제공되는 Pointcut

 

  1. target 클래스의 정적인 정보(클래스명, 메소드명)을 기반으로 Pointcut를 만들고자 할 때 사용.
  2. matches(Method method, Class targetClass) 메소드 구현
  1. target 클래스의 정적인 정보를 포함하여 동적인 정보(메소드 호출 시 전달되는 인자값)를 기반으로 Pointcut을 만들고자 할 때 사용.
  1. 정규표현식을 이용하는 것이 가능하도록 지원.
  2. 첫번째 : Perl5 형식의 정규표현식, 두번째 : JDK에서 지원하는 정규표현식을 지원.
  1. 이 Pointcut 에 전달되는 메소드 이름에 해당하는 메소드에 대해서만 Advice를 적용되도록 지원. 메소드 이름을 여러개 전달하는 것이 가능.
  1. union(), intersection() 을 지원함으로써 두 개 이상의 Pointcut 을 결합하기 위해 사용.
  1. target 클래스 메소드 하위에서 특정 패턴에 일치하는 메소드에 대해서만 Advice를 적용하도록 지원.

 

 

Spring 에서 POJO 빈 생성하는 방법

 

자바의 new 예약어를 이용

 

  • new 로 Object를 생성후 BeanFactory에 캐쉬하여 getBean() 메소드로 가져다 사용

 

<bean id="userDAO" 
class="net.javajigi.user.dao.MySQLUserDAO" />

 

 

 

Factory 메소드를 이용

 

  • 다른 벤더에 의하여 지원되는 라이브러리나 이미 개발되어 있는 애플리케이션의 경우 인스턴스 생성을 위해 지원하는 메소드를 이용할 수 있도록 다음과 같이 지원됨

 

<bean id="aspect" 
class="net.javajigi.advice.LoggingAspect" factory-method="aspectOf" />

 

  • 위와 같이 빈 정의 시 factory-method 속성을 이용하여 인스턴스를 생성하기 위한 메소드명을 지정하는 것이 가능

 

 

FactoryBean 클래스를 이용

 

  • FactoryBean 인터페이스를 구현하여 POJO빈을 생성하는 방법으로 다른 프레임워크나 라이브러리를 통합하기 위한 용도로 사용됨

 

 

package org.springframework.beans.factory;
public interface FactoryBean {
Object getObject()
throws Exception;
Class getObjectType();
boolean isSingleton();
}

 

  • FactoryBean 인터페이스를 구현한 AOPFactoryBean 클래스 생성

 

public class AOPFactoryBean
implements FactoryBean, BeanFactoryAware, InitializingBean {
private boolean singleton = true;
private Object instance;
private String[] interceptorNames;
private Object target;
private BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void setInterceptorNames(String[] interceptorNames) {
this.interceptorNames = interceptorNames;
}
pulic void setTarget(Object target) {
this.target = target;
}
public void afterPropertiesSet() throws Exception {
... Setter Injection 된 인스턴스 체크...
}
public Advisor getAdvisor(String interceptorName) {
Object advObject = beanFactory.getBean(interceptorName);
if(advObject instanceof Advisor) {
return (Advisor)advObject;
} else if(advObject instanceof Advice) {
return new DefaultPointcutAdvisor((Advice)advObject);
} else { 
return null;
}
}
public Object getObject() throws Exception { ... }
public Class getObjectType() { ... }
public boolean isSingleton() { ... }
}  

 

  • 빈 설정파일에 AOPFactoryBean 클래스 설정

 

 

 

ProxyFactoryBean 을 이용하여 선언적으로 AOP 적용

 

<beans>
...
<bean id="userDAOTarget">
...
</bean>
<bean id="userServiceTarget">
...
</bean>
<bean id="userLoggingadvisor">
...
</bean>
<bean id="userLoggingAdvice">
...
</bean>
<bean id="emailNotificationAdvisor">
...
</bean>
<bean id="meailNotificationThrowsAdvice">
...
</bean>
<bean id="userDAO" 
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="userDAOTarget" />
</property>
<property name="interceptorNames">
<list>
<value>userLoggingadvisor</value>
</list>
</property>
</bean>
<bean id="userService" 
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="userServiceTarget" />
</property>
<property name="interceptorNames">
<list>
<value>userLoggingadvisor</value>
<value>emailNotificationAdvisor</value>
</list>
</property> 
</bean>
...
</beans>

 

Spring Automatic Proxy 사용

  • 모든 Target 클래스에 ProxyFactoryBean 의 적용 부분을 추가한다면 귀찮은 작업임(Logging 의 경우)
  • Spring 에서 제공되는 Automatic Proxy 클래스를 사용.

 

  1. ApplicationContext에서 관리되고 있는 모든 빈에 관현된 Advisor를 적용하도록 하는 것이 가능함.
  2. 아래 applicationContext.xml 에 보면 위 설정에서 ProxyFactoryBean 을 설정하는 부분이 없어짐.
  3. 주의점 : ApplicationContext 의 모든 빈에 적용되는 것을 원칙으로 하기 때문에 Pointcut을 적절하게 사용하지 않을 경우 Advice 가 원하는 대로 적용되지 않을 수 있음.

 

<beans>
...
<bean id="userDAO">
...
</bean>
<bean id="userService">
...
</bean>
<!-- Default Advisor를 기준으로 해당 패턴에 일치하는 모든 빈에 대해서 Advice 적용 -->
<bean id="proxyCreator" 
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
<bean id="userLoggingadvisor">
...
</bean>
<bean id="userLoggingAdvice">
...
</bean>
<bean id="emailNotificationAdvisor">
...
</bean>
<bean id="meailNotificationThrowsAdvice">
...
</bean>
...
</beans>

 

  1. Bean 설정 파일의 빈 이름을 기준으로 aspect를 적용할지의 여부를 결정

 

<beans>
...
<bean id="userDAO">
...
</bean>
<bean id="userService">
...
</bean>
<!-- BeanNameAutoProxyCreator 를 설정 -->
<bean id="proxyCreator" 
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*DAO</value>
<value>*Service</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>userLoggingadvisor</value>
<value>emailNotificationAdvisor</value>
</list>
</property>
</bean>
<bean id="userLoggingadvisor">
...
</bean>
<bean id="userLoggingAdvice">
...
</bean>
<bean id="emailNotificationAdvisor">
...
</bean>
<bean id="meailNotificationThrowsAdvice">
...
</bean>
...
</beans>

 

 

  • Spring 은 automatic proxy를 지원하기 위하여 소스 레벨에 메타 데이터를 추가하여 AOP를 지원하는 것이 가능함

- spring reference 참조

 

  • Spring 과 AspectJ 와의 통합 : p 194 ~ 199

 


Spring JDBC

 

 

  • JdbcTemplate, SQLObjects 의 사용, DAO 구현전에 abstract class 인 DAOSupport 를 제공, 쿼리를 MessageSource로 분리

 

 

  • RowMapper 의 mapRow(ResultSet rs, int rownum) 을 구현하여 ResultSet 과 Entity Object의 수동 매핑

 

  • JdbcTemplate 클래스의 주요 메소드 : Template Method Pattern 을 기반으로 함.

 

# insert, update, delete 관련 메소드

int update(String sql)

int update(String sql, Object[] args)

int update(String sql, Object[] args, int[] argTypes)

# select 관련 메소드

int queryForInt(String sql)

int queryForInt(String sql, Object[] args)

long queryForLong(String sql)

long queryForLong(String sql, Object[] args)

Object queryForObject(String sql, Class requiredType)

Object queryForObject(String sql, Object[] args, RowMapper rowMapper)

List queryForList(String sql)

LIst queryForList(String sql, Object[] args)

# PreparedStatementSetter 사용

PreparedStatementSetter psSetter = new PreparedStatementSetter() {

public void seValues(PreparedStatement ps)

throws SQLException{

.. 구현 ..

}

}

ResultReader resultReader = new ResultReader() {

List list = new ArrayList();

public void processRow(Result rs)

throws SQLException {

.. 구현 ..

}

public List getResults() {

return list;

}

}

List list = getJdbcTemplate().query(sql, psSetter, resultReader);

return list;

 

 

  • SQLObjects 를 이용하여 구현 : springframework.jdbc.object 패키지의 상위 클래스를 상속하여 구현

 

public class SelectByNo extends MappingSqlQuery {
public SelectByNo(DataSource ds, String sql) {
super(ds, sql);
declareParameter(new SqlParameter(Types.INTEGER));
}
public Object mapRow(ResultSet rs, int rownum)
throws SQLException {
.. 구현 ..
}
}
public Insert extends SqlUpdate {
public Insert(DataSource ds, String sql) {
super(ds, sql);
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
.. declareParameter() 정의 ..
}
}
//DAO 클래스 내에서
public class SpringDAO 
extends SpringDAOSupport
implements ISpringDAO {
private SelectByNo selectByNo;
private Insert insert;
public void initDAO() throws Exception {
super.initDAO(); 
this.selectByNo = new SelectByNo(getDataSource(), selectByNoSql);
this.insert = new Insert(getDataSource(), insertSql);
}
public int insert(int no, SpringVO vo)
throws Exception {
insert.update(new Object[] { vo.getName(), vo.getTitle(), .... });
int[] i = insert.flush();
return i[0];
}
public List selectByNo(int vono)
throws Exception {
return selectByNo.execute(vono);
}
...
}

 

 

- Stored Procedure 구현 방법은 reference 참조

 

  • abstract class SpringDAOSupport 클래스 구현 후 하위 클래스에서 상속할 때 beans.xml 에 설정방법.

- abstract class 에서 Dependency Insection 해야 되므로 함께 설정함, 단, abstract="true" 로 설정하고 상속하는 클래스의 설정에서 parent="SpringDAOSupport" 로 지정하여야 함

 

<bean id="myJdbcDAOSupport" abstract="true" class="~~~~" >
<property name="dataSource" >
<ref local="dataSource" />
</property>
</bean>
<bean id="userDAO" parent="myJdbcDAOSupport" class="~~~" >
...
</bean>
<bean id="boardDAO" parent="myJdbcDAOSupport" class="~~~" >
...
</bean>  

 

  • 빈설정파일의 관리(분리 방법)

 

- 프로젝트 규모에 따라 수평적 분리, 수직적 분리로 나눈다.
- 수평적 분리 :
User 컴퍼넌트 : applicationContext-user.xml ( UI, Business, Persistence 설정)
Board 컴퍼넌트 : applicatoinContext-board.xml ( UI, Business, Persistence 설정)
- 수직적 분리 : 
UI, Business, Persistence 계층별로 설정
- 위 두가지 방법다 장단점 내포
- 그러므로 빈설정파일 관리 전략에서 공통 모듈의 UI, Business, Persistence 계층의 abstract class 의 설정, 하위는 업무별(user, board)로 빈 설정파일을 관리함(수평적 분리 적용)

 

 


Spring Transaction

 

 

 

  • Transaction 처리의 계층? business Layer? Persistence Layer?

- business Layer에서 처리하도록 함. 모듈의 독립성 및 재사용성. DAO 의 코드량 줄임.

 

- Atomicity(원자성) : 트랜젝션 내 있는 모든 작업은 완료되거나 완료되지 않아야 함(commit or rollback)

- Consistencey(일괄성) : 트랜젝션 중에 오류없이 유효한 데이터만 db에 저장되어야 함

- Isolation(격리성) : 트랜젝션 중에 변경된 내용이 트랜젝션 완료 전까지 다른 트랜젝션에 영향끼치면 안됨

- Durability(지속성) : 트랜젝션이 완료된 경우 시스템 고장, 네트워크 에러 등에 데이터가 유실되지 않고 정상적으로 기록되어야 함

 

- TransactionDefinition.ISOLATION_DEFAULT : default isolation level.

- TransactionDefinition.ISOLATION_READ_UNCOMMITTED : isolation level 중 가장 낮음. commit 되지 않은 transaction에서 변경된 데이터 확인가능. transaction 기능을 거의 수행하지 않음.

- TransactionDefinition.ISOLATION_READ_COMMITTED : 대부분 db에서 디폴트로 지원하는 isolation level, commit 되지 않은 transaction 에서 변경된 데이터를 볼 수 없음. 다른 transaction에 의해 입력 또는 수정된 데이터는 조회할 수 있음.

- TransactionDefinition.ISOLATION_REPEATABLE_READ : COMMITTED 보다 상위 isolation level

- TransactionDefinition.ISOLATION_SERIALIZABLE : 가장 많은 비용이지만 가장 신뢰할만한 isolation level, 하나의 transaction 완료 후 다른 transaction 실행

 

- TransactionDefinition.PROPAGATION_REQUIRED : 이미 활성화된 트랜젝션이 존재한다면 그 트랜젝션을 지원, 없다면 새로운 트랜젝션을 시작.

- TransactionDefinition.PROPAGATION_SUPPORTS : 이미 활성화된 트랜젝션이 존재한다면 그 트랜젝션을 지원, 없다면 비-트랜젝션 형태로 수행.

- TransactionDefinition.PROPAGATION_MANDATORY : 이미 활성화된 트랜젝션이 존재한다면 그 트랜젝션을 지원, 없다면 예외를 던짐.

- TransactionDefinition.PROPAGATION_REQUIRES_NEW : 이미 활성화된 트랜젝션이 존재한다면 일시 정지, 언제나 새로운 트랜젝션을 시작함

- TransactionDefinition.PROPAGATION_NOT_SUPPORTED : 이미 활성화된 트랜젝션을 지원하지 않음, 언제나 비-트랜젝션으로 수행, 존재하는 트랜젝션은 일시 정지.

- TransactionDefinition.PROPAGATION_NEVER : 활성화된 트랜젝션이 존재하더라도 비-트랜젝션적으로 수행, 활성화된 트랜젝션이 존재한다면 예외를 던짐.

- TransactionDefinition.PROPAGATION_NESTED : 활성화된 트랜젝션이 존재한다면 내포된 트랜젝션으로 수행, 작업 수행은 PROPAGATION_REQUIRED 으로 세팅된 것처럼 수행.

 

  • TransactionStatus : 트랜젝션 상태를 관리하는 역할을 담당하는 인터페이스.

 

  • PlatformTransactionManager : 실질적인 트랜젝션을 실행. 에러없이 완료하면 commit, 에러가 발생하면 rollback 작업 실행하도록 지원.

 

package org.springframework.transaction;
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) 
throws TransactionException;
void commit(TransactionStatus status)
throws TransactionException;
void rollback(TransactionStatus status)
throws TransactionException;
}

 

 

PlatformTransactionManager 
|
AbstractPlatformTransactionManager
|        |          |        |
HibernateTransactionManager  |          |        |
DataSourceTransactionManager |        |
JmsTransactionManager |
JtaTransactionManager

 

 

  • 빈설정파일의 선언적 트랜젝션 처리 : p 288 ~ p 294

 

=== applicationContext-jdbc.xml ===
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
<property name="transactionManager" >
<ref bean="transactionManager" />
</property>
<property name="preInterceptors" >
<list>
<ref bean="loggingAdvice" />
<ref bean="emailNotificationThrowsAdvice" />
</list>
</property>
<property name="transactionAttributes" >
<props>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED, readOnly</prop>
</props>
</property>
</bean>
=== applicationContext-board.xml ===
<bean id="boardServiceTarget" 
class="net.javajigi.board.service.BoardServiceImpl" >
<property name="boardDAO" >
<ref local="boardDAO" />
</property>
<property name="boardFileDAO" >
<ref local="boardFileDAO" />
</property>
</bean>
<bean id="boardService" parent="txProxyTemplate" >
<property name="target" >
<ref local="boardServiceTarget" />
</property>
<property name="transactionAttributes" >
<props>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="findBoardWithView">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED, readOnly</prop>
</props>
</property>
</bean>

 

 

  • 빈설정파일에 선언적으로 트랜젝션을 설정할 때 트랜젝션 속성을 정의하는 방법 :

 

PROPAGATION, ISOLATION, readOnly, -Exceptions, +Exceptions

전달행위(필수) 격리레벨(선택) readOnly유무(선택) Rollback규칙(선택)

- 전달행위 : 위 전달행위 중 하나 선택

- 격리레벨 : 위 격리레벨 중 하나 선택

- readOnly : select 쿼리 수행 시 설정

- Rollback 규칙 : Spring 에서는 디폴트 설정이 RuntimeException 이 발생하는 경우 Rollback, Checked Exception 이 발생하는 경우 Commit 되도록 하고 있음 (-) 로 시작되는 Exception 에 대해서는 무조건 Rollback, (+)로 시작되는 Exception 에 대해서는 무조건 Commit 되도록 규칙을 변경할 수 있음.

- ex) PROPAGATION_REQUIRED, ISOLATION_SERIALIZABLE, -net.javajigi.user.PasswordMismatchException

 

  • aspectJ reference 사이트 :

 

- AspectJ 메인 사이트 : http://www.eclipse.org/aspectj

- AspectJ 기반 개발환경을 지원하는 사이트 : http://www.eclipse.org/ajdt/

- AspectJ in Action(번역서 존재)

 


Spring MVC

 

 

 


Spring 프로젝트 개발 전략

 

공통 표준코딩 구현 방안

 

  • Commons Lang (jakarta apache) 의 Builder를 사용하여 VO 의 BaseObject를 구현

toString(), equals(), hashCode() 를 override. 단, toString()의 경우 출력할 필드 filtering 기능필요

  • Spring 사용 시 Exception, Logging, 국제화 메시지 처리 방법에 대한 논의(개발 초기에 정책 수립 필요)
  • Checked Exception 보다 UnChecked Exception 을 사용

- Checked Exception 은 명시적으로 호출자의 예외발생에 따른 뭔가를 처리해 주어야 할때만 사용

 

 

AOP를 이용한 효율적인 개발 전략

  • 프로젝트 초반에 구현한느 애플리케이션에 대한 명명규칙을 확립하고 이에 따라 프로젝트를 진행해야 함. 초반에 명명규칙을 확립하고 개발자들이 명명규칙을 따르도록 교육하는 단계가 필요.
  • p 201 AOP 의 사용이 프로젝트에 미치는 영향 그래프 참조

 

 

테스트 전략

 

  • JUnit 으로 테스트 클래스 작성법 숙지 (p 137 ~ p 146)
  • persistence Layer의 테스트 : JUnit 으로 단위 테스트 클래스 작성 시 메소드 당 테스트 메소드를 작성하는 것이 아니라 하나의 CRUD 테스트를 하나의 테스트 메소드에 구현하여 작성하는 것이 적절함( testUserDAO() 작성 시 user 생성, 조회, 수정, 삭제)
  • business Layer 의 테스트 : 데이터베이스와의 연결이 없는 상태에서 비지니스 수행로직의 모든 경우를 테스트를 할수 있도록 구현함
  1. UserDAO 인터페이스를 구현한 MockUserDAO 테스트 클래스를 생성하여 테스트 수행
  2. EasyMock framework 숙지(p 144, 145)

Comments (0)

You don't have permission to comment on this page.