지금까지 사용중인 객체가 proxy객체 인지 아닌지 구별하기 위해
instanceof 연산자로 SpringProxy 타입을 비교해서 구분 했는데
이미 그렇게 구분해서 값을 돌려주는게 있었네요...

org.springframework.aop,.support.AopUtils

'Spring > AOP' 카테고리의 다른 글

Proxy 객체인지 확인하려면...?  (0) 2009.01.21
Spring AOP - aop스키마 기반  (0) 2008.01.14
Spring AOP - @AspectJ  (0) 2007.12.31
Spring AOP APIs - ProxyFactoryBean, AutoProxy  (0) 2007.12.31
Spring AOP APIs - Advisor  (2) 2007.12.31
Spring AOP APIs - Pointcut  (0) 2007.12.31
Posted by 째코

댓글을 달아 주세요

BeanFactoryPostProcessor 구현체를 하나 만들었습니다.
메소드 구현 내용은 이렇습니다.

@Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
   throws BeansException {
beanFactory.createBean(ActionDefinition.class);
}

ActionDefinition 클래스는 bean 설정파일에 정의되지 않은 임의의 클래스 입니다.
또한 컴포넌트 스캔 대상도 아닙니다. 단, @Autowired 는 사용하고 있습니다.

여기서 발생하는 문제는 @Autowired 가 작동되지 않는 것 입니다.
이유는 간단합니다.

bean 생성 시점이 BeanFactoryPostProcessor 이기 때문입니다.
BeanFactoryPostProcessor 가 수행되는 시점은 @Autowired 를 처리하기 위한 AutowiredAnnotationBeanPostProcessor 가 등록되기 전입니다.
Posted by 째코

댓글을 달아 주세요

  1. 기선 2008.11.02 22:29 신고  댓글주소  수정/삭제  댓글쓰기

    우와.. 그렇군요. 흠. 근데 어떤 이유로 인해 빈을 저런 방법으로 등록하는지 궁금합니다. 어떤 경우인거죠?

    • 째코 2008.11.03 00:15 신고  댓글주소  수정/삭제

      쿼리와 뷰만으로 CURD를 가능하게 하기 위한 무언가를 만들고 있습니다. 설정방식을 최소화 시킬 생각으로 자바클래스와 애노테이션을 이용하고 있는데 바로 이 설정이 되는 자바클래스를 스프링 초기화 타임에 리플렉션으로 애노테이션을 읽어서 내부적으로 사용될 설정객체를 생성하고 있습니다. ConfigurableListableBeanFactory 인터페이스에 아주 막강한 기능이 많더군요...

개발 환경이 Java5 미만이거나 애노테이션 보다 XML을 더 선호할 경우 aop 네임스페이스를 이용해
선언적으로 AOP를 구현 할 수 있으며 모든 aop 선언적 요소들은 <aop:config /> 요소 안에서 사용 됩니다.
<aop:config /> 요소는 auto-proxy 기능이 있는데 2.0이전에 사용하던 auto-proxy와 함께 사용할 경우
여러가지 문제가 발생 하므로 둘중 한가지 방식만 사용해야 합니다.

Aspect 선언
<aop:aspect /> 요소를 이용해 선언 합니다. 아래는 POJO로 작성된 Aspect 클래스 입니다.
public class ExampleAspect {
    public void beforeAdvice(JoinPoint joinPoint, String msg) {
       ...
    }
   
    public void afterReturingAdvice(Object retVal) {
       ...
    }
}

위에서 작성된 클래스 bean으로 등록하고 <aop:aspect /> 요소에서 참조 하고 있습니다.
여러개의 aspect가 존재할 경우를 대비해 적용순서를 설정할 수 있는 order 속성도 존재 합니다.
<aop:config proxy-target-class="true>
    <aop:aspect id="aspect" ref="exampleAspect" order="1"/>
</aop:config>

<bean id="exampleAspect" class="jjaeko.ExampleAspect" />
<aop:config /> 요소는 auto-proxy 기능이 있기 때문에 CGLIB 사용 여부를 설정 할 수 있습니다.

Pointcut 선언
<aop:pointcut /> 요소를 이용해 선언 합니다.
첫번째 설정은 <aop:config />내 어떤 aspect에서도 참조 가능하고,
두번째 설정은 <aop:aspect />내에서만 참조 가능 합니다.
<aop:config>
    <aop:pointcut id="setPointcut" expression="execution(* set*(..))"/>
</aop:config>

<aop:config proxy-target-class="true>
    <aop:aspect id="aspect" ref="exampleAspect" order="1">
       <aop:pointcut id="setPointcut" expression="execution(* set*(..))"/>
    </aop:aspect>
