Whiteship's Note


Spring In Action 1장 ~ 4장 소스 코드





AJN에서 진행하고 있는 Spring In Action 스터디에서 사용할 소스코드 입니다. 발표 준비하면서 책에 나와있는 코드들을 돌려 본 코드 입니다.

1. @Configure 실패
2. AspectJ와의 연동 실패

두 개 모두 로드타임위빙 인가를 사용해야 하는데, 그 것을 어떻게 해야하는지 해메고 있어서 예제 코드 실습에 실패했습니다. 그 밖에 모든 코드는 책을 보시면서 따라해 보시거나 제가 만들어 둔 Test 코드를 실행하시면 모두 동작하는 것을 확인하실 수 있습니다.
top


Spring이 제공하는 AutoProxyCreator



classic Spring Aspect를 사용할 때 Target 하나 마다 일일히 ProxyFactoryBean을 만들어 주는 일은 완전 노가다 입니다. 이 노가다를 줄여주기 위해 Spring에서 제공하는 녀석들이 바로 AutoProxyCreator 입니다.

Spring이 제공하고 있는 AutoProxyCreator들을 살펴보겠습니다.
사용자 삽입 이미지
위와 같은 상속 구조를 가지고 있습니다.
interceptorNames 속성에 Proxy를 생성할 Target이 되는 bean의 이름들을 설정하여 여러 Proxy를 만들 수 있도록 하는 클래스입니다.
BeanNameAutoProxyCreator 사용 예
@Aspect 애노테이션이 붙어있는 클래스에 정의되어 있는 Adivce가 적용될 Pointcut을 가지고 있는 클래스들의 Proxy를 생성하는 클래스입니다. 이 클래스를 bean으로 등록해둬도 되지만 aop 네임스페이스를 사용하고 있다면 <aop:aspectj-autoproxy />만 추가하면 됩니다.
현재 BeanFactory에 등록되어 있는 Advisor들을 사용하여 그들의 대상이 되는 Target 들을 파악하여 Proxy를 만들어줍니다. 이 녀석 역시 그냥 bean으로 등록해두기만 하면 됩니다.
Auto-proxy creator that considers infrastructure Advisor beans only, ignoring any application-defined Advisors. 라고 써있는데.. 흠 Infra Advisor는 Spring에서 제공하는 인터셉터(DebugInterceptor, PerformanceMonitorInterceptor)를 말하는 것 같습니다. 이 녀석들이 적용될 Target의 Proxy만 만들는 것 같습니다. 흠.. 아마도 디버깅이나 테스트 용도로 사용하겠군요.
top


Spring 2.5에 추가되는 bean() joinpoint



참조 : http://blog.interface21.com/main/2007/09/24/the-new-bean-pointcut/

Classic Spring AOP를 사용할 때 여러 Target에 대해 각각의 proxyFactoryBean을 정의하는 것은 매우 귀찮은 일이고 XML도 방대해 지기 때문에 AutoProxyCreator를 사용했었습니다.

그 중에서도 BeanNameAutoProxyCreator는 XML에 등록되어 있는 bean 이름을 설정해 주면 해당 bean의 Proxy를 만들어 주는 편리한 API였지만, 단점은 aop 네임스페이스 기반 이나 @AspectJ 애노테이션 기반과 같이 사용할 수 없다는 것이였습니다.

Spring 2.5에서는 bean() 이라는 joinpoint 표현식을 제공하여 다음과 같이 다수의 target bean을 편리하게 지칭할 수 있게 됩니다.
사용자 삽입 이미지

Pointcut Join points selected in
bean(accountRepository) The bean named "accountRepository"
!bean(accountRepository) Any bean except the "accountRepository" bean
bean(*) Any bean
bean(account*) Any bean with name starting in "account"
bean(*Repository) Any bean with name ending in "Repository"
bean(accounting/showaccount) The bean named accounting/showaccount (designating, say, a controller handling that URL)
bean(accounting/*) Any bean whose name starts with "accounting/" (designating, say, any controller handling accounting-related URLs)
bean(accounting/*/edit) Any bean whose name starts with "accounting/" and ends with "/edit" (designating, say, any controller handling the edit operation functionality related to accounting)
bean(*dataSource) || bean(*DataSource) Any bean whose name ends with either "dataSource" or "DataSource"
bean(service:name=monitoring) The bean named "service:name=monitoring"

