본문 바로가기

스프링

[spring] aop 트랜잭션 전파속성_사라진 어드바이스_클래스프록시 9

1.     Com.springsource.javax.activation : Spring java-mail

2.     Com.springsource.javax.mail : Spring java-mail

3.     Com.springsource.org.aopalliance : Spring ProxyFactoryBean

4.     Com.springsource.org.aspectj.tools : AspectJExpressionPointcut 포인트컷 표현식 지원

5.     Com.springsource.junit : junit

6.     Commons-logging : Spring-context가 사용

7.     Mail : java-mail

8.     Mockito : 목 프레임워크 중 Mockito

9.     Mysql-connector : Mysql JDBC

10.   Org.springframework.context.support : Spring java-mail

11.   Spring-aop : 스프링 기능 자체의 aop, Spring ProxyFactoryBean

12.   Spring-bean : 스프링 빈을 활용하는 경우 필요. 스프링의 XML 설정파일과 자바 애노테이션을 파싱 하는데 필요한 클래스 포함

13.   Spring-context : 스프링 코어를 확장한 많은 클래스가 들어 있는데 모든 클래스는 EJB, JNDI(Java Naming Directory Interface), JMX용 클래스와 연동하는데 applicationcontext기능을 사용해야 하며 스프링 리모팅 클래스, 동적 스크립팅 언어(제이루비, 그루비등)와 연동하는 클래스, 빈 유효성검증(JSR-303) API, 스케줄링을 하는 클래스도 포함되어 있다.

14.   Spring-core : 모든 스프링 모듈에서 필요한 모듈. 다른 스프링 모듈에서 사용하는 공통 클래스가 포함됨.

15.   Spring-dao : EmpltyResultDataAccessException 등 사용을 위한 jar

16.   Spring-expression : 스프링 표현언어(SpEL) 지원 클래스 포함.

17.   Spring-jdbc : 스프링이 지원하는 jdbc.

18.   Spring-test.jar

-       @RunWith : Junit 프레임워크의 테스트 실행방법을 확장시 사용.

-       SpringJUnit4ClassRunner : 어플리케이션컨텍스트를 만들고 관리하는 확장 클래스

-       @ContextConfiguration(경로) : 자동으로 만들어줄 어플리케이션 컨텍스트 설정파일

19.   Spring-tx : DuplicateKeyException.class 파일 존재 및 스프링 트랜잭션

 

 

Level이 할일은 레벨에게 위임

 

 

User가 할일은 User에게 위임

 

 

JDBC, JDO, Hibernate, JPA

 

JDBC 구현. jdbcTemplate : JDBC 템플릿, 콜백 오브젝트를 제공하므로 사용

 

 

데코레이터 패턴, 프록시 오브젝트 생성을 위한 인터페이스

* 인터페이스에 트랜잭션 경계설정을 거는 이유

또한 포인트컷을 해당 서비스인터페이스에 걸어두면 setDataSource() 등의 필요없는 부분은 제외시키고 AOP를 적용시킬 수 있다. setDataSource()에 트랜잭션이 적용되어도 기능상 큰 문제는 없겠지만 불필요한 메서드 까지 트랜잭션 경계설정 작업을 수행하느라 그만큼 시간과 리소스를 소모하게 된다.

 

 

메일서버 과부하 방지를 위한 테스트스텁

 

타겟 오브젝트

 

 

부가기능 어드바이스 구현.

l  트랜잭션 : 더 이상 쪼갤 수 없는 최소 단위의 작업. 트랜잭션 경계(16~25)안에서 진행된 작업은 모두 성공 혹은 모두 취소되어야함.

16. DefaultTransactionDefinition

> PROPAGATION_REQUIRED 트랜잭션 전파 속성. 이미 시작한 트랜잭션이 있으면 그곳에 참여하고, 없으면 새로 트랜잭션을 시작함.(아래 스샷 참조)

> 격리수준은 ISOLATION_DEFAULT(DataSource에 설정되어 있는 디폴트 격리수준을 그대로 따름)

> 제한시간(timeout) 을 설정할 수 있는데, 기본 설정은 제한시간이 없다.

> 읽기전용 등으로 트랜잭션 정의를 수정하려면 이 오브젝트를 사용하는 대신 외부에서 정의된 TransactionDefinition 오브젝트를 DI받아서 사용하도록 만들면 된다. TransactionDefinition 타입의 빈을 정의해두면 프로퍼티를 통해 원하는 속성을 지정할 수 있다. 하지만 한꺼번에 다 바뀜. 이 문제는 ?? 이 어드바이스는 그만 쓰고 스프링의 TransactionInterceptor 사용

