Whiteship's Note


AspectJ와의 연동을 고려한다면, 포인트컷을 최소화 해야합니다.

AOP : 2008.10.08 16:37


무슨 이야기냐면, 최소 권한 원칙인가... 그런거랑 비슷한겁니다.

바로 예제를 보면서 살펴보죠.

@Aspect
public class HibernateExceptionToDataAccessException {

    @Pointcut("@within(org.springframework.stereotype.Repository)")
    public void accountHibernateExceptionInDao(){}

    @AfterThrowing(pointcut="accountHibernateExceptionInDao()", throwing="e")
    public void translateHibernateException(HibernateException e){
        throw SessionFactoryUtils.convertHibernateAccessException(e);
    }

}

이 애스팩트는 하이버네이트 예외를 스프링의 DataAccessException으로 변환해주는 애스팩트입니다. 이 녀석은 하이버네이트로 DAO 만드는 여러 방법 중에 가장 깔끔한 방법을 사용할 때 쓰면 좋고 안 써도 별로 상관없는(하이버네이트 버전이 올라가면서 하이버네이트 예외도 uncatched exception으로 바꼈으며 계층 구조도 세밀하게 나눠뒀기 때문입니다.) 그런 애스팩트입니다. 그래도, 버전 올리기 힘든 하이버 예전 버전을 사용하는 라이브러리를 사용해야 한다면 유용하겠죠.

어쨋든, 본론으로 돌아가서..

저렇게 만들어둔 애스팩트에 문제점이 보이나요? 저도 방금전까진 몰랐습니다. 일단, 저 애스팩트의 포인트컷은 이해가 가시죠? @Repository 애노테이션을 가지고 있는 클래스의 모든 조인포인트를 나타낸 겁니다.

문제는 바로 이 조인포인트.. 이게 핵심입니다. 스프링에서는 메소드 실행 조인포인트만 사용하기 때문에, 저 애스팩트를 스프링 AOP에서 사용할 땐, 원하던 메소드에만 적용이 될 겁니다. 하지만, 만약 저 애스팩트를 AspectJ와 연동해서 사용한다면? 어떤 일이 벌어질까요?

사용자 삽입 이미지

캬오.. 43개??? 말도 안돼. 내가 테스트 할려고 만든 DAO가 몇개나 된다고.. 그 안에 메소드도 거의 한 두 개밖엔 안만들었는데.. 왠 43개.... 바로 크로스 레퍼런스 뷰를 열고 확인해봤습니다.

사용자 삽입 이미지

캬오~~~~ 마이 미스테이크... 처방이 필요합니다. 처방은 간단하기 때문에 비밀! 캬캬캬.(이번주 KSUG 세미나에서 공개하도록 하죠.) 처방후에는..

사용자 삽입 이미지

이렇게 AspectJ에서도 메소드 실행 조인포인트에만 걸 수 있습니다. 음하하하..
신고
top


AspectJ로 final method에도 위빙하기

Spring/Chapter 6 : 2008.10.02 10:03


소스 코드는 이전과 동일합니다. 그 상태에서 프록젝트에 AspectJ Nature를 추가해줍니다.

사용자 삽입 이미지

그런 다음에 프로젝트 클린을 하여 기존의 클래스파일을 비우고 다시 컴파일하게 합니다.(Alt + P, N, 엔터) 지금, 저는 아무런 .aj 파일도 만들지 않았습니다. 그냥 이전에 만들어둔 Spring @AOP를 사용하고 있을 뿐입니다.

@Aspect
public class FinalHelloAspect {

    @Before("execution(* org.opensprout.sandbox.proxy.withfinal.FinalHello.*(..))")
    public void withFinalAdvice() {
        System.out.println("before advice from FinalHelloAspect");
    }

}

이 녀석이죠. AspecJ 프로젝트가 됐기 때문에, 빌드타임에 저 애스팩트의 포인트컷에 해당하는 클래스 파일을 조작해서 "새로운 .class 파일"을 생성하게 될 겁니다.

사용자 삽입 이미지

컴파일이 끝난 후 AspectJ IDE가 발동한 모습이니다. 포인트컷 마다 저런 화살표가 표시됩니다. 보시면 final 메소드에도 화살표가 표시되어 있습니다. 해당 코드와 Cross References 뷰를 동기화 시켜두면, 해당 지점에 적용될 어드바이스까지 표시됩니다.

사용자 삽입 이미지

놀랍습니다. 안 그러세요? @Aspect 캬오~ 하긴, 그리 놀랍지도... @Aspect 애노테이션이 aspectj 프로젝트에 속한 애노테이션이니까,..

사용자 삽입 이미지

이 결과를 보면 더이상 프록시를 사용하고 있는것이 아니기 때문에, Advised 라는 인터페이스는 사용할 수 없다는 것을 볼 수 있습니다. ClassCastException이 보이시죠?

흠냐;;

회사 청소해야되서 이만.. 황급히 마무리 합니다.
신고
top


CGLIB 프록시 제약 사항 테스트.

Spring/Chapter 6 : 2008.10.02 09:10


@Component
public class FinalHello implements Hello {

    public String hi() {
        return "hi";
    }
    
    public final void finalHi(){
        System.out.println("안녕");
    }
    
}

위 클래스에 모든 메소드 호출을 포인트컷으로 간단한 Adivce를 적용하여 CGLIB 프록시를 생성했을 때, 다음과 같은 결과를 확인할 수 있었습니다.

1. final 메소드를 호출할 경우 Advice를 적용하지 않음.
2. final이 아닌 메소드에는 Advice가 적용됨.

이런 간단한 테스트를 해보기 전에는 막연히 final 메소드가 있으면 CGLIB 프록시를 못 만드는 건가 싶었는데, 그게 아니었습니다.

한 가지 더 확인해봤습니다. 기본 생성자(인자가 없는 생성자)가 반드시 있어야 한다고 본것 같아서, 그걸 확인해봤습니다. 위의 경우에는 기본 생성자가 있는 상태로 테스트를 했으니까 기본 생성자 없이 테스트를 한 번 더 해보면 되겠죠.

@Component
public class FinalHello implements Hello {
   
    String hi;
   
    public FinalHello(String hi) {
        this.hi = hi;
    }

    public String hi() {
        return hi;
    }
   
    public final void finalHi(){
        System.out.println("안녕");
    }
   
}

그리고 위에서 실행했던 테스트 코드와 애스팩트를 그대로 사용해봤습니다.


java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:203)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:255)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:93)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:130)
    at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
    at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
    at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'finalHello' defined in file [C:\workspace\osaf\target\test-classes\org\opensprout\sandbox\proxy\withfinal\FinalHello.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.opensprout.sandbox.proxy.withfinal.FinalHello]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.opensprout.sandbox.proxy.withfinal.FinalHello.<init>()
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:883)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:839)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:440)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:729)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:381)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:84)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:42)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:173)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:199)
    ... 16 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.opensprout.sandbox.proxy.withfinal.FinalHello]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.opensprout.sandbox.proxy.withfinal.FinalHello.<init>()
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:58)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:877)
    ... 33 more
Caused by: java.lang.NoSuchMethodException: org.opensprout.sandbox.proxy.withfinal.FinalHello.<init>()
    at java.lang.Class.getConstructor0(Class.java:2706)
    at java.lang.Class.getDeclaredConstructor(Class.java:1985)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:54)
    ... 34 more

맞네요. 두 번쨰 제약 사항까지 살펴봤습니다.