</aop:config>

Java5 이상에서 외부 클래스에 @Pointcut 애노테이션을 이용해 Pointcut을 정의했다면
다음과 같은 방법으로 참조 할 수 있습니다.
<aop:config>
    <aop:pointcut id="setPointcut" expression="x.y.z.ExamplePointcut.setPointcut()"/>
</aop:config>

Advice 선언
모든 Advice 선언은 <aop:aspect /> 요소 하위에서 선언됩니다.

Before Advice
<aop:before /> 요소를 이용해 선언 합니다.
method 속성은 참조되는 bean의 Before Advice로 사용될 메소드를 설정 합니다.
pointcut 속성을 이용해 직접 Pointcut을 작성할 수 있으며 이미 정의된 Pointcut을 사용할땐
pointcut-ref 속성을 이용 합니다.
<aop:config proxy-target-class="true>
    <aop:aspect ref="exampleAspect">
        <aop:before method="advice" pointcut="execution(* set*(..))  and args(msg)"/>
    </aop:aspect>
</aop:config>
애노테이션기반 에서 처럼 args 지시자를 이용해 Target 객체의 호출되는 메소드로 전달되는 인자를
받을 수 있습니다. 이때 advice 메소드는 해당 매개변수가 선언되어 있어야 합니다.
그리고 XML에서는 '&&' 문자를 사용할 수 없기 때문에 and 키워드를 사용했습니다.

After-Returning Advice
<aop:after-returning /> 요소를 이용해 선언 합니다.
returning 속성을 이용해 Target 객체의 메소드가 리턴하는 값을 받을 수 있습니다. 이때 afterReturningAdvicea 메소드는 리턴값을 받을 수 있는 매개변수가 선언되 있어야 합니다.
<aop:config proxy-target-class="true">
    <aop:pointcut id="setPointcut" expression="execution(* set*(..))"/>

    <aop:aspect ref="exampleAspect">
        <aop:after-returning method="afterReturningAdvice" pointcut-ref="setPointcut"          
                    returning="retVal"/>
    </aop:aspect>
</aop:config>

After-Throwing Advice
<aop:after-throwing /> 요소를 이용해 선언 합니다.
throwing 속성을 이용해 특정예외가 발생 했을때만 호출되도록 설정할 수 있습니다.
다음 설정은 throwingAdvice 메소드에 매개변수로 선언된 ex 타입의 예외가 발생했을 때만 호출됩니다.
<aop:config proxy-target-class="true">
    <aop:pointcut id="setPointcut" expression="execution(* set*(..))"/>
       
    <aop:aspect ref="exampleAspect">
        <aop:after-throwing method="throwingAdvice" pointcut-ref="setPointcut"        
                    throwing="ex"/>
    </aop:aspect>
</aop:config>

After-Finally Advice
<aop:after /> 요소를 이용해 선언 합니다. 이건 특별한 속성이 없네요
<aop:config proxy-target-class="true">
    <aop:pointcut id="setPointcut" expression="execution(* set*(..))"/>
       
    <aop:aspect ref="exampleAspect">
        <aop:after method="after" pointcut-ref="setPointcut"/>
    </aop:aspect>
</aop:config>

Around Advice
<aop:around /> 요소를 이용해 선언 합니다. 애노테이션 기반과 마찬가지로 메소드의 첫번째 매개변수로
반드시 ProceedingJoinPoint 타입이 선언되어 있어야 합니다.
<aop:config proxy-target-class="true">
    <aop:pointcut id="setPointcut" expression="execution(* set*(..))"/>
       
    <aop:aspect ref="exampleAspect">
        <aop:around method="aroundAdvice" pointcut-ref="setPointcut"/>
    </aop:aspect>
</aop:config>

Introduction Advice
<aop:declare-parents /> 요소를 이용해 선언 합니다.
types-matching 속성은 AspectJ 표현식을 이용해 타입패턴을 설정 합니다.
implements-interface 속성은 추가된 기능이 정의된 인터페이스를 설정 합니다.
default-imple 속성은 추가된 기능이 정의된 인터페이스를 구현한 클래스를 설정 합니다.

Spring AOP - @Aspectj 에서 사용했던 잠금기능 예제를 스키마 기반으로 바꿔보겠습니다.
<aop:config proxy-target-class="true">
    <aop:pointcut id="setPointcut" expression="execution(* set*(..))"/>
       
    <aop:aspect ref="exampleAspect">
        <aop:declare-parents types-matching="aop2.Foo"
                                 implement-interface="aop.Lockable"
                                 default-impl="aop.LockImpl"/>
        <aop:around method="lockCheckAdvice" pointcut-ref="setPointcut"/>                     
    </aop:aspect>