16. getTransaction() : 항상 트랜잭션을 새로 시작하는 것이 아닌, 현재 진행 중인 트랜잭션이 존재하는지 여부에 따라서 새로운 트랜잭션을 시작할 수도 있고, 이미 진행 중인 트랜잭션에 참여하기만 할 수도 있다. 진행 중인 트랜잭션에 참여하는 경우는 트랜잭션 경계의 끝에서 커밋하지 않으며, 최초로 트랜잭션을 시작한 경계까지 정상적으로 진행돼야 커밋된다.

 

 

 

트랜잭션 전파(transaction propagation) : 이미 진행 중인 트랜잭션이 있을 때 또는 없을 때 어떻게 동작할 것인가를 결정하는 방식

PROPAGATION_REQUIRED : 진행 중인 트랜잭션이 없으면 새로 시작하고, 이미 시작된 트랜잭션이 있으면 이에 참여한다. 결합해서 하나의 트랜잭션으로 구성시 용이. (DefaultTransactionDefinition의 트랜잭션 전파 속성임(default))

PROPAGATION_REQUIRES_NEW : 항상 새로운 트랜잭션을 시작. 즉 앞에서 시작된 트랜잭션이 있든 없든 상관없이 새로운 트랜잭션을 만들어서 독자적으로 동작하게 한다.

PROPAGATION_NOT_SUPPORT : 진행 중인 트랜잭션이 있어도 무시한다. 보통 AOP를 이용해 한번에 많은 메소드에 적용한다. 물론 포인트컷을 잘 활용하여, 특정 메소드만 트랜잭션이 적용되게 할 수 있지만, 이 속성을 설정하여 트랜잭션 없이 동작하게 할 수 있다.

트랜잭션 격리수준(isolation level)

서버환경에서는 여러 개의 트랜잭션이 동시에 진행될 수 있다. 가능하면 모든 트랜잭션이 순차적으로 진행돼서 다른 트랜잭션 작업에 독립적인 것이 좋겠지만, 그러자면 성능이 크게 떨어진다. 따라서 적절하게 격리수준을 조정해서 가능한 많은 트랜잭션을 동시에 진행시키면서도 문제가 발생하지 않게 제어가 필요하다. 격리 수준은 보통 DB에 설정되어 있지만 JDBC 드라이버나 DataSource 등에서 재설정할 수 있고, 필요에 따라 트랜잭션 단위로 격리수준을 조정할 수 있다.

트랜잭션 제한시간(timeout)을 설정할 수 있으며 DefaultTransactionDefinition의 기본 설정은 제한시간 없음. 제한시간은 PROPAGATION_REQUIRED, PROPAGATION_REQUIRES _NEW와 함께 사용해야만 의미가 있음.

*  읽기전용(read only) : 트랜잭션 내에서 데이터를 조작하는 시도를 막아줄 수 있다. 또한 데이터 엑세스 기술에 따라 성능이 향상될 수도 있다. 엔터프라이즈 시스템이라면 적어도 조회전용 메소드에 읽기전용 속성을 부여해서 성능을 최적화 하는 것이 기본.

 

 

여태 사용한 TransactionAdvice를 보면 트랜잭션 부가기능의 동작방식을 변경할 수 있는 것이 위와 같이 2군데 이다.

1.     TransactionAdviceRuntimeException의 경우에만 트랜잭션을 롤백시킨다. 런타임 예외가 아닌 경우는 제대로 처리되지 않고 메소드를 빠져나간다.

2.     비즈니스 로직상의 체크예외를 던져 DB 커넥션 커밋해야하는 경우 이 로직은 못쓰는 로직이 된다.

3.     스프링의 TransactionInterceptor에는 기본적으로 두가지 예외 처리 방식 존재한다.

런타임 예외 발생시 트랜잭션 롤백

>  체크예외의 경우 이것을 예외상황으로 해석하지 않고 일종의 비즈니스 로직에 따른, 의미 있는 리턴 방식의 한 가지로 인해서 트랜잭션을 커밋가능.

 

TransactionInterceptor

동작방식은 TransactionAdvice와 다르지 않다. 여태 구현한 Advice를 대체함. 다만 트랜잭션 정의를 메소드 이름 패턴을 이용해서 다르게 지정할 수 있는 방법을 추가로 제공해 줌.

PlatformTransactionManagerProperties 타입의 두 가지 프로퍼티를 갖고 있다.

28. transactionAttributes

> 트랜잭션 속성을 정의한 프로퍼티. 트랜잭션 속성은 TransactionDefinition4가지(전파, 격리수준, 제한시간, 읽기전용) 기본 항목rollbackOn() 이라는 메소드를 하나 더 갖고 있는 TransactionAttribute 인터페이스로 정의된다. rollbackOn() 메소드는 어떤 예외가 발생하면 롤백을 할 것인가를 결정하는 메소드.

> Properties라는 컬렉션을 사용하는 이유는 메소드 패턴에 따라서 각기 다른 트랜잭션 속성을 부여하기 위함.

30. get으로 시작하는 메소드 : PROPAGATION_REQUIRED이면서 읽기전용이고 시간제한은 30초다. 보통 조회용 메소드는 읽기전용으로 설정해서 성능을 향상시킬 수 있다.

> PROPAGATION_REQUIRED 트랜잭션이 시작되어 있으면 그 트랜잭션에 참여한다. 만약 쓰기 작업 중에 읽기전용 트랜잭션이 뒤따른다면 충돌?? readOnlytimeout 등은 트랜잭션이 처음 시작될 때가 아니라면 적용되지 않는다. 따라서 get으로 시작하는 메소드에서 트랜잭션을 시작하는 경우라면 읽기전용에 제한시간이 적용되지만 그 외의 경우에는 진행 중인 트랜잭션 속성을 따른다.

31. update로 시작하는 메소드 : 항상 독립적인 트랜잭션으로 동작하도록 NEW로 설정. , 다른 동시 작업에 영향을 받지 않도록 완벽하게 고립된 상태에서 트랜잭션이 동작하도록 격리수준을 최고수준인 ISOLATION_SERIALIZABLE로 설정.

32. * 모든 메소드 : 필수항목인 REQUIRED만 지정하고 나머지는 디폴트 설정을 따르게 했다.

* 때로는 메소드 이름이 하나 이상의 패턴과 일치하는 경우 메소드 이름 패턴 중에서 가장 정확히 일치하는 것이 적용된다.

 

 

트랜잭션 전파 항목만 필수, 나머지는 다 생략 가능. 생략하면 DefaultTransactionDefinition에 설정된 디폴트 속성이 부여된다.

+ 또는 로 시작하는 건 기본 원칙을 따르지 않는 예외를 정의

+ : 런타임예외지만 커밋하게 만들 수 있다.

>   - : 체크예외는 모두 커밋이지만, 롤백대상이 됨.

 

Tx 네임스페이스를 이용한 설정방법

5, 10~11. 네임스페이스와 스키마 지정

29. tx:advice : 이 태그에 의해 TransactionInterceptor 빈이 등록된다.

29. transaction-manager : transactionManager 아이디 등록. 빈 아이디가 transactionManager라면 생략 가능.

31. propagation : 디폴트 값이 스키마에 정의되어 있으므로 REQUIRED라면 아예 생략도 가능.

* 가독성과 XML에디터 자동완성 기능을 통해 편하게 작성할 수 있으므로 TX스키마의 태그를 사용해 어드바이스를 등록하도록 권장함.

36. 위처럼 어드바이저에 pointcut을 등록하면 포인트컷이 하나의 어드바이저에만 쓰인다는 뜻이다. 여러 개 일시 아래내용 참고.

* AOP적용시 가능한한 인터페이스를 활용한다.(37라인 Imple 클래스이지만 해당 클래스는 인터페이스를 구현하고 있어, 스프링은 JavaProxy를 활용하여 ProxyBean을 생성한다.)

> 인터페이스로 적용하면 구현체에서만 필요한 메소드들(setDataSource() )AOP 대상에서 제외되어 시간과 리소스를 낭비하지 않는다.

> 인터페이스를 구현하지 않은 클래스를 AOP로 적용한다면, 스프링은 CGLIB을 이용하여 대상 클래스를 상속받아 프록시를 구현한다. 따라서 클래스가 final인 경우 프록시를 생성할 수 없다.

 

 

 

어드바이저는 다른 빈이나 설정에서 참조할 일이 없으므로 id를 넣지 않는다.