CGLIB 프록시가 JDK 프록시에 비해 성능도 좋고, concrete 클래스의 프록시도 만들어 주기 때문에, 좋긴 한데, 위의 두 개의 제약 사항(final 메소드에는 어드바이스 적용 불가(상속을 못하니까.), 기본 생성자 필요.)에 주의 하면서 사용해야겠습니다.

만약에 이런 경우엔 어떻게 해야 할까요.

1. 구현하는 인터페이스 없음 ==> JDK 프록시 사용불가
2. 기본 생성자 없음 ==> CGLIB 사용불가
3. 어드바이스 적용하고자 하는 메소드가 fianl ==> 역시 CGLIB 사용불가
4. 하지만 AOP 하고파. ==> Spring AOP로는 불가능.

결론(지금은 머리로만 생각한 겁니다. 검증은 조금 뒤에..)
==> Spring AOP + AspectJ 연동해서, 빌드 타임에 aspectj-waever를 사용하던가, 클래스 로딩 타임에 loadtime-weaving을 하면 될 것입니다. AspectJ를 사용한 위빙은 프록시를 만드는게 아니라, 바이트코드랑 .aj 파일을 조작해서 타겟 클래스에 대한 .class파일을 다시 생성하고 그 코드를 사용하는 것이기 때문에 런타임시에 부하도 없고 위와 같은 Spring AOP 제약에서 벗어날 수 있으리라 봅니다.
신고
top


Spring AOP 어디다 쓰면 좋을까?

Spring/Chapter 7 : 2008.09.16 14:58


어드바이스 종류 별로 생각해 보는게 좋을 듯 합니다. 어드바이스 특징이나 기타 자세한 설명은 생략하겠습니다. 제 블로그 어딘가에 다 있을 겁니다.ㅋ

1. Before 어드바이스
- 보안에 쓰면 좋겠다. 해당 메소드 실행 전에 인증과 권한을 체크해서, 없으면 예외를 던지도록 할 수 있겠다.
- 로깅은 어떨까.. 어떤 메소드를 실행하기 전에 해당 메소드 실행하겠다고 로그 메시지를 남기는거..

2. After returning 어드바이스
- 결과값을 확인해야 할 경우. 결과값이 특정 값이면 예외를 던지도록...
- 흠.. 매우 사용처가 난감하네. 이미 메소드 호출 한 다음이라.. 뭘 해야 한담.
- 메소드 호출 한 뒤에 로그 메시지를 남기도록 할까?

3. Throws 어드바이스
- 예외 로그 처리. 특정 예외가 발생했을 때 로그를 남길 수 있겠다.
- 예외 계층 구조를 바꿀 수 있겠다. Hibernate 예외를 스프링 예외로 변환하는 작업을 해본적이 있었지..

4. Around 어드바이스
- 트랜잭션 처리는 아마 이 녀석이 할 것 같다. 특정 메소드 실행 전에 트랜잭션 열고, 메소드 호출하고 트랜잭션을 커밋하거나 롤백해야 하니까.
- 로깅을 이걸로 할까. 그러면 Before, After, Throws에서 하려던 로깅을 이걸로 다 할 수 있으니..
- 성능측정. 스프링 레퍼런스에 StopWatch 예제가 있었던 것 같다. 개발 할 때만 만들어서 돌려보고 병목지점 발견하는데 요긴하게 쓸 수 있을 것 같기도 하다.

전반적으로 AOP는 기존 코드를 건드리지 않고 애플리케이션에 특정 행위를 추가할 수 있다는 것이 매력적인 것 같다.
신고
top


[Spring Masters] 스프링 AOP 학습 참가 시험 문제 공개

모하니?/Planning : 2008.08.28 14:15


Spring AOP 학습 이전에 필수로 알고 있어야 할 스프링 지식들을 확인하는 시험입니다. 24시간 안에 답을 적어서 저에게 메일로 제출해주세요.

1.    FactoryBean에 대해서 설명하세요.
2.    FactoryBean에서 getObject()가 돌려주는 객체 말고, FacotryBean 자체를 받아오려면 어떻게 해야 하나요?
3.    BeanFactory와 ApplicationContext의 공통적인 Life-cycle 클래스들의 나열하고 각자의 역할들을 설명하세요.
4.    Bean을 생성하고 소멸시킬 때 특정 메소드를 호출하는 방법에는 어떤 것들이 있으며, 그 방법 들 중에 어떤 방법이 더 좋을지, 이유와 함께 설명하세요.
5.    Bean들 사이에서 Circular Dependency가 있을 경우 어떤 일이 발생하는지, 그리고 해결할 수 있는 방법들을 나열해 주세요.
6.    Autowiring을 사용하여 byType으로 빈을 주입할 때, 같은 타입의 빈이 두 개 이상일 때 어떤 현상이 발생하나요?
7.    Lookup method injection과 Arbitrary method replacement가 무엇인지 설명하세요.
8.    @Autowired는 빈의 타입을 사용하여 종속성을 주입합니다. 이 때, 같은 타입의 빈이 여러 개라면, 어떻게 해야 하나요?
9.    <context:component-scan> 설정을 등록해보세요. 기본 패키지는 me.whiteship 이며, 단, org.springframework.stereotype.Controller 애노테이션이 붙어있는 것들은 컴포넌트 스캔 대상에서 제외합니다.
10.    <tx:annotation-driven transaction-manager="transactionManager"/> 이 빈 설정에는 불필요한 설정이 있습니다. 그 설정이 무엇이며 이유가 뭔가요?

다음은 OX 퀴즈입니다.

11.    웹 서버에서 동작할 스프링의 Singleton Scope 빈은 반드시 Thread-Safe 해야 한다.
12.    스프링에서 선언적인 트랜잭션 관리를 할 때는 반드시 Proxy(CGLib이던 JDK의 Proxy건)를 사용해야 한다.
13.    ApplicationContext는 모든 Singleton 스코프의 빈을 초기에 생성한다.
14.    JtaTransactionManager는 nested transactions을 지원한다.
15.    ApplicationContext – B가 ApplicationContext – A를 상속하고 있을 때, 즉 A가 부모, B가 자식 관계일 때, A에 있는 빈이 B에 있는 빈을 참조할 수 있다.

마지막으로 Spring Masters에서 스프링 AOP 학습을 하려는 이유와 목적이 무엇인지 말씀해주세요. 수고하셨습니다.

시험을 치르고자 하시는 분들은 저에게 메일을 주시면 위와 동일한 문제를 담고 있는 워드 파일을 보내드립니다. 시험 방식은 오픈북이며, 시간 제한은 24시간입니다. 오늘 보내드리면, 적어도 내일 저녁까지는 제 메일 함으로 와야 합니다. 그럼 제가 답변들을 검토한 다음 피드백과 함께 참가 가능 여부를 메일로 보내드리겠습니다.

위 문제들이 너무 쉽다고 스터디도 쉽게 보시면 안 됩니다. 위 문제들을 풀 수 있느냐가 중요 한 게 아닙니다. 이 시험은 스프링 AOP 스터디 참가 조건 중 하나 일 뿐이라는걸 알아주세요.

본 글에 댓글로 질문에 대한 답변이나 또 다른 질문을 하시는 건 자제해주세요. 경우에 따라서는 삭제할 수도 있습니다. ;)
신고
top


HibernateExceptionToDataAccessExceptionApsect 구상 중

모하니?/Coding : 2008.02.22 12:26