</aop:config>

Advisor
<aop:advisor /> 요소를 이용해 선언합니다. 선언 위치는 <aop:aspect /> 요소보다 먼저 있어야 합니다.
용도는 외부 Advice를 통한 Advisor 생성입니다.
다른 aop 선언적 요소들이 있는데 궂이 사용하는 이유는 외부 Advice가 바로 트랜잭션 Advice이기 때문입니다.
<aop:config>
    <aop:pointcut id="servicePointcut" expression="within(x.y.z.service..*)"/>

    <aop:advisor pointcut-ref="servicePointcut" advice-ref="tx-advice"/>
</aop:config>

<tx:advice id="tx-advice">
  <tx:attributes>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>
advice-ref 속성에 반드시 트랜잭션 Advice만 설정할 수 있는건 아니지만 그렇다고 Spring AOP APIs Advice를
설정할 일은 없겠습니다. 주 목적자체가 트랜잭션 이라는걸 짚고 넘어가야 합니다.

'Spring > AOP' 카테고리의 다른 글

Proxy 객체인지 확인하려면...?  (0) 2009.01.21
Spring AOP - aop스키마 기반  (0) 2008.01.14
Spring AOP - @AspectJ  (0) 2007.12.31
Spring AOP APIs - ProxyFactoryBean, AutoProxy  (0) 2007.12.31
Spring AOP APIs - Advisor  (2) 2007.12.31
Spring AOP APIs - Pointcut  (0) 2007.12.31
Posted by 째코

댓글을 달아 주세요

먼저 두개의 bean을 보겠습니다.

masterName 프로퍼티의 경우 아빠와 아들의 경우 설정이 중복 됩니다.
이때 parent속성으로 부모 bean을 지정하게 되면 설정 내용들을 상속 받게 됩니다.
(단, 동일한 설정에 대해서는 오버라이드 되지 않습니다.)

여기서 포인트가 있는데 자식 bean의 class가 명시되어 있다면 부모 bean의 설정 내용들만 상속 받게 됩니다.
이 말은 부모 bean이 다른 클래스여도 상관 없고 class가 지정안된 bean일지라도 상관 없다는 뜻입니다.
아래 예제가 바로 그런 경우 입니다.

master는 class가 지정되지 않았기 때문에 인스턴스화 될 수 없으므로 abstract="true"을 사용해 인스턴스 생성을 방지 하고 있습니다.
abstract가 설정된 bean은 스스로 인스턴스화 될 수 없고 오직 자식 bean에서만 사용가능하게 됩니다.

만약 자식 bean의 class가 명시되어 있지 않다면 부모 bean의 class를 상속 받게 됩니다.
따라서 부모 bean은 완전한 형태여야만 합니다.

Posted by 째코

댓글을 달아 주세요

Spring AOP - @AspectJ

Spring/AOP 2007. 12. 31. 12:30
Spring 2.0에서는 AspectJ에서 제공되는 애노테이션을 이용해 POJO로 Aspect를 작성 할 수 있습니다.
AspectJ를 전략적으로 이용하기 때문에 Spring2.0 에서의 AOP도 여전히 Proxy방식으로 작동 됩니다.

Aspect
@Aspect 애노테이션은 클래스 레벨에 위치하며 해당 클래스가 Aspect임을 명시합니다.
@Aspect
public class ExampleAspect {
    ...
}
이 클래스는 pointcut, advice, introduction 을 가질 수 있습니다.

@Aspect 애노테이션이 존재하는 클래스를 찾아서 Aspect로 인식하기 위해 후처리기를 등록 합니다.
다음 두 가지 방법은 동일 합니다.
aop 스키마
<aop:aspectj-autoproxy proxy-target-class="true" />

DTD 기반
<bean
 class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator">
    <property name="proxyTargetClass" value="true" />
</bean>
두 경우 모두 CGLIB 사용 여부를 설정 할 수 있습니다.
하지만 aop스키마를 이용할 경우 세부설정들(exposeProxy, 등등)은 설정 할 수가 없더군요.
혹시 있다면 알려주세요.