top


왜 Spring MVC 컨트롤러에 AOP가 적용되지 않을까?



왜그럴까요? 저도 궁금해서 관련 아티클도 읽고 소스코드도 좀 해봤는데요; 아직도 조금 긴가민가 합니다.

AOP / AJAX Enabled Controllers in Spring MVC

위 글을 참조하였습니다.

Controller 인터페이스를 직접 구현하여 컨트롤러를 만들면 handleRequest() 메소드에 AOP를 적용할 수 있습니다. 하지만 자주 사용하는 MultiActionController, SimpleFormController에는 AOP가 적용되지 않습니다.

왜냐면, AbstractController에서 Controller 인터페이스를 구현하고, handleRequest 메소드를 final 메소드로 구현했습니다. 그래서 AOP가 적용되지 않는다고 합니다.

왜 냐면, MultiActionController, SimpleFormController 들의 프록시 객체를 만들 때, 인터페이스 기반으로 Proxy를 만들지 않고 CGlib 라이브러리를 사용해서 하위클래스를 생성하여 Proxy를 만들려고 하는데 final 메소드를 가지고 있기 때문에 그것마저도 못하기 때문인듯 합니다.

사용자 삽입 이미지
위와 같은 상속구조에서 SampleController의 handleRequest() 메소드에 BeforeAdvice를 적용하는 Aspect를 다음과 같이 정의했습니다.

@Aspect
public class SampleAspect {

    @Pointcut("execution(* finalClassProxying.SampleController.handleRequest())")
    public void samplePointcut(){}

    @Before("samplePointcut()")
    public void sampleAdvice(){
        System.out.println("과연 될까?");
    }

}

테스트를 해보면 적용이 되지 않는 것을 확인할 수 있습니다.

public class FinalClassProxingTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("finalClassProxying/applicationContext.xml");
        SampleController controller = (SampleController) context.getBean("sampleController");
        controller.handleRequest();
    }
}

흠냐... 로그 메시지에서 final 메소드라서 Proxy객체를 만들 수 없다는 메시지가 보고 싶었는데 보지를 못했습니다... 그래서 위에 제가 생각한 것이 맞는 건인지 아리까리 합니다.



top


AOP 관련 질문 :: Before Advice의 객체를 Target 클래스로 넘겨주고 싶다.



메일을 받았습니다.

"XML을 사용하는 초간단 예제에 대한 질문인데요.
만약에 before메서드에서 생성한 객체를 target클래스에 넘겨야할땐 어떻게 해야되는지요.
방법이 있나요?"

답변은 다음과 같이 해드렸습니다.

"before메서드에서 생성한 객체를 target클래스에 넘겨야할땐"가 구체적으로 어떤 경우인지를 설명해주시면 더 좋겠습니다.

일 단은 Before Advice에서 target이 되는 클래스의 대상 메소드로 값을 넘기려면, Target이 되는 클래스에서 Before Advice를 가지고 있는 Aspect를 알아야 합니다. 그 말은 Aspect에 종속성이 생기게 된다는 것입니다.

제 생각에는 그런 경우가 발생한다면, Before Adivce보다는 Around Adivce를 사용하는 것을 추천하고 싶습니다. Around Advice를 사용하시면 대상이 되는 메소드에 넘겨줄 인자 들이나, 메소드의 반환 값등을 마음대로 바꿀 수 있으며, 심지어 대상이 되는 메소드를 실행하지 않을 수도 있습니다.

제 답변이 도움이 되셨으면 좋겠습니다."


top


휴.. Spring AOP와 AspectJ 연동 성공