어제랑 오늘은 회사 이사를 하느라고 공부를 제대로 못하겠네요. 아 삭신이.. 쑤시네요. ㅠ.ㅠ 살이 조금이라도 빠지기를 바라며.. 각설하고 본론으로 들어갑니다.

스프링에서 Hibernate를 사용해서 DAO를 구현하는 방법은 세 가지가 있습니다. 각각은 다음과 같은 특징이 있습니다.

HibernateTemplate

  • JDBC Template과 비슷한 방식이다. 기존 스프링 사용자들에게 가장 익숙한 방법.
  • Session에 직접 접근할 필요가 있을 때는 콜백 메소드를 사용해야 한다.
  • 스프링 클래스를 사용해야만 한다.
  • HibernateDaoSupport를 사용하면 SessionFactory의 세터를 만들 필요가 없다.

Spring 기반 DAO

  • 콜백을 사용하지 않고도 Session에 직접 접근할 수 있다.
  • HibernateDaoSuppoer의 getSession(Boolean)을 사용한다.
  • unchecked exception은 물론이고 checked exception도 던질 수 있다.
  • convertHibernateAccessException()를 사용해서 하이버네이트의 예외를 스프링의 DAE 형태로 바꿀 수 있다. 하지만 try-catch를 해서 다시 던져야 한다.

Hibernate 3 API

  • 하이버 3이 "contextual Sessions"이라는 개념으로 하이버 자체에서 트랜잭션 당 Session 하나를 관리한다.
  • 스프링 코드를 하나도 사용하지 않는다.
  • 엊그제 저녁 토비형이 언급한 부분