Pointcut
@Pointcut 애노테이션을 이용해 pointcut을 정의 합니다.
Spring AOP는 메소드 Joinpoint만 지원하기 때문에 메소드레벨에서만 사용 가능 합니다.
아래 간단한 예제는 모든 set이라는 이름을 포함한 메소드 Joinpoint를 나타냅니다.
@Pointcut("execution(* set*(..))")
private void examplePointcut() {}
중요한 것은 메소드 자체는 @Pointcut 애노테이션을 식별하기 위한 id로 사용되기 때문에 몸체가 없습니다.
Pointcut을 나타내는 메소드는 반드시 리턴타입이 void여야 하며 접근제어자는 일반적인 메소드와
다를 것 없이 접근성을 나타냅니다. 자세한 Pointcut 표현식 문법은 Spring 래퍼런스 6장을 참고 하세요.

오직 메소드 Joinpoint만 지원 하는 이유로 AspectJ의 모든 Pointcut 지시자를 사용 할 수 없습니다.
만약 지원되지 않는 지시자를 사용할 경우 IllegalArgumentException이 던져 집니다.
Spring AOP에서 사용 가능한 Pointcut 지시자는 다음과 같습니다.
execution(메소드패턴) - 메소드패턴에 해당하는 Joinpoint를 나타냅니다.
within(타입패턴) - 타입패턴에 해당하는 클래스의 모든 Joinpoint를 나타냅니다.
this(타입) - 타입과 일치하는 객체의 모든 Joinpoint를 나타냅니다.
target(타입) - 타입과 일치하는 Target 객체의 모든 메소드 Joinpoint를 나타냅니다.
args(타입) - 타입과 메소드 인자의 타입이 일치하는 모든 Joinpoint를 나타냅니다.
@target(애노테이션) - 지정된 애노테이션을 가지고 있는 Target 객체의 모든 Joinpoint를 나타냅니다.
@args(애노테이션) - 지정된 애노테이션과 런타임시 메소드 인자로 전달된 객체의 애노테이션이 일치하는
                              Joinpoint를 나타냅니다.
@within(애노테이션) - 지정된 애노테이션을 가지고 있는 객체의 모든 Joinpoint를 나타냅니다.
Spring AOP는 런타임 Proxy방식이기 때문에 this와 target 지시자는 같은 객체를 참조 하며
@target과 @within 지시자 역시 같은 객체를 참조 합니다.

Pointcut 연산
Pointcut 표현은 '&&' , '||', '!' 를 이용하여 논리연산을 할 수 있고, id로써 연산도 가능 합니다.
아래 예제에서 examplePointcut3() 는 x.y.z.service 나 하위 패키지 모든 클래스의 set 이나 get으로
시작하는 모든 public 메소드를 나타냅니다.
@Pointcut("execution(public * set*(..)) || execution(public * get*(..))")
private void examplePointcut() {}
   
@Pointcut("x.y.z.service..*")
public void examplePointcut2() {}
   
@Pointcut("examplePointcut() && examplePointcut2()")
public void examplePointcut3() {}

Pointcut의 참조
외부 클래스에 정의된 Pointcut을 참조하기 위해 다음과 같이 합니다.
@Before("x.y.z.ExamplePointcut.beforePointcut()")
public void beforeAdvice() {
   ...
}
해당 Pointcut이 있는 클래스를 인스턴스화할 필요는 없습니다. 단지 리플렉션을 통해 메소드에 선언된
애노테이션만 얻어오면 되기 때문입니다.

Advice
Before Advice
@Before 애노테이션을 이용해 정의하며 인자로 Pointcut의 id를 사용하거나
직접 in-line형식으로 표현식을 기술할 수 있습니다. (in-line 방식은 다른 Advice에서도 사용 가능 합니다.)
@Before("examplePointcut()")
public void beforeAdvice() {
    System.out.println("호출 전 입니다.");
}

또한 메소드의 첫번째 인자로 JointPoint를 선언하면 Joinpoint에 대한 정보를 얻을 수 있습니다.
@Before("examplePointcut()")
public void beforeAdvice(JoinPoint joinPoint) {
    System.out.println(joinPoint.getTarget());
        for (Object args : joinPoint.getArgs()) {
            System.out.println(args);
        }
}

마지막 예제 입니다.
@Before("examplePointcut() && args(msg)")
public void beforeAdvice(String msg) {
    System.out.println(msg);
}
@Before 애노테이션의 Pointcut에 '&&' 연산자를 이용해 args 지시자를 추가 했습니다.
그런데 이전에 알던 것과 다르게 args 지시자의 인자가 타입이 아니고 매개변수명으로 되어 있습니다.
Target 객체의 호출되는 메소드로 전달되는 인자를 Advice에서 받고 싶다면 위 예제의 문법을 사용해야 합니다.
또한 JoinPoint와 같이 받는것도 가능하며 이 때 JoinPoint는 args 지시자의 인자와 관계 없이 첫번째 매개변수로 하면 됩니다.