이전 글의 예상이 맞았습니다.

AspectJ 파일(aj 확장자)을 ajc라는 것으로 컴파일을 해야 하는데 이 녀석을 Eclipse에서는 어떻게 컴파일 할 지 모르기 때문에 컴파일을 못해서 class 파일이 없으니까 결국 Spring 컨테이너는 class 파일이 없어서 AspectJ로 작성한 Aspect를 못 읽어 들이고 에러를 내고 만 것이였습니다.

해결책은 ajc 사용해서 컴파일 해도 되겠지만.. 커맨드 라인에서 무언가를 하는것이 상당히 귀찮은 저 같은 분들은 AJDT 플러그인을 설치하시면 됩니다.

AJDT 홈페이지에 가시면 Eclipse 버전에 따라 release 정보가 표시되어 있습니다.

저는 Eclipse 3.3을 사용하기 때문에 다음의 URL로 업데이트 사이트를 등록하고 플러그인을 설치했습니다.
http://download.eclipse.org/tools/ajdt/33/update

설치한 뒤 해당 프로젝트에 "Convert to AspectJ Project"를 실행해주면 aj 파일은 ajc 사용해서 자동으로 컴파일 해주고, aspecjrt.jar 파일을 클래스패스에 추가해 줍니다.

사용자 삽입 이미지

그럼 Advice가 적용되는 지점에 해당 Advice가 몇 개 적용되는지 보여 줍니다.
사용자 삽입 이미지

멋진 툴입니다.

Class를 못찾던 문제도 해결 됐고 Spring AOP와 AspectJ를 연동도 간단하게 해결되었습니다.
top


Aspect-Oriented Programming with Spring AOP and AspectJ



Interface 21의 아드리안이 발표한 PPT인것 같습니다. 구글에서 "spring aop aspectj"로 검색했더니 나왔습니다. 굉장히 멋진 발표였을 것 같은데 중간 중간 데모를 못보는 것이 너무 아쉽습니다.

Spring AOP에서 AsepctJ와 연동하는 부분이 막혀서 구글링을 하던 중이였는데 AspectJ의 aj 파일을 컴파일 해서 class 파일로 만들어야 할 것 같습니다. 안그러면 Spring 컨테이너에서 Bean으로 읽어 들일 때 ClassNotFoundException이 떨어지게 됩니다. 물론 그걸 Spring이 잡아서 CannotLoadBeanClassException이라고 좀 더 명확하게 이야기를 해줍니다.ㅋ

2006년 7월에 PPT 파일명을 보니.. Free Seminar... Interface 21에서 무료 세미나도 해주나 봅니다. 우워..... 부럽..
지만 한국에는 KSUG가 있죠.

top


POJO기반 Spring AOP



참조 : http://en.wikipedia.org/wiki/Plain_Old_Java_Object

문득 POJO가 궁금해 졌습니다. 대체 어떤 객체를 Plain Old Java Object라고 하는 것인지...말이죠. 어떤 클래스에 종속성을 가지면 안되는 것인지? 그런건 너무 규약이 심하니까(혼자서 하면 뭘 얼마나 혼자서 하겠습니까.) 그건 아닌거 같고, 특정한 인터페이스를 구현하면 안 되는 건가? 흠.. 이건 왠지 그럴듯하네.. 라고 생각만하다가 그냥 검색을 해봤습니다.


원문을 보시면 아시겠지만, 특정한 클래스를 상속 받거나, (스펙같은 역할을 하는)특정 인터페이스를 구현 해야만 하거나, 특정 애노테이션을 붙여야만 하는 클래스는 POJO가 아닙니다.

따라서 Spring AOP 중에서 aop 네임 스페이스를 사용하는 방법만이 POJO를 사용하는 Spring AOP라고 할 수 있겠습니다. Classic Spring AOP는 특정 인터페이스를 구현해야만 하고, @AspectJ는 특정 애노테이션을 붙여줘야 하기 때문이죠.
top