Fortunately, Spring's LocalSessionFactoryBean supports Hibernate's SessionFactory.getCurrentSession() method for any Spring transaction strategy, returning the current Spring-managed transactional Session even with HibernateTransactionManager. Of course, the standard behavior of that method remains: returning the current Session associated with the ongoing JTA transaction, if any (no matter whether driven by Spring's JtaTransactionManager, by EJB CMT, or by JTA).


예전에 정리해둔 내용인데 다 까먹었서, 엊그제 토비형이 물어봤는데 이상한 대답을 했습니다. 머리에서 점점 사라져 가는 스프링.. 난 널 원해.. 내 머리에서 나가지 말아죠.

어쨋거나. 세 번째 경우의 DAO의 코드가 가장 심플한데 딱 하나의 단점(?)을 가지고 있습니다. 바로 예외와 관련된 부분인데, 하이버 3.0 부터 Unchecked Exception으로 바꾸긴 했지만, 스프링의 Data Access Exception을 꼭 사용하고 싶을 수도 있겠습니다.

예외 처리가 필요 없거나, 하이버네이트를 매우 많이 사용하는 애플리케이션이라면 그냥 사용하면 되겠지만, 스프링의 DAE를 사용하여 OptimisticLockingFailureException 이런 에러를 잡아 내고 싶다면, 이런 제약 사항이 별로 달갑지 않을 것입니다.

그래서.. 생각 해본 게 바로 HibernateException을 DataAccessException으로 바꿔주는 Aspect입니다. HibernateException이 발생하면, 해당 예외를 잡아서 DataAccessException으로 바꿔서 다시 던지는 역할을 하는 Aspect를 만들고 싶습니다. 재미로..

Apsect 구현은 간단하게 Spring의 @Aspect 를 사용하는 방법과 AspectJ를 사용하여 구현한 다음 Spring과 연동하는 방법이 있는데 전자를 먼저 해보고, 잘 되면 후자를 해보겠습니다.

ps:  설마 이미 누가 만들어 두진 않았겠죠. 간단한거라.. 그랬을 수도 있겠지만 재미삼아 ㄱㄱㅆ
신고
top


Spring AOP 예제 코드

모하니?/Coding : 2007.09.28 13:40



사용자 삽입 이미지
패키지 구조
aopSchema 에는 aop 스키마 기반의 Spring AOP 예제 코드
aspectAnnotation 에는 @AspectJ 기반의 Spring AOP 예제 코드
aspectJ 에는 Spring에서 AspectJ를 사용하는 예제 코드
classicSpringAOP 에는 Spring 2.0 이전의 Spring AOP 예제 코드가 담겨있습니다.

주의사항
문자열 인코딩은 UTF-8 입니다.
AJDT 플러그인과, Spring IDE 2.0 플러그인을 설치 해주시면 유용합니다.
각 패키지 별로 ~~~Test 라는 클래스를 실행하시면 됩니다.

*위의 소스코드는 AJN에서 제가 진행하고 있는 Spring In Action 세 번째 스터디에서 사용 할 예정입니다.
따라서 자세한 설명은 온라인에서 하지 않습니다. 문의 사항이 있으시면 메일(블로그 바닦에 있습니다.)을 보내 주세요.


2007/09/05 - [Spring In Action/4. Advising beans] - 휴.. Spring AOP와 AspectJ 연동 성공
2007/09/10 - [Spring IDE] - Spring IDE 2.0 활용하기2
2007/07/02 - [Good Tools] - Eclipse 3.3 Europa 설치 뒤 할 일


신고
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


Spring AOP APIs

Spring/Chapter 7 : 2007.04.08 09:02


7.1. Introduction

이전 장에서 배운 것은 Spring 2.0에서의 AOP이고 이번에는 좀 더 하위 레벨의 1.2 방식을 알아보겠습니다.

Spring AOP(old) 특징

7.2. Pointcut API in Spring

Pointcut 인터페이스를 구현한 클래스들을 제공하며 이 클래스들을 사용하여 포인트컷을 만들고 재사용할 수 있습니다.

Spring AOP(old) Pointcut
Spring AOP(old) Pointcut Implementation
기선 씨네마 :: Pointcut

7.3. Advice API in Spring

모든 advice는 bean으로 등록하며 per-class와 per-instance 라이프 사이클이 존재합니다.

Spring AOP(old) Advice
기선 씨네마 :: Advice

7.4. Advisor API in Spring

advice와 pointcut을 하나씩 묶어 놓은 것입니다. DefaultPointcutAdvisor  를 사용하여 기본적으로 묶을 수 있습니다.

Spring AOP(old) Advisor

7.5. Using the ProxyFactoryBean to create AOP proxies

ProxyFactoryBean을 사용하여 AOP proxy를 만드는 방법을 설명합니다.

7.6. Concise proxy definitions

중복되는 설정이 많을 때는 parent 속성을 사용하면 좋습니다.

7.7. Creating AOP proxies programmatically with the ProxyFactory

설정이 아닌 코딩을 통해 ProxyFactory를 사용하는 방법입니다.

7.8. Manipulating advised objects

...

7.9. Using the "autoproxy" facility

매번 ProxyFactoryBean으로 등록하는 것이 아니라 Autoproxy를 사용하면 BeanPostProcessor가 알아서 만들어 줍니다.

Spring AOP(old) ProxyFactoryBean 불편한 점
Autoproxy
BeanNameAutoProxyCreator 사용 예

7.10 Using TargetSources

...

7.11. Defining new Advice types

...

7.12. Further resources

JPetStore를 보라고 하는군요.
신고
top


6.2. @AspectJ support

Spring/Chapter 6 : 2007.04.03 14:10


@AspectJ 는 Java 5에 추가된 어노테이션을 사용하여 aspect를 정의하는 스타일을 말하며 이것은 AspectJ 라이브러리를 사용하여 AspectJ 5와 동일한 방법으로 aspect와 pointcut을 정의할 수 있습니다.

하지만 Spring AOP를 사용하여 실행하기 때문에 AspectJ의 weaver나 complier는 사용하지 않아도 됩니다. 그것들을 사용하면 AspectJ의 모든 기능을 사용할 수 있습니다.

6.2.1. Enabling @AspectJ Support

autoproxing을 사용하여 자동으로 aspect가 적용될 bean의 프록시를 만들게 됩니다. 이렇게 하려면 설정파일에 간단한 설정을 추가해 주면 됩니다.

스키마 기반의 XML에서는 다음의 한 줄을 추가하면 됩니다.
<aop:aspectj-autoproxy/>

DTD 기반의 XML에서는 다음과 같이 적어 줍니다.
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />

아마도 내용은 동일한데 스키마 기반이라 네임스페이스를 달리하여 좀 더 간단하게 선언할 수 있는듯 합니다.

XML 스키마(Schema) (2)

6.2.2. Declaring an aspect

@Aspect를 클래스 선언 위에 붙여 줍니다. 그리고 bean으로 등록하여 주기만 하면BeanPostProcessor가 알아서 찾아서 Spring AOP 설정으로 사용하게 될 겁니다.

물론 일반 클래스기 때문에 평범한 클래스처럼 사용할 수 있습니다. 아마도 그렇기 때문에 투명성(transparency)을 보장하게 됐다는 것이 아닌가 싶습니다. 자기 자신이 Aspect로 사용되지만 일반 클래스와 다를바가 없기 때문이죠.(물론 어노테이션이 좀 붙는 건 어쩔 수 없겠지만요.ㅋㅋ)

Enabling @AspectJ Support

6.2.3. Declaring a pointcut

pointcut 선언은 두 부분으로 나눠집니다. pointcut 서명(signature) 부분과 표현(expression) 부분입니다.
서명 부분은 일반 메소드 선언 부분과 동일합니다.(반드시 리턴타입이 void 여야 합니다.)
표현 부분은 @Pointcut 을 사용하며 AspectJ 5의 형식을 사용합니다.

초간단 @Pointcut 과 @Advice 예제
@Pointcut Designator

6.2.4. Declaring advice

pointcut 표현식과 함께 사용할 수 도 있고 pointcut 이름을 사용하여 특정 메소드의 실행 전, 후, 예외가 발생했을 때 등에 할일을 추가할 수 있습니다.

@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
이런식으로 @Before 를 사용하여 before advice를 만들 때 이 advice가 적용될 포인트 컷의 이름을 줄 수 있으며

@Before("execution(* com.xyz.myapp.dao.*.*(..))")
이렇게 직접 pointcut 표현식을 사용하여 advice가 적용될 지점을 나타낼 수도 있습니다.(in-place pointcut)

같은 pointcut이 여러 advice에서 사용될 거라면 이름을 사용하는 방법이 좋겠죠.

@AfterReturning 어드바이스 만들기
@Around 어드바이스 예제
Advice parameters
Advice ordering

6.2.5. Introductions

@DeclareParents 을 사용하여 선언하며 지정된 타입들이 새로운 부모 클래스를 상속 받도록 합니다.

새로 추가 할 메소드나 필드를 모아놓은 인터페이스를 만들고 그 인터페이스를 구현한 클래스를 만듭니다. 그리고 이 인터페이스의 기능을 추가할 대상 클래스들을 지정해 주고 구현한 클래스를 묶어 주면 대상 클래스들도 해당 인터페이스 타입으로 사용할 수 있게 됩니다. 즉 새로운 메소드와 필드를 추가적으로 사용할 수 있게 됩니다.

Introduction 예제

6.2.6. Aspect instantiation models

@Aspect의 속성으로 perthis와 pertarget을 사용할 수 있습니다.

perthis는 해당 포인트컷에 매칭되는 메소드를 실행할 때 새로운 Aspect 객체를 만들고 그 메
소드를 가진 객체와 연관을 맺게 됩니다. 즉 타겟 객체와 묶이게 되는거죠. 해당 타겟 객체와 연관된 aspect 객체가 만들어 지기 전가진 advice가 적용되지 않습니다.

pertarget는 perthis와 다른건 포인트컷에 매칭되는 메소드를 가진 객체들 마다 새로운 aspect객체를 만들지 않고 딱 하나만 만든다는 것만 다르고 나머진 같습니다.

6.2.7. Example

around advice를 사용하여 PessimisticLockingFailureException을 잡아서 처리하고 사용자에게 안 보이도록 하는 aspect를 만들어 봅니다.
신고

'Spring > Chapter 6' 카테고리의 다른 글

Schema 기반 Spring AOP 희한한 것  (0) 2007.04.05
초간단 Schema 기반 Spring AOP  (0) 2007.04.05
Introduction 예제  (0) 2007.04.04
Advice ordering  (0) 2007.04.04
Advice parameters  (0) 2007.04.03
6.2. @AspectJ support  (0) 2007.04.03
@Around 어드바이스 예제  (0) 2007.04.03
@AfterReturning 어드바이스 만들기  (0) 2007.04.03
@Pointcut Designator  (0) 2007.04.02
@Aspect 붙은 클래스끼리는 상속이 안 됩니다.  (0) 2007.04.02
초간단 @Pointcut 과 @Advice 예제  (0) 2007.04.02
top


6.3. Schema-based AOP support

Spring/Chapter 6 : 2007.04.01 23:32


Java 5 를 사용하지 못하거나 단순히 XML 설정을 좋아하는 분들은 스키마 기반의 AOP 설정을 사용하여 @AspectJ에서 했던 모든 것을 할 수 있습니다.

단 aop 네임스페이스를 사용하기 위해서는 설정 파일로 스키마 기반의 XML 파일을 사용해야 합니다.
<!-- XML Schema-style -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<!-- <bean/> definitions here -->

</beans>

aop 설정은 모두 <aop:config> </aop:config> 안에 들어가야 하며 이 태그는 <beans> </beans> 내부에서 여러 번 사용할 수 있습니다.

초간단 Schema 기반 Spring AOP

6.3.1. Declaring an aspect

aspect는 일반 클래스로써 그냥 bean으로 등록을 해둡니다. 포인트컷과 어드바이스에 대한 정보는 XML로 나타낼 수 있습니다.

포인트 컷은 @Pointcut의 표현식을 그대로 xml 설정에 적어 주면 되고, advice로 사용할 메소드의 이름을 역시 xml에 적어 주면 될 것 같습니다.

어찌됐든 aspect 역할을 할 bean을 <aop:config> 안에 <aop:aspect> 태그의 ref 속성으로 정의합니다.

Schema 기반 Spring AOP 희한한 것
Schema 기반 Spring AOP 포인트컷 만들 때 주의 할 것

6.3.2. Declaring a pointcut

pointcut은 <aop:config> 또는 <aop:aspect> 태그 안에 <aop:pointcut> 태그를 사용하여 정의할 수 있습니다.

<aop:aspect> 로 정의하면 그 aspect 내에서만 사용할 수 있으며 <aop:config> 바로 하위에 정의하면 여러 aspect에서 사용할 수 있습니다.

6.3.3. Declaring advice

@AspectJ 에서 사용 할 수 있었던 다섯 가지의 Advice 모두 정의할 수 있습니다.

more..

<aop:after-returning> 어드바이스에서 리턴값 받아오기
<aop:around> 어드바이스 예제
Schema 기반 Advice parameters

6.3.4. Introductions

<aop:declare-parents> 를 사용합니다.

<aop:declare-parents
      types-matching="com.xzy.myapp.service.*+",
      implement-interface="UsageTracked"
      default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>
 
  <aop:before
    pointcut="com.xyz.myapp.SystemArchitecture.businessService()
              and this(usageTracked)"
    method="recordUsage"/>

Schema 기반 Introduction

6.3.5. Aspect instantiation models

스키마 기반에서 제공되는 건 singleton 모델 뿐이고 차후에 나머지도 제공될 예정입니다. 앞에서 살펴봤던 @Aspect 기반에서는 perTarget(singleton) 또는 perThis(peototype) 속성을 사용하여 선택할 수 있었습니다.

6.3.6. Advisors

Advisor 컨셉은 Spring 1.2에서 왔으며 AspectJ에 있는 개념은 아닙니다. AspectJ 스타일의 Pointcut 표현식을 사용할 수 있다는 장점이 있으며 Advisor에서 합 칠 Advice bean은 반드시 이전 유형의 Advice들이 구현해야 했던 인터페이스들을 구현하고 있어야 합니다.

6.3.7. Example

이전 글
의 예제를 스키마 기반으로 다시 작성합니다.
신고

'Spring > Chapter 6' 카테고리의 다른 글

6.2. @AspectJ support  (0) 2007.04.03
@Around 어드바이스 예제  (0) 2007.04.03
@AfterReturning 어드바이스 만들기  (0) 2007.04.03
@Pointcut Designator  (0) 2007.04.02
@Aspect 붙은 클래스끼리는 상속이 안 됩니다.  (0) 2007.04.02
초간단 @Pointcut 과 @Advice 예제  (0) 2007.04.02
Enabling @AspectJ Support  (0) 2007.04.02
6.3. Schema-based AOP support  (0) 2007.04.01
6.1. Introduction  (0) 2007.04.01
Spring 2.0 AOP  (2) 2007.03.27
Aspect Oriented Programming with Spring  (0) 2007.03.26
top


BeanNameAutoProxyCreator 사용 예

AOP : 2007.04.01 11:06


    @Test
    public void anotherClassesJoinPoint() {
        keesun.getAge();
        movie.getName();
        hein.getCard().getPoint();
    }

Customer, Movie, MemberShipCard 객체에 있는 getter들이 호출 될 때 어떤 일을 추가하고 싶을 때 각각의 ProxyFactoryBean을 만드는 대신 BeanNameAutoProxyCreator를 사용하여 advisor가 적용될 bean들을 등록해 두면 자동으로 해당 bean의 프록시를 만들어 사용하게 됩니다.

먼저 간단한 before Advice를 하나 만들고 bean으로 등록 합니다.
public class GetterAdvice implements MethodBeforeAdvice {

    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("getter joined " + target.getClass().getName() + " class " + method.getName());
    }
}