After-Returning Advice
@AfterReturning 애노테이션을 이용해 정의하며 Target 객체의 메소드가 정상적으로 종료되었을 때
호출 됩니다.
@AfterReturning("examplePointcut()")
public void beforeAdvice() {
    System.out.println("호출 후");
}

Target 객체의 호출되는 메소드의 리턴값을 받을 수 있습니다.
또한 Before Advice 처럼 첫번째 매개변수를 JoinPoint로 정의할 수 있습니다.
@AfterReturning(pointcut="examplePointcut()", returning="retVal")
public void afterReturningAdvice(JoinPoint joinPoint, Object retVal) {
    ...
}
returning 값은 매개변수명과 동일 합니다.

After-Throwing Advice
@AfterThrowing 애노테이션을 이용해 정의하며 지정된 타입의 예외가 발생했을 때만 적용하려면
throwing 속성을 사용 합니다. throwing 속성은 Advice에 정의된 예외타입의 매개변수명 입니다.
@AfterThrowing(pointcut="examplePointcut()", throwing="ex")
public void afterThrowingAdvice(DataAccessException ex) {
    ...
}
이것 역시 첫번째 매개변수로 JoinPoint를 사용 할 수 있습니다.

After-Finally Advice
@After 애노테이션을 이용해 정의하며 Target 객체의 메소드에서 예외가 발생하든 발생하지 않든 종료되면
호출 됩니다. 이것은 자원을 해제 하기 위해 사용 됩니다.
@After("examplePointcut()")
public void afterAdvice(JoinPoint joinPoint) {
    System.out.println("자원 해제");
}

Around Advice
@Around 애노테이션을 이용해 정의하며 2.0이전의 방식과 사용법은 동일 합니다.
첫번째 매개변수로 반드시 ProceedingJoinPoint 타입의 변수를 선언해야 합니다.
@Around("examplePointcut()")
public Object aroundAdvice(ProceedingJoinPoint pjp) {
    System.out.println("호출 전");
    Object retVal = null;
    try {
        retVal = pjp.proceed();
    } catch (Throwable e) {
        e.printStackTrace();
    }
    System.out.println("호출 후");
    return retVal;
}

Advice 적용순서
2.0 이전에서는 Advisor의 order 속성을 이용했으나 2.0이후의 애노테이션 방식에서는
클래스내 선언된 Advice의 순서대로 적용 됩니다.
@Before("examplePointcut()")
public void before1() {
    ...      
}
   
@Before("examplePointcut()")
public void before2() {
    ...
}

@Before("examplePointcut()")
public void before3() {
    ...
}
3개의 Advice 적용 순서는 선언된 순서대로 before1() -> before2() -> before3() 입니다.

Introduction Advice
2.0 이전 방식에서는 특정 클래스를 상속받아서 Introduction Advice를 구현 했습니다.
2.0 이후에서는 POJO로 구현이 가능한데 잠금기능이 추가된 예제를 구현해 보겠습니다.

일반 bean 입니다.

잠금기능이 정의된 인터페이스를 구현한 클래스 입니다.
이 잠금 기능이 위에서 정의한 Foo 클래스에 추가됩니다.

그리고 Aspect를 정의하겠습니다.
추가된 기능이 정의된 인터페이스를 public static 필드로 선언 하고 @DeclareParents 애노테이션을 이용해
Pointcut과 구현클래스를 지정합니다. 그러면 Pointcut에 일치하는 클래스를 찾아서 구현클래스로 지정된
클래스를 Target 객체에 추가될 것입니다.

2.0이전에는 상속받은 DelegatingIntroductionInterceptor 클래스의 invoke 메소드를 재정의 하는방식으로
추가된 상태에 따라 메소드 호출을 제어 할 수 있었지만 2.0이후에서는 DelegatingIntroductionInterceptor 클래스를 상속받지 않기 때문에 별도의 Around Advice를 추가 했습니다.

테스트 클래스 (XML 설정은 별거 없기 때문에 생략 하겠습니다.)

결과는
안녕 Foo
현재 잠금상태 이므로 호출 할 수 없음

'Spring > AOP' 카테고리의 다른 글

Proxy 객체인지 확인하려면...?  (0) 2009.01.21
Spring AOP - aop스키마 기반  (0) 2008.01.14
Spring AOP - @AspectJ  (0) 2007.12.31
Spring AOP APIs - ProxyFactoryBean, AutoProxy  (0) 2007.12.31
Spring AOP APIs - Advisor  (2) 2007.12.31
Spring AOP APIs - Pointcut  (0) 2007.12.31
Posted by 째코

댓글을 달아 주세요