Spring AOP 사용 방법



간추리면 세 가지. 크개는 두 가지

1. 옛날 방법

- Spring AOP API를 사용하는 방법.

2. 2.0 방법

- @AsepctJ를 사용하는 방법
- <aop />를 사용하는 방법

옛날 방법이 훨씬 복잡하지만 그걸 알고 나면 2.0 방법은 누워서 떡먹기 입니다. 여기에 하나 더 추가하자면 AspectJ를 사용하는 방법이 있는데 특별히 필드 Jointpoint나 생성자 Jointpoint 또는 Spring에서 지원하지 않는 AspectJ Pointcut 표현식이 필요한 상황이 아니라면 위의 Spring AOP 만으로도 충분할 것 같습니다.
top


Classic 스프링 Aspect 만들기



1. Advice 만들기(Aspect에서 할 일을 모아 높은 클래스 입니다.)
Spring AOP(old) Advice

2. Pointcut 정의하기(XML에서 합니다. 정규 표현식과 AspectJ 표현식을 사용할 수 있습니다.)
Spring AOP(old) Pointcu
Spring AOP(old) Pointcut Implementationt

3. Advisor 만들기(XML에서 합니다. 1번과 2번에서 만든것을 묶어 주면 됩니다. 특화된 Advisor를 사용하면 2번을 굳이 만들지 않아도 됩니다.)
Spring AOP(old) Advisor

4. Proxy 만들기(XML에서 합니다. 이것이 바로 프록시 기반의 Aspect에 해당하게 됩니다. ProxyFactoryBean을 사용하여 타겟 클래스, 인터셉터(advisor), 인터페이스 속성을 지정해 줍니다.)
6.6. Proxying mechanisms
Spring AOP(old) ProxyFactoryBean 불편한 점


단점으로 설정할 내용이 많고, 몇 일 지나면 어떻게 만들었는지 잊어버리게 됩니다.
top


AOP 소개



객체 지향에서의 재사용에 관한 두 가지 기술, 상속(inheritance)과 위임(delegation)의 한계
- 상속 : 특정 타입에 얽매이게 된다. "왜 상속이 나쁜가?" 라는 아티클이 있슴.
- 위임 : 애플리케이션이 복잡해진다. 데메테르의 법칙에 위배 될 가능성이 높아짐.

Aspect를 사용하여 상속과 위임을 대체하여 좀 더 나은 애플리케이션을 만들 수 있다.
- 크로스 커팅 concern들을 모듈화 할 수 있다.
    - 흩어져 있는 concern과 관련된 코드를 한 곳에 모을 수 있다.
    - 서비스 계층이 깨끗해 진다.

AOP 용어 정리
  1. 2006/12/29 1. AOP 용어 정리 (2)
스프링의 AOP 특징
- 스프링에서 AOP를 사용할 수 있는 방법은 여러가지다.
    - 프로그래밍을 통해 프록시 기반 AOP를 사용하는 방법(어떤 버전에서도 사용 가능함)
    - AspectJ와 연동하여 사용하는 방법(어떤 버전에서도 사용 가능함)
    - @AsepctJ 를 사용하는 방법(Spring 2.0부터 사용 가능)
    - 오직 POJO 만을 사용하는 방법(Spring 2.0부터 사용 가능)
- AspectJ와 연동하여 사용하는 방법을 제외 하면 모두 프록시 기반이다.
- 오직 메소드 호출 조인 포인트만 지원한다.
- 스프링 어드바이스는 자바로 작성한다.
- 런타임시 프록시 객체를 생성한다.
    - 프록시 객체를 만드는 두 가지 방법이 있다.
       - 인터페이스 기반으로 만들 때 : JDK 의 Proxy 클래스 사용 -> 별도의 라이브러리 필요 없슴.
       - Concrete 클래스 기반으로 만들 때 : CGLIB 사용 -> cglib.jar 필요함. -> final 메소드는 어드바이스 될 수 없다.

top