<bean id="getter" class="aop.oldStyle.advice.GetterAdvice" />

그리고 이제 BeanNameAutoProxyCreator를 bean으로 등록합니다.
    <!-- BeanNameAutoProxyCreator -->
    <bean id="getterAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" >
        <property name="beanNames">
            <list>
                <value>혜인</value>
                <value>기선</value>
                <value>KCcard</value>
                <value>movie</value>
            </list>
        </property>
        <property name="interceptorNames" value="getter" />
    </bean>

beanNames속성에는 advice가 적용될 bean들을 적어 줍니다. 여기에서도 역시 와일드 카드를 사용하여 하나의 문장으로 여러 bean을 나타낼 수 있습니다.

테스트를 실행하면 콘솔창에서 다음과 같은 메시지를 확인할 수 있습니다.
getter joined aop.oldStyle.domain.Customer class getAge
getter joined aop.oldStyle.domain.Movie class getName
getter joined aop.oldStyle.domain.Customer class getCard
getter joined aop.oldStyle.domain.MemberShipCard class getPoint


신고
top

AOP : 2007.04.01 11:06 Trackback. : Comment.

Autoproxy

AOP : 2007.04.01 10:07


이전에 잠시 ProxyFactoryBean을 사용하여 일일히 Proxy 객체를 만드는 것이 얼마나 피곤할 수 있는지 언급을 했었습니다.

aspect가 적용 될 부분이 객체 단위로 나눠 진다면 저렇게 하는게 편할 수도 있겠지만 그냥 타겟 객체를 멤버로 가지고 타겟 객체의 인터페이스를 구현하면서 타겟 객체의 메소드를 활욜하여 원하는 대로 구현하면 되니까요. 굳이 복잡하게 프록시 객체니 AOP니 사용할 필요가 없겠죠.

하지만 여러 객체을 관통하여 그 객체의 곳곳에 침투 해야 하는 Advice라면 어떻게 해야 할까요? 그 여러 객체에 해당하는 모든 프록시 객체를 만들어 줘야 하겠죠. Spring AOP는 프록시 객체를 사용하기 때문에 그렇게 될 것입니다.

이럴 때 Spring IoC container에 AbstractAutoProxyCreator 를 등록해 두면 컨테이너가 알아서 프록시들을 자동으로 생성해 주게 됩니다. 이 기능을 사용하면 굉장히 편하겠죠.

다음은 AbstractAutoProxyCreator를 상속한 클래스들 입니다.

BeanNameAutoProxyCreator, AbstractAdvisorAutoProxyCreator, AspectJAwareAdvisorAutoProxyCreator, DefaultAdvisorAutoProxyCreator, AnnotationAwareAspectJAutoProxyCreator

BeanNameAutoProxyCreator 사용 예

인터페이스를 구현하지 않은 객체들의 프록시도 필요할 수 있기 때문에 CGLIB jar파일을 필요로 합니다.

BeanPostProcessor를 사용하여 프록시 객체의 대상이 될 객체들을 찾아내서 그 객체들이 호출되기 전에 인터셉터들(어드바이스)을 끼워넣습니다.

신고

'AOP' 카테고리의 다른 글

AOP를 설명하는 그림 두 장  (2) 2008.10.04
4. @AspectJ 사용하는 초간단 AOP 예제  (13) 2008.01.08
AOP Design 이슈  (0) 2007.09.07
AspectJ In Action 3장  (0) 2007.08.22
BeanNameAutoProxyCreator 사용 예  (0) 2007.04.01
Autoproxy  (0) 2007.04.01
Spring AOP(old) ProxyFactoryBean 불편한 점  (0) 2007.03.30
기선 씨네마 :: Pointcut  (0) 2007.03.30
기선 씨네마 :: Advice  (0) 2007.03.30
기선 씨네마 :: 티켓 만들어 주기  (0) 2007.03.30
Spring AOP 공부를 위한 예제 - 기선 씨네마  (0) 2007.03.30
top


기선 씨네마 :: Advice

AOP : 2007.03.30 13:01


만드는 방법은 이전에도 살짝 살펴봤지만 소스는 아래와 같습니다.
public class WelcomeAdvice implements MethodBeforeAdvice {

    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("어떤 영화를 보시겠습니까?");
    }

}

public class ThankYouAdvice implements AfterReturningAdvice {

    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("감사합니다.");
    }

}

Advice만 만든 상태에서 이걸 ProxyFactoryBean에 주입시킵니다.
    <!-- Advice -->
    <bean id="welcome" class="aop.oldStyle.advice.WelcomeAdvice" />
    <bean id="thanks" class="aop.oldStyle.advice.ThankYouAdvice" />

    <!-- AOP cinema -->
    <bean id="AOP씨네마" class="org.springframework.aop.framework.ProxyFactoryBean" >
        <property name="proxyInterfaces" value="aop.oldStyle.domain.Cinema" />
        <property name="interceptorNames">
            <list>
                <value>welcome</value>
                <value>thanks</value>
            </list>
        </property>
        <property name="target" ref="기선씨네마" />
    </bean>