<tx:advice> 및 기타 방법으로 등록한 어드바이스의 아이디와 <aop:pointcut>으로 정의한 포인트컷 아이디를 참조 애트리뷰트에 넣어줘야 한다.

> 이렇게 포인트컷이나 어드바이스가 아이디가 있다는 건 어드바이저를 여러 개 만들어서 다양한 조합을 할 수도 있다는 뜻이다.

 

 

스프링의 AOP는 기본적으로 다이내믹 프록시 기법을 이용해 동작한다. 다이내믹 프록시를 적용하려면 인터페이스가 있어야한다. 인터페이스 사용은 DI에서도 가장 기본원칙인 만큼 문제될 것은 없지만 특별한 경우 인터페이스를 구현하지 않은 클래스에 트랜잭션을 적용할 경우가 있다.

위처럼 proxy-target-class 의 디폴트는 false지만 class에 적용시 true로 변경해준다.

스프링은 다이내믹 프록시 외에도 CGLib 라이브러리가 제공해주는 클래스 레벨의 프록시도 사용할 수 있게 해줌.

이 때 포인트컷의 선정 대상도 클래스여야 한다.

* 클래스 프록시 사용

> 인터페이스를 구현하지 않은 클래스에서 주로 사용됨.

> 인터페이스를 구현한 클래스에도 강제로 적용이 가능하다. 이때는 반드시 클래스에 @Transactional을 부여해줘야 한다. 인터페이스에 적용하면 트랜잭션이 적용되지 않는다.

* 클래스 프록시 제약사항

> 클래스 프록시는 상속을 통해 프록시를 생성하기 때문에 상속이 불가능한 final 클래스에는 적용되지 않는다.

> 상속을 통해 프록시를 만들기 때문에 클래스의 생성자가 두 번 호출된다. 이 때문에 생성자에 리소스를 할당하는 것 같은 중요한 작업은 피해야 한다.

> 인터페이스에 이용하지 않기 때문에 수정자 같은 DB작업이 없는 메소드에도 불필요한 트랜잭션이 실행되므로 시간과 리소스 낭비가 발생한다.

 

 

첫번째 * : 리턴타입

두번째 * : 패키지를 표현할 때 임의의 패키지 1계층

..       : 패키지를 표현할 때 임의의 패키지 0개 이상 계층을 의미

1.     트랜잭션 포인트컷 표현식은 타입 패턴이나 빈 이름일 이용한다.

일반적으로 트랜잭션을 적용할 타깃 클래스의 메소드는 모두 트랜잭션 적용 후보가 되는 것이 바람직하다.

우리 소스의 add 메소드도 트랜잭션에 참여할 가능성이 높다. DB에 추가하는 것 외에도 DB의 정보를 다루는 작업이 추가될 가능성이 높기 때문

또한 단순한 조회 작업만 하는 메소드에도 읽기전용 트랜잭션 속성을 설정해두면 그만큼 성능의 향상을 가져올 수 있다.

따라서 트랜잭션용 포인트컷에는 메소드나 파라미터, 예외데 대한 패턴을 정의하지 않는게 바람직하다.

>  빈 이름을 사용하는 방법도 고려해볼만함.

 

 

공통된 메소드 이름 규칙을 통해 최소한의 트랜잭션 어드바이스 속성을 정의한다. 너무 다양하게 트랜잭션 속성을 부여하면 관리만 힘들어지므로.

가끔 트랜잭션 속성의 적용 패턴이 일반적인 경우와 크게 다른 오브젝트가 존재하는데, 가장 간단한 트랜잭션 속성 부여 방법은 위처럼 모든 메소드에 대해 디폴트 속성을 지정하는 것.

 

 

위와 같이 트랜잭션 속성을 지정할 수 있다. 전파속성은 propagation

 

 

 

 

 

MockUserDao : 목오브젝트 단위테스트를 위한 오브젝트. 이렇게 매번 작성하면 번거로움. 메일 테스트 스텁 오브젝트 마찬가지.

118. 트랜잭션 및 AOP기능 검증을 위한 메서드

 

 

152. mock 프레임웍 사용 목 오브젝트를 일일이 만들 필요없이 인터페이스를 던져주면 만들어줌

 

 

레벨이 할일은 레벨에게 위임

 

 

User의 일은 User에게 위임

 

 

JDBC, Hibernate, JDO, JPA 등과 같은 Data Access API 로 변경될 수 있음으로 인터페이스

 

 

JDBC로 구현. JdbcTemplate : 템플릿과 콜백을 가지고 있는 스프링 것 사용

 

29. Get, find, select 와 같이 조회전용 메소드의 접두어를 정해두는 것이 좋음.

33, 34. get으로 시작하는 메소드는 읽기전용 속성을 두고 나머지는 디폴트 트랜잭션 속성.

39. bean(*Service) : 아이디가 Service로 끝나는 모든 빈에 transactionAdvice 빈의 부가기능이 적용될 것이다.

 

Tx 스키마에 정의된 태그를 이용하면 정의가 훨씬 이해하기 쉽고 간결해졌다.

Tx 네임스페이스를 이용한 설정방법

5, 10~11. 네임스페이스와 스키마 지정

29. tx:advice : 이 태그에 의해 TransactionInterceptor 빈이 등록된다.

29. transaction-manager : transactionManager 아이디 등록. 빈 아이디가 transactionManager라면 생략 가능.

31. propagation : 디폴트 값이 스키마에 정의되어 있으므로 REQUIRED라면 아예 생략도 가능.

* 가독성과 XML에디터 자동완성 기능을 통해 편하게 작성할 수 있으므로 TX스키마의 태그를 사용해 어드바이스를 등록하도록 권장함.

 

 

데코레이터패턴, 프록시를 위한 인터페이스.

트랜잭션  경계설정의 일원화(하나로 만듬)

비즈니스 로직을 담고 있는 서비스 계층 오브젝트에 트랜잭션 경계를 부여하기에 가장 적절한 대상이다.

-       현재 코딩되어있는 add 메서드 처럼 부가 로직을 적용할 수 있고, 트랜잭션 속성도 제어할 수 있기 때문이다.

예를 들어 UserService가 아니라면 UserDao를 직접 사용하지 않고, UserService를 사용하는 것이 바람직하다. 단순 조회나 간단한 수정이라면 직접 UserService외의 서비스 계층 오브젝트에서 UserDao를 사용해도 상관없다. 하지만 등록이나 수정, 삭제가 포함된 작업이라면 다른 모듈의 DAO를 직접 이용할 때 신중을 기해야 한다. 안전하게 사용하려면 다른 모듈의 서비스 계층을 통해 접근하는 방법이 좋다.(트랜잭션 모두 취소 혹은 모두 성공)

-       단순히 레코드 개수를 리턴하는 getCount()를 제외하면 나머지는 독자적인 트랜잭션을 가지고 사용될 가능성이 높다. 따라서 이 4개의 메소드를 추가함.

 

 

49~52. DAO로 위임. 필요한 부가 로직을 넣어도 됨. 트랜잭션을 서비스에 걸어둠으로 다른 서비스에서의 트랜잭션 처리를 위함.

 

 

메일 테스트를 위한 테스트 스텁

 

 

 

트랜잭션 어드바이스지만 이제 안쓸거임 transactionInterceptor 사용할 거임. 트랜잭션 일원화를 위해.

 

 

 

DB 접속불가를 위한 목오브젝트를 이용한 테스트

 

목 오브젝트 생성과 add 테스트, 트랜잭션을 위한 테스트 서비스로 테스트

 

 

트랜잭션 속성 테스트

 

 

번거롭게 목, 스텁 오브젝트를 생성하지 않고, 목 프레임웍을 활용한 테스트

 

 

읽기전용 속성이 원인이 돼서 발생한 예외발생

 

예외의 종류를 파악하여 테스트 반영

 

 

1개의 프로젝트에 2개 이상의 트랜잭션 정의

트랜잭션 성격이 많이 다른 배치 작업용 클래스를 위한 트랜잭션 어드바이스는 별도로 정의해서 독자적인 트랜잭션 속성을 지정함.  

 

 

클라이언트는 인터페이스를 통해 타깃 오브젝트를 사용하는 다른 모든 오브젝트.

, 프록시를 통한 트랜잭션은 적용되나, 타깃내에서의 호출은 트랜잭션이 반영되지 않음

 

 

 

같은 타깃내에서의 호출이 반영되지 않음을 막기위한 것들. 추후 차차 배우자.

 

 

트랜잭션 전파속성 종류

 

트랜잭션 전파속성 종류

 

 

 

격리수준 : 데이터 변화감지

 

 

 

 

 

 

 

 

체크 예외는 커밋된다. 하지만 체크예외도 롤백시킬 수 있다 위처럼

 

 

트랜잭션 커밋 예외