토비의 스프링 AOP






< 6.1. 트랜잭션 코드의 분리 >


- 비지니스 로직과 트랜잭션 경계설정의 분리를 통해 성격이 다른 코드를 각각 독립적인 코드로 만들 수 있다


- DI의 기본 아이디어는 실제 사용할 오브젝트의 클래트 정체는 감춘 채 인터페이스를 통해 간접으로 접근 하는 것.


트랜잭션 경계설정 코드 분리의 장점 (412p)


1. 비지니스 로직을 수정할 때 트랜잭션과 같은 기술적인 내용은 전혀 신경쓰지 않아도 된다. 

2. 비니지스 로직에 대한 테스트를 손쉽게 만들 수 있다.




< 6-2. 고립된 단위 테스트 (테스트에 대한 내용으로 스킵) >


- 단위 테스트를 만들기 위해서는 스텁이나 목오브젝트의 사용이 필수적이다. 

- 목 오브젝트를 편리하게 작성하도록 도와주는 다양한 오브젝트 지원 프레임워크가 있다.


그것이 바로 Mockito 프레임워크 (426p)


- Mockito는 지금까지 나온 목 오브젝트 방식을 지원하는 프레임워크 중에서 가장 사용하기 편리한 기능을 갖고 있다.

- Mokito와 같은 목 오브젝트 지원 프레임워크 하나쯤은 익숙하게 사용할 수 있도록 학습해두자.




< 6.3 다이내믹 프로시와 팩토리 빈 >


프록시란 ? 

- 자신이 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서 클라이언트의 요청을 받아주는 것


타깃 or 실체  

- 프록시를 통해 최종적으로 요청을 위임받아 처리하는 실제 오프젝트



p431

프록시의 특징

- 타깃과 같은 인터페이스를 구현했다는 것과, 프록시가 타깃을 제어할 수 있는 위치에 있다는 것

프록시의  사용 목적

1. 클라이언트가 타깃에 접근하는 방법을 제어하기 위해서다.

2. 타깃에 부가적인 기능을 부여해주기 위해서다. 




데코레이터 패턴 (431p)


- 타깃에 부가적인 기능을 런타임 시 다이나믹하게 부여해주기 위해 프록시를 사용하는 패턴


- 다이나믹하게 기능을 부가한다는 의미는 컴파일 시점, 즉 코드상에서는 어떤 방법과 순서로 프록시와 타깃이 연결되어 사용되는지 정해져 있지 않다는 뜻


- 데코레이터라 불리는 이유는 마치 케익을 여러 겹으로 포장하고 그 위에 장식을 붙이는 것처럼 실제 내용물은 동일하지만 부가적인 효과를 줄수 있기 때문


- 그래서 프록시를 여러개 쓸고 있고 순서를 정해서 단계적으로 위임하면 된다.


- 인터페이스를 통해 위임하는 방식이므로 , 어느 데코레이터에서 타깃으로 연결될지 코드 레벨에선 미리 알 수 없다




프록시 패턴 (433 ~ 434P)


- 일반적으로 사용하는 프록시라는 용어는 클라이언트와 사용대상 사이에 대리 역할을 맡은 오브젝트를 두는 방식을 총칭


- 프록시패턴은 프록시를 사용하는 방법중에서 타깃에 대한 접근 방법은 제어하려는 목적을 가진 경우


- 프록시 패턴은 타깃의 기능 자체에는 관여하지 않으면서 접근하는 방법을 제어해주는 프록시를 이용하는 것이다. (434p)


- 앞으로는 타깃과 동일한 인터페이스를 구현하고 클라이언트와 타깃사이에 존재하면서 기능의 부가 또는 접근 제어를 담당하는 오브젝트를 모두 프록시라 부른다.




다이나믹 프록시 (435p)


- 프록시는 기존코드에 영향을 주지 않으면서, 타깃의 기능을 확정하거나 접근방법을 제어할 수 있는 유요한 방법이다.




- 435~ 437p

프록시를 만드는 것이 상당히 번거롭다


이런 문제를 해결하는데 유용한 것이 바로 JDK의 다이나믹 프록시


- 다이내믹 프록시는 리플렉션 기능을 이용해서 프록시를 만들어 준다.


리플렉션 (437p)


- 자바의 코드 자체를 추상화해서 접근하도록 만든 것


번외 검색 출처: http://gyrfalcon.tistory.com/entry/Java-Reflection


- 리플렉션은 구체적인 클래스타입을 알지 못해도, 그 클래스의 메서드, 타입, 변수들을 접근할 수 있도록 해주는 자바 API

- 리플렉션이란 객체를 통해 클래스의 정보를 분석해 내는 프로그램 기법


ex)

Class c = Data.class;

//Class c = Class.forName("클래스이름");

Method[] m = c.getMethods();                     

Field[] f = c.getFields();

Constructor[] cs = c.getConstructors();

Class[] inter = c.getInterfaces();

Class superClass = c.getSuperclass();





프록시 클래스 : 439p


- 예제설명 439 ~ 441p

- 예제의 프록시는 프록시 적용의 일반적인 문제점 두 가지를 모두 갖고 있다



다이나믹 프록시 적용 : 441p


- 다이나믹 프록시는 프록시 팩토리에 의해 런타임 시 다이나믹하게 만들어지는 오브젝트이다.


- 클라이언트는 다이나믹 프록시 오브젝트를 타깃 인터페이스를 통해 사용할 수 있다.

- 부가기능은 따로 InvocationHandler를 구현한 오브젝트에 담는다.

- InvocationHandler 인터페이스는 invoke메서드 한개만 가진 간단한 인터페이스다.

- public Object invoke(Object proxy, Method method, Object[] args)

- 다이내믹 프록시가 클라이언트로부터 받는 모든 요청은 invoke() 메서드로 전달됨




다이나믹 프록시를 위한 팩토리 빈 : 449p


- 다이내믹 프록시 오브젝트는 일반적인 스프링의 빈으로는 등록할 방법이 없다.

- 스프링은 내부적으로 리플렉션 api를 이용하여 빈 정의에 나오는 클래스 이름을 가지고 빈 오브젝트를 생성하지만

문제는 다이나믹 프록시 오브젝트는 이런 식으로 프록시 오브젝트가 생성되지 않는다


사실 스프링은 클래스 정보를 가지고 디폴트 생성자를 통해 오브젝트를 만드는 방법 외에도, 빈을 만들수 있는 방법을 제공한다. 

대표적으로 팩토리 빈. 


< 팩토리 빈 450p>


- 스프링을 대신해서 오브젝트의 생성로직을 담당하도록 만들어진 특별한 빈

- 이를 가장 쉽게 구현하는 방법은 FactoryBean이라는 인터페이스를 구현하는 것이다.

- FactoryBean 인터페이스를 구현한 클래스를 스프링의 빈으로 등록하면 팩토리 빈으로 동작한다.





< 6.4 스프링의 프록시 팩토리 빈 462p> 


- ProxyFactoryBean은 프록시를 생성해서 빈 오브젝트로 등록하게 해주는 팩토리 빈이다.

- 순수하게 프록시를 생성하는 작업만을 담당하고 프록시를 통해 제공해줄 부가기능은 별도의 빈에 둘 수 있다.

- ProxyFactoryBean이 생성하는 프록시에서 사용할 부가기능은 MethodInterceptor 인터페이스를 구현해서 만든다.




<6.5 스프링 AOP >


- AOP란 무엇인가? 500p ~ 



"스스로 정리했을때는 관점에 따른 분리를 통해 하나의 모듈에만 집중할수 있게 하는 관심지향프로그램 기법."



: 비니지스 로직을 담은 UserService에 트랜잭션을 적용해온 과정을 보자






1. 트랜잭션 서비스 추상화


- 트랜잭션 경계설정 코드를 비지니스 로직을 담은 코드에 넣으면 특정 트랜잭션 기술에 종속되는 코드가 되버린다. 


- 서비스 추상화 기법을 적용


- 트랜잭션 추상화란 인터페이스와 DI를 통해 무엇을 하는지를 남기고, 그것을 어떻게 하는지 분리하는 것






2. 프록시와 데코레이터 패턴


- 여전히 비즈니스 로직 고 코드에는 트랜잭션을 적용하고 있다는 사실이 드러나 있다. 문제는 트랜잭션은 거의 대부분의 비즈니스 로직을 담은 메소드에 필요하다. 


- 이를 제거하기 위해 DI를 이용한 데코레이터 패턴을 적용


- 클라이언트가 인터페이스와 DI를 통해 접근하도록 설계하고, 데코레이터 패턴을 적용해서, 


비즈니스 로직을 담은 클래스의 코드에는 전혀 영향을 주지 않으면서 트랜잭션이라는 부가기능을 자유롭게 부과할 수 있는 구조를 만들었다.








3. 다이나믹 프록시와 프록시 팩토리 빈


- 프록시 클래스 없이도 프록시 오브젝트를 런타임 시에 만들어주는 나이나믹 프록시 기술을 적용


- 하지만 동일한 기능의 프록시를 여러 오브젝트에서 적용할 경우 오ㅡ젝트 단위로는 중복이 일어나는 문제는 해결하지 못했다. 


- JDK 다이나믹 프록시와 같은 프록시 기술을 추상화한 스프링의 프록시 팩토리 빈을 이용하여 다이나믹 프록시 생성 방법에 DI를 도입






4. 자동 프록시 생성 방법과 포인트 컷


- 트랜잭션 적용 대상이 되는 빈마다 일일이 프록시 팩토리 빈을 설정해줘야 한다는 부담


- 이를 해결하기 위해 스프링 컨테이너의 빈 생성 후처리 기법을 활용해 컨테이너 초기화 시점에서 자동으로 프록시를 만들어주는 방법을 도입했다. 


- 최종적으로 포인트컷 표현식이라는 좀 더 편리하고 깔끔한 방법을 활용해서 간단한 설정만으로 적용 대상을 손쉽게 선택할 수 있게 됐다.






5. 부가기능의 모듈화


- 지금까지 DI, 데코레이터 패턴, 다이나믹 프록시, 오브젝트 생성 후처리, 자동 프록시 생성, 포인트것과 같은 기법은 이런 문제들을 해결하기 위해 적용한 대표정인 방법이다. 


- 결국 핵심은 모듈이다.!!!






6. AOP 에스펙트 지향 프로그래밍 (504p)


- 부가기능을 어떻게 모듈화할 것인가 연구한 사람들은 이 모듈화 작업이 기존의 객체지향 설계 패러다임과는 구분되는 새로운 특성이 있다고 생각했다.


그것이 바로 에스펙트(aspect)




- 애스팩트 : 그 자체로 애플리케이션의 핵심기능을 담고 있지는 않지만, 애플리케이션을 구성하는 중요한 한가지 요소이고, 핵심기능에 부가되어 의미를 갖는 특별한 모듈




- 애플리케이션의 핵심적인 기능에서 부가적인 기능을 분리해서 애스펙트라는 모듈로 만들어서 설계하고 개발하는 방법을 애스펙트 지향 프로그래밍 (AOP) 라고 한다.




- AOP는 OOP를 돕는 보조적인 기술이지 OOP를 완전히 대체하는 새로운 개념은 아니다. 






AOP 적용기술 (506p)




1.프록시를 이용한 AOP


- 스프링은 다양한 기술로 조합해 AOP를 지원하고 있다


- 그중 가장 핵심은 프록시를 이용했다는 것


: 프록시로 만들어서 DI로 연결된 빈 사이에 적용해 타깃의 메서드 호출과정에 참여해서, 부가기능을 제공해 주도록 만들었다.




2.바이트코드 생성과 조작을 통한 AOP


- AspectJ 프레임워크를 사용하여 프록시를 쓰지 않는 대표적인 AOP 기술




- AspectJ는 프록시처럼 간접적인 방법이 아니라, 타깃 오브젝트를 뜯어고쳐서 부가기능을 직접 넣어주는 직접적인 방법을 사용한다.




컴파일된 타깃의 클래스 파일 자체를 JVM에 로딩되는 시점을 가로채서 바이트코드로 조작하는 복잡한 방법을 사용한다. 




- 왜 프록시를 사용하지 않고 이러한 방법을 사용할까??


1. 바이트코드를 조작해 타깃 오브젝트를 직접 사정해버리면 DI 컨테이너의 도움을 받아 프록시를 생성하지 않고 AOP를 적용할 수 있다.




2. 프록시 방식보다 훨씬 강력하고 유연한 aop가 가능하다.


- 프록시를 사용하면 대상이 메서드로 제한되지만, 바이트 코드를 직접 조작하면 오브젝트의 생성, 필드값의 조회와 조작, 스태틱 초기화 등의 다양한 부가기능을 수행할 수 있다.




- 타깃 오브젝트가 생성되는 순간에도 작업이 가능하다.




AOP의 용어 (508p)


1. 타깃

- 부가기능을 부여할 대상

- 핵심기능을 담은 클래스일 수도 있지만 경우에 따라서는 다른 부가기능을 제공하는 프록시 오브젝트 일수도 있다.



2. 어드바이스 = 부가기능

- 타깃에게 제공할 부가기능을 담은 모듈



3. 조인 포인트

- 어드바이스가 적용될수 있는 위치

- 스프링의 프록시 AOP에서 조인 포인트는 메서드의 실행 단계뿐

- 타깃 오브젝트가 구현한 인터페이스의 모든 메서드는 조인 포인트가 된다.



4. 포인트컷 = 대상을 선정

- 어드바이스를 적용할 조인 포인트를 선별하는 작업 또는 그 기능을 정의한 모듈

- 스프링 조인 포인트는 메서드의 실행이므로 스프링의 포인트컷은 메서드를 선정하는 기능을 갖고 있다.



5. 프록시 = 접근을 제어해주는 것

- 프록시는 클라이언트와 타깃 사이에 투명하게 존재하면서 부가기능을 제공하는 오브젝트

- DI를 통해 타깃 대신 클라이언트에게 주입되며

, 클라이언트의 메서드 호출을 대신 받아서 타깃에 위임

, 그 과정에서 부가기능을 부여한다. 

- 스프링은 프록시를 이용해 AOP를 지원한다.



6. 어드바이저

- 포인트컷과 어드바이스를 하나씩 갖고 있는 오브젝트

- 어떤 부가기능(어드바이스)을 어디에(포인트컷) 전달할 것인가를 알고 있는 AOP의 가장 기본이 되는 모듈

- 스프링 AOP에서만 사용되는 특별한 용어



7. 애스펙트

- AOP의 기본 모듈

- 한 개 또는 그이상의 포인트컷과 어드바이스의 조합으로 만들어지며 보통 실글톤 형태의 오브젝트로 `

- 따라서 클래스와 같은 모듈 정의와 오브젝트와 같은 실체(인스턴스)의 구분이 특별히 없다.


------------------------------------------------------------------------------------------

512~


< 6.6 트랜잭션 속성 > 

ex) this.transactionManager.getTransaction( new DefaultTransactionDefiniton() );


DefaultTransactionDefinition 의 용도가 무엇인가? 를 알아보자. 


1. 트랜잭션 정의

- DefaultTransactionDefinition이 구현하고 있는 TransactionDefinition 인터페이스는 트랜잭션의 동작방식에 영향을 줄 수 있는 네가지 속성을 정의하고있다.


1). 트랜잭션 전파 

트랜잭션 전파란 ? 

- 트랜잭션의 경계에서 이미 진행 중인 트랜잭션이 있을 때, 또는 없을 때 어떻게 동작할 것인가 결정하는 방식

- 독자적인 트랜잭션 경계를 가진 코드에 대해 이미 진행 중인 트랜잭션이 어떻게 영향을 미칠수 있는가를 정의하는 것이 트랜잭션 전파속성이다.


대표적인 트랜잭션 전파속성

a. PROPACATION_REQUIRED (프라퍼케이션 : 전파,보급   , 리콰이어드 : 필수)

- 가장많이 사용되는 트랜잭션 전파속성으로, 

진행중인 트랜잭션이 없으면 새로 시작하고, 이미 시작된 트랜잭션이 있으면 이에 참여


b. PROPACATION_REQUIRED_NEW

- 항상 새로운 트랜잭션을 시작


c. PROPACATION_NOT_SUPPORTED

- 트랜잭션 없이 동작하도록 만들 수 있다. 진행중인 트랜잭션이 있어도 무시한다.



2). 격리수준 (isolation level)

- 적절하게 격리수준을 조정해서 가능한 많은 트랜잭션을 동시에 진행시키면서 문제가 발생하지 않게 하는 제어

- 기본적으로 DB에 설정되어 있지만, JDBC 드라이버나 DataSource 등에서 재설정할 수 있고, 트랜잭션 단위로도 조정가능


3). 제한시간

- 트랜잭션을 수행하는 제한시간


4). 읽기전용

- 읽기전용으로 설정해두면 트랜잭션 내에서 데이터를 조작하는 시도를 막아줄 수있다




----

트랜잭션 정의를 바꾸고 싶다면 디폴트 속성을 갖고 있는 DefaultTransactionDefinition을 사용하는 대신 외부에서 정의된 TransactionDefinition 오브젝트를 DI 받아서 사용하도록 만들면 된다. 

하지만 이방법으로는 TransactionAdvice를 사용하는 모든 트랜잭션의 속성이 한꺼번에 바뀐다. 


원하는 메소드만 선택해서 독자적인 트랜잭션을 정의할 수 있는 방법이 설명된다. 

----




2. 트랜잭션 인터셉터와 트랜잭션 속성

메서드별로 다른 트랜잭션 정의를 적용하려면 어드바이스의 기능을 확장해야한다.



TransactionInterceptor

- 이미 스프링에서 편리하게 트랜잭션 경계설정 어드바이스를 사용할 수 있도록 만들어진 것

- 트랜잭션 정의를 메소드 이름 패턴을 이용해서 다르게 지정할 수 있는 방법을 추가로 제공


TransactionInterceptor 는  PlatformTransactionManager와 Properties 의 두가지 프로퍼티를 갖고 있다. 


매소드 이름 패턴을 이용한 트랜잭션 속성 지정




+ Recent posts