이제 테스트를 해봅니다.
    @Test
    public void aopKeesunCinema() {
        hein.buyTicket(aopCinema, movie);
    }

당연히 원한대로의 결과가 나옵니다.
어떤 영화를 보시겠습니까?
감사합니다.

그럼 ProxyFactoryBean에 지정한 인터페이스의 다른 메소드들을 호출하면 어떻게 될까요?
    @Test
    public void onlyAdviceTest() {
        aopCinema.checkTicket(hein, movie);
        aopCinema.giveMemberShipCard(hein);
        aopCinema.playMovie(movie);
        aopCinema.sellTicket(movie, new Date());
    }

전혀.. 원하던 결과가 아니게 됩니다.
어떤 영화를 보시겠습니까?
감사합니다.
어떤 영화를 보시겠습니까?
감사합니다.
어떤 영화를 보시겠습니까?
공공의적상영합니다.
감사합니다.
어떤 영화를 보시겠습니까?
감사합니다.

ProxyFactoryBean에는 Advice도 등록할 수 있고 포인트컷은 해당 인터페이스에 지정한 모든 메소드가 라는 걸 확인 할 수 있습니다.

이래서 포인트컷으로 원하는 곳에만 Advice가 적용될 수 있도록 지정한뒤, 그것을 Advice와 뭉쳐서 Advisor로 등록하고 그것을 Advice를 등록한 위치(interceptorNames)에 넣어 두면 원하는 곳에만 원하는 일을 추가할 수 있게 되겠습니다.
신고

'AOP' 카테고리의 다른 글

AspectJ In Action 3장  (0) 2007.08.22
BeanNameAutoProxyCreator 사용 예  (0) 2007.04.01
Autoproxy  (0) 2007.04.01
Spring AOP(old) ProxyFactoryBean 불편한 점  (0) 2007.03.30
기선 씨네마 :: Pointcut  (0) 2007.03.30
기선 씨네마 :: Advice  (0) 2007.03.30
기선 씨네마 :: 티켓 만들어 주기  (0) 2007.03.30
Spring AOP 공부를 위한 예제 - 기선 씨네마  (0) 2007.03.30
Spring AOP(old) Pointcut Implementation  (0) 2007.03.28
Spring AOP(old) Advisor  (0) 2007.03.28
Spring AOP(old) Pointcut  (0) 2007.03.28
top

AOP : 2007.03.30 13:01 Trackback. : Comment.

Spring AOP(old) Pointcut Implementation

AOP : 2007.03.28 11:24


이전에 살펴 봤듯이 포인트컷에는 두 가지 종류가 있습니다.

정적 포인트컷
- 직접 만들고 싶다면 StaticMethodMatcherPointcut 클래스를 상속하여 isMatch 메소드를 구현.
- NameMatchMethodPointcut :: NameMatchMethodPointcutAdvisor가 이에 상응하는 Advisor입니다. 클래스 이름에서 알 수 있듯이 메소드 이름으로 해당 포인트컷을 지정합니다.

사용자 삽입 이미지

위 두개의 setter를 사용하여 값을 configuration metadata 상에서 injection하면 됩니다. 위에껀 매칭되는 메소드 이름 설정이 하나일때 사용하고 아래는 여러개 일때 사용합니다.

매칭되는 메소드 이름 설정은 메소드 이름 앞이나 뒤에 *을 사용할 수 있습니다. 예) order* 은 oder로 시작하는 모든 메소드.

Sta*Meth* 이런식으로 *을 메소드 이름 중간에 넣으면 못 찾나? - 이건 나중에 코딩해보면서 확인해봐야겠습니다.

- Perl5RegexpMethodPointcut :: RegexpMethodPointcutAdvisor가 정규식을 사용한 포인트 컷을 받아 줄 수 있으며 JDK1.4 이상 부터는 기본으로 JdkRegexpMethodPointcut을 사용하는데 명시적으로 Perl5RegexpMethodPointcut을 사용할 수 도 있습니다.

NameMatchMethodPointcut과 다른 점은 무엇인가? - 정규 표현식을 사용한다는 것이고 메소드 이름으로만 포인트컷을 나타내는 것이 아니라 전체 패키지 경로와 클래스 이름까지 포함하여 나타내야 합니다.

Perl5RegexpMethodPointcut 과 JdkRegexpMethodPointcut 의 차이는 뭔가? - 둘 다 AbstractRegexpMethodPointcut 클래스를 상속한 자식 클래스들이며 정규 표현식으로 매칭되는 포인트컷을 정의할 때 사용합니다. Perl5RegexpMethodPointcut는 Perl5의 정규 표현식을 사용하며 Jakarta ORO regular expression library가 필요합니다. 측 추가적인 jar파일이 필요한거죠. 하지만 JdkRegexpMethodPointcut 는 JDK 1.4 정규표현식을 사용하며 추가적인 jar파일은 필요 없고 jdk버전이 1.4이상일 때 사용가능 합니다.

정규 표현식은 어떻게 쓰는거지? - SIA에 잘 나와있군요. 쩜(.), 별(*), 더하기(+), 역슬래쉬(\) 네가지를 사용합니다.
쩜(.)은 어떤 문자든 딱 한 글자를 나타냅니다.
별(*)은 * 전에 있는 문자가 0 ~ 여러개 있을 수 있슴을 나타냅니다.
더하기(+)는 + 전에 있는 문자가 1 ~ 여러개 있을 수 있슴을 나타냅니다.
역슬래쉬(\)는 \ 다음에 오는 문자가 있어야 한다는 것을 나타냅니다.
.*set.* :: set이라는 글자가 들어있는 모든 메소드
.*get.+By.+ :: get뭐시기By모시기 라는 형태의 글자가 들어있는 모든 메소드
거의 암호 수준이군요. 쩜별(.*) 은 쩜이 0~여러개 있을 수 있다는 거니까 그말은 다시 어떤 문자든지 여러개 있을 수 있다는 거니까 저렇게 되는거죠.

동적 포인트컷
- MethodMatcher를 구현하고 isRuntime() 메소드가 true를 리턴하게 합니다. 실행 중에 포인트컷에 해당하는 모든 메소드를 만날때 마다 matches(Method, Class, Object[]) 가 호출되기 때문에 상당한 성능 저하[각주:1]가 발생합니다.

- ControlFlowPointcut :: 이 포인트컷을 위한 Advisor가 따로 존재하진 않고 이전에 찾아냈던 DefaultPointcutAdivisor로 Pointcut 빈과 Advice 빈을 묶어줍니다. AspectJ의 cflow 포인트 컷에 해당하는 일을 합니다. 현재 동작중인 쓰레드 스택에서 특정한 클래스나 메소드가 찾아 졌을 경우에만 true를 리턴하도록 설정이 가능합니다.
사용자 삽입 이미지
이 생성자들을 사용하여 constuctor injection으로 쓰레드의 스택에서 어떤 타입의 어떤 메소드 이름의 메소드가 호출될 지점을 포인트컷으로 지정할 수 있습니다.
  1. API를 보니 "Note that evaluating such pointcuts is 10-15 times slower than evaluating normal pointcuts, but they are useful in some cases." 이렇게 나와있네요. [본문으로]
신고
top

AOP : 2007.03.28 11:24 Trackback. : Comment.

Spring AOP(old) Advisor

AOP : 2007.03.28 09:53


앞에서 살펴본 Advice와 Pointcut을 묶어서 하나의 Advisor로 만들 수 있습니다. 그 Advisor 하나를 보면 어디(Pointcut)에 뭐가(Advice) 적용될지 한눈에 보이게 됩니다.

이렇게 만든 Advisor 빈을 프록시 객체를 만들기 위한 ProxyFactoryBean에서 interceptorNames속성에 적어 주고, 타켓 빈을 target 속성에 적어 주고, 타켓 빈이 구현한 인터페이스를 proxyInterfaces에 적어 주면 프록시 bean을 사용할 수 있게 됩니다.

인터페이스가 없을 땐 CGLib을 사용한다고 했는데 어떻게 쓰는거지? 어딘가 예제가 나오겠지? 일단 지금은 그냥 인터페이스를 사용해서 프록시 객체 만드는 방법만...

사용자 삽입 이미지
Pointcut과 Advice를 설정할 수 있는 가장 일반적인 Advisor 클래스는 무엇인가? - 찾아봐야겠네요.
AbstractBeanFactoryPointcutAdvisor, AbstractGenericPointcutAdvisor, AbstractPointcutAdvisor, AspectJExpressionPointcutAdvisor, AspectJPointcutAdvisor, DefaultBeanFactoryPointcutAdvisor, DefaultPointcutAdvisor, DynamicMethodMatcherPointcutAdvisor, NameMatchMethodPointcutAdvisor, PersistenceExceptionTranslationAdvisor, ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor, RegexpMethodPointcutAdvisor, StaticMethodMatcherPointcutAdvisor, TransactionAttributeSourceAdvisor
PointcutAdvisor 인터페이스를 구현한 클래스들 입니다. 이중에서 DefaultPointcutAdvisor가 setAdvice()와 setPointcut()을 가지고 있으니까 이녀석을 사용해서 따로 존재하는 pointcut과 advice를 합치면 될 것 같습니다.

보통은 PointcutAdvisor의 이름들을 보시면 Pointcut 이름 마다 거기에 해당하는 Advisor가 존재하는 것 같습니다. 그 말은 포인트컷 따로 어드바이스 따로 만들어서 DefaultPointcutAdvisor로 합치지 말고 어드바이스는 따로 만들고 포인트 컷은 Advisor의 속성에 직접 주입하는 방식으로 만들때 사용하라는 것 같습니다. 포인트 컷을 재사용할 것이 아니라면 그게 편하겠네요. 아마도 어드바이스는 재사용하기가 유리하지만 포인트컷은 해당 어플리케이션에 종속되기 쉬우니까 재사용이 어렵기 때문이겠죠.


신고
top

AOP : 2007.03.28 09:53 Trackback. : Comment.

Spring AOP(old) Pointcut

AOP : 2007.03.28 08:42


참조 : Reference 7.2, Spring In Action

Concept 
advice가 어떤 타입이든 상관없이 같은 포인트컷에 여러 adivce를 적용할 수 있습니다.
public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
}

핵심이 되는 포인트컷 인터페이스로 클래스와 메소드로 포인트컷을 판단하게 되기 때문에 저렇게 나뉘어져 있습니다. 클래스 필터와 메소드 필터의 재사용성을 고려한거라고 볼 수 있습니다.
public interface ClassFilter {
    boolean matches(Class clazz);
}

인자로 받은 클래스 타입에 따라 true, false를 리턴하면 되겠죠. 이 것 보다는 훨씬 복잡하고 중요한 것이 메소드 필터 입니다.
public interface MethodMatcher {
    boolean matches(Method m, Class targetClass);
    boolean isRuntime();
    boolean matches(Method m, Class targetClass, Object[] args);
}

왜 클래스 필터가 있는데 클래스를 인자로 받을까요? - 모르겠네..흠...

위에서 부터 차례대로 프록시 객체를 생성할 때 호출이 되며 먼저 matches(Method, Class)를 호출해서 ture면 isRuntime()이 호출 되는 때 이 때 정적 포인트컷이면 false를 리턴하고 동적 포인트컷이면 true를 리턴합니다. isRuntime이 false면 세번째 메소드는 호출하지 않고 true면 매번 advice가 적용될 때 마다 호출 하여 확인하게 됩니다.
정적 포인트컷 : matches(Method, Class)로 딱 한번 실행 됩니다.
동적 포인트컷 : matches(Method, Class, Object[])는 매번 adivce가 적용 될 때 마다 실행 되서 체크하게 됩니다.

동적 포인트컷을 확인할 때 인자를 받는 이유는? - 메소드 이름과 클래스 이름이나 타입만 가지고는 확인하는데 필요한 정보가 부족한가. 왜 그걸론 부족하지? - 메소드와 클래스 정보로 부족하다면 정적 포인트컷은 왜 인자를 받아 오지 않는거지..

포인트컷 연산
- union :: 합집합
- intersaction :: 교집합
Pointcuts 클래스에 있는 메소드를 사용합니다. 하지만 2.0에 추가된 AspectJ의 표현식을 사용하면 더 간단하죠. || 나 && 이런 걸로 해결이 가능하니까요.
신고
top

AOP : 2007.03.28 08:42 Trackback. : Comment.

Spring AOP(old) Advice

AOP : 2007.03.28 01:44


참조 : SIA 3장

Spring이 제공하는 Advice 종류와 특징
1. arond advice :: MethodInterceptor :: 대상 메소드에 대한 호출을 가로챔
2. before advice :: BeforeAdvice :: 대상 메소드가 실행되기 전에 호출됨
3. after advice :: AfterReturningAdvice :: 대상 메소드가 리턴한 후에 호출됨
4. throws advice :: ThrowsAdvice :: 대상 메소드가 예외를 던질 때 호출됨

2.0 부터는 after finally 였나.. 대상 메소드가 끝났거나 예외가 발생했거나 종료하면 무조건 실행하는 Advice가 추가 됐습니다.

1. Before Advice 만들기
MethodBeforeAdvice 인터페이스를 구현합니다. 구현해야 할 메소드는 한 개 입니다.

void before(Method method,
            Object[] args,
            Object target)
            throws Throwable

    Callback before a given method is invoked.

    Parameters:
        method - method being invoked
        args - arguments to the method
        target - target of the method invocation. May be null.
    Throws:
        Throwable - if this object wishes to abort the call. Any exception thrown will be returned to the caller if it's allowed by the method signature. Otherwise the exception will be wrapped as a runtime exception.

첫번째 인자는 대상이 되는 메소드
두번째는 그 메소드에 전달하는 인자들
세번째는 대상 메소드를 가지고 있는 객체

2. After Advice 만들기
AfterReturningAdvice 인터페이스를 구현합니다.

void afterReturning(Object returnValue,
                    Method method,
                    Object[] args,
                    Object target)
                    throws Throwable

    Callback after a given method successfully returned.

    Parameters:
        returnValue - the value returned by the method, if any
        method - method being invoked
        args - arguments to the method
        target - target of the method invocation. May be null.
    Throws:
        Throwable - if this object wishes to abort the call. Any exception thrown will be returned to the caller if it's allowed by the method signature. Otherwise the exception will be wrapped as a runtime exception.

첫번째 인자에 대상 메소드의 리턴값이 추가 됐고 나머진 한칸씩 밀렸습니다.
메소드가 끝난 뒤 실행하는 Advice니까 가용한 정보가 하나 늘었습니다.

3. Throws Advice 만들기
ThrowsAdvice 인터페이스를 구현합니다.

void afterThrowing( ThrowableSubclass)
void afterThrowing(Method , args, target, ThrowableSubclass)

위에 있는 두 가지 적어도 한 가지는 구현해야 합니다.
궁금한건... 이 인터페이스는 마커 인터페이스인데.. 어떻게 둘 중 하나만 구현하거나 둘 다 구현해도 되게 만든건지 신기합니다.

4. Around Advice 만들기
MethodInterceptor 인터페이스를 구현합니다.

가장 자주 사용될 것 같은 Advice로 Spring의 인터페이스가 아닌 AOP alliance의 인터페이스 입니다. 따라서 다른 AOP alliance 진영에 속한 AOP 구현체에서도 이 것을 구현한 Advice를 재사용할 수 있겠습니다.

public java.lang.Object invoke(MethodInvocation invocation)
                        throws java.lang.Throwable

    Implement this method to perform extra treatments before and after the invocation. Polite implementations would certainly like to invoke Joinpoint.proceed().

    Parameters:
        invocation - the method invocation joinpoint
    Returns:
        the result of the call to Joinpoint.proceed(), might be intercepted by the interceptor.
    Throws:
        java.lang.Throwable - if the interceptors or the target-object throws an exception.

유일한 인자인 MethodInvocation에는 getMethod()가 있어서 Method 타입의 객체를 넘겨 받을 수 있습니다.

리턴 타입이 있는 이유? 이 Advice는 대상 메소드의 리턴값을 바꿀 수 있습니다. 대상 메소드의 수행 여부 또한 결정할 수 있는 매우 강력한 기능을 가진 Advice입니다.

대상 메소드 실행 여부는 어떻게 결정하는가? invocation.proceed(); 를 넣어 주면 실행이 되고 해당 메소드가 값을 리턴 한다면 리턴 값을 받을 수도 있겠죠.

5. Introduction Adice

공부해야 할 것
신고
top

AOP : 2007.03.28 01:44 Trackback. : Comment.

Spring 2.0 AOP

Spring/Chapter 6 : 2007.03.27 18:33


참조 : 3월달 마소 AOP 특집 중 토비님의 Spring 2.0 AOP

AOP를 구현 하는 방법.

1. AOP 언어 자체를 확장하는 방법 - AspectJ - 전용 컴파일러 필요함.
2. 그냥 자바 클래스에 설정파일이나 어노테이션을 사용하는 방법
    2-1. 컴파일 된 클래스를 변환하는 방법
    2-2. 클래스 로딩 시 바이트 코드를 조작하는 방법
    2-3. 순수한 자바 언어와 API만을 사용하는 방법 - Spring AOP

Spring AOP는 프록시 기반의 AOP 구현 방법을 사용.
1. 인터페이스에 대한 프록시 만들 때 - JDK의 Dynamic Proxy사용
2. 클래스에 대한 프록시 만들 때 - CGLib 사용

기존 Spring AOP.
1. 포인트컷(Pointcut)
    조인 포인트-Spring AOP는 메소드 실행 시점만 지원-의 묶음
    Spring 1.X 버전에서는 스태틱 포인트컷과 다이내믹 포인트컷 방식이 있다.

2. 어드바이스(Advice)
    Interception arount 어드바이스 : 메소드 실행 전 후 모두, 대상이 되는 메소드의 실행 여부 결정 가능 (ex, MethodInterceptor)
    Before 어드바이스 : 메소드 실행 전에
    Throws 어드바이스 : 메소드에서 예외 발생 했을 때
    After Return 어드바이스 : 메소드 실행 후에
    Introduction 어드바이스 : 기본의 클래스에 동적으로 필드나 메소드 추가

3. 어드바이저(Advisor)
    Advice + Pointcut = Aspect = 어드바이져(ex. DefaultPointcutAdvisor)

4. 프록시
    ProxyFactoryBean : target, proxyIntergace, interceptorNames 속성을 주입하여 사용.
    AutoProxyCreator(ex. BeanNameAutoProxyCreator) : BeanPostProcessor를 사용하여 임의의 빈에 다이내믹하게 프록시 설정 부여함.

5. 한계와 단점
    5-1. Pointcut 인터페이스 구현해야함. 포인트컷 표현, 적용이 어렵다.
    5-2. XML 설정이 복잡해 진다.
    5-3. IoC 컨테이너에 빈으로 등록되지 않은 객체는 적용할 수 없다.
    5-4. 약간의 성능저하
    5-5. 타깃 객체와 프록시가 분리되어 있다,

Spring 2.0 AOP.
1. AspectJ의 애스펙트 선언과 포인트컷 언어 도입
    한계와 단점에서 첫 번째와 두 번째 문제 해결

2. AspectJ AOP 이용방법
    AspectJ를 사용하여 세번째 문제 해결 가능.(@Configurable 사용)
    2-1. Spring 설정과 무관하게 사용.
    2-2. 에스팩트 정의를 빈으로 등록.(factory-method="aspectOf" 사용)
신고

'Spring > Chapter 6' 카테고리의 다른 글

6.2. @AspectJ support  (0) 2007.04.03
@Around 어드바이스 예제  (0) 2007.04.03
@AfterReturning 어드바이스 만들기  (0) 2007.04.03
@Pointcut Designator  (0) 2007.04.02
@Aspect 붙은 클래스끼리는 상속이 안 됩니다.  (0) 2007.04.02
초간단 @Pointcut 과 @Advice 예제  (0) 2007.04.02
Enabling @AspectJ Support  (0) 2007.04.02
6.3. Schema-based AOP support  (0) 2007.04.01
6.1. Introduction  (0) 2007.04.01
Spring 2.0 AOP  (2) 2007.03.27
Aspect Oriented Programming with Spring  (0) 2007.03.26
top


7. @AspectJ 사용하는 초간단 AOP 예제 2

AOP : 2007.01.13 22:27


@AspectJ 를 사용하는 초간단 AOP 예제에서 Pointcut을 선언하고 Advice들에서 해당 Pointcut을 불러가며 사용했었습니다.

    @Pointcut("execution(public * ex1.Human.sayName(..))")
    public void greeting() {
    }

    @AfterReturning("greeting()")
    public void doBeforeOne() {
        System.out.println("AOP 죽~ 여~ 줍니다~");
    }

이렇게 했었는데요. AspectJ에서는 Anonymous Pointcut이라고 하는데 Spring AOP(중에서도@AspectJ)에서도 사용할 수 있습니다. 다음과 같이 Pointcut 이름을 적을 필요 없이 바로 적용될 부분은 Advice에 명시해 주면 됩니다.

    @AfterReturning("execution(public * Human.sayName(..))")
    public void doBeforeOne() {
        System.out.println("AOP 죽~ 여~ 줍니다~");
    }

두 개의 차이점은 위에 기존에 사용해 봤던 방식은 당연히 재사용이 편하다는 것. 그리고 아래의 것은 재사용하기 불편하다는 차이밖에 없는 것 같습니다. :)

Spring AOP를 JDK 6.0 에서 돌리지 마세요 ㅠ.ㅠ Pointcut이 있는데도 없다고 나옵니다. 이것 때문에 정말 황당해 하며 1시간 가량을...'저번에는 분명히 됐는데;; 왜이러지..' 하면서 괴로워 했습니다.
참조 : http://forum.springframework.org/showthread.php?p=92737

신고
top







티스토리 툴바