2011-10-03 2 views
16

나는 Spring으로 실행되는 애플리케이션을 가지고 있으며, 몇몇 곳에서는 AOP를 사용하고있다. 인터페이스 레벨에서 @Transactional 어노테이션을 사용하고자하기 때문에 Spring이 JDK 프록시를 생성하도록 허용해야한다. 따라서 프록시 대상 클래스 속성을 true로 설정하지 않았습니다. 다른 한편으로는, 필자가 권고하는 모든 단일 클래스에 대한 인터페이스를 만들고 싶지 않다. 인터페이스가 이해가되지 않는다면 구현 만하고 Spring은 CGLIB 프록시를 만들어야한다.Spring 내에서 JDK와 CGLIB 프록시를 혼합하기

내가 설명한대로 모든 것이 완벽하게 작동했습니다. 그러나 나는 인터페이스에 들어가서 구현 클래스 ("@Transactional"처럼)에 의해 "상속 (inherited)"되는 몇 가지 다른 주석 (나로부터 생성 된)을 가지고 싶었다. 스프링에서 AOP에 대한 내장 된 지원으로 그렇게 할 수 없다는 것을 알았습니다. (적어도 연구 후에는 어떻게해야할지 모르겠습니다. 인터페이스의 주석은 구현 클래스에서 볼 수 없으며, 그러므로 그 수업은 조언을받지 못한다.)

그래서 나는 다른 방법 주석 인터페이스에 갈 수 있도록 내 자신의 포인트 컷인터셉터을 구현하기로 결정했다. 기본적으로, 내 포인트 컷은 메소드에 대한 주석을 찾는다. 클래스 나 슈퍼 클래스가 구현하는 인터페이스의 같은 메소드 (같은 이름과 매개 변수 유형)에서 찾지 못할 때까지 찾는다.

문제점 : DefaultAdvisorAutoProxyCreator 빈을 선언하면이 pointcut/interceptor가 제대로 적용되므로 인터페이스가없는 클래스에 대한 조언 동작이 중단됩니다. 분명히 무언가가 잘못되어 Spring은 CGLIB를 사용하고 JDK를 사용하여 한 번 내 클래스를 두 번 프록시하려고 시도합니다.

@Component 
public class ServiceExecutorImpl 
{ 
    @Transactional 
    public Object execute(...) 
    { 
     ... 
    } 
} 

내가 다른 콩에 자동으로 묶어하려고 :

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" 
    xsi:schemaLocation=" 
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> 

    <!-- Activates various annotations to be detected in bean classes: Spring's 
     @Required and @Autowired, as well as JSR 250's @Resource. --> 
    <context:annotation-config /> 

    <context:component-scan base-package="mypackage" /> 

    <!-- Instruct Spring to perform declarative transaction management automatically 
     on annotated classes. --> 
    <tx:annotation-driven transaction-manager="transactionManager" /> 

    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" /> 

    <bean id="logger.advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> 
     <constructor-arg> 
      <bean class="mypackage.MethodAnnotationPointcut"> 
       <constructor-arg value="mypackage.Trace" /> 
      </bean> 
     </constructor-arg> 
     <constructor-arg> 
      <bean class="mypackage.TraceInterceptor" /> 
     </constructor-arg> 
    </bean> 
</beans> 

이없이 인터페이스 나 프록시를 사용하는 클래스가있다 :

내 구성 파일입니다 예 :

다음과 같은 예외가 있습니다.

java.lang.IllegalArgumentException: Can not set mypackage.ServiceExecutorImpl field mypackage.BaseService.serviceExecutor to $Proxy26 

은 봄 출력의 일부 라인은 다음과 같습니다

13:51:12,672 [main] DEBUG [org.springframework.aop.framework.Cglib2AopProxy] - Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [[email protected]] 
... 
13:51:12,782 [main] DEBUG [org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'serviceExecutorImpl' with 0 common interceptors and 1 specific interceptors 
13:51:12,783 [main] DEBUG [org.springframework.aop.framework.JdkDynamicAopProxy] - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [[email protected]] 

누군가가 도움이 될 것입니다 생각하면 내가 전체 출력을 제공 할 수 있습니다. Spring이 왜 내 클래스를 "이중 프록시"하려고하는지, 그리고 이것이 DefaultAdvisorAutoProxyCreator 빈을 선언 할 때 왜 발생하는지에 대해서는 잘 모른다.

저는 지금 당분간이 문제에 시달렸습니다. 어떤 도움이나 아이디어라도 대단히 감사 할 것입니다.

편집 :

이것은 요청한대로 나의 인터셉터 소스 코드입니다. 기본적으로 메소드 실행을 기록합니다 (@Trace로 주석 처리 된 메소드 만 인터셉트됩니다). 메소드가 @Trace (false)로 주석되어 있으면 메소드가 리턴 될 때까지 로깅이 일시 중단됩니다.

public class TraceInterceptor 
    implements 
     MethodInterceptor 
{ 

    @Override 
    public Object invoke(
     MethodInvocation invocation) 
     throws Throwable 
    { 
     if(ThreadExecutionContext.getCurrentContext().isLogSuspended()) { 
      return invocation.proceed(); 
     } 

     Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(), 
      invocation.getThis().getClass()); 
     Trace traceAnnotation = method.getAnnotation(Trace.class); 

     if(traceAnnotation != null && traceAnnotation.value() == false) { 
      ThreadExecutionContext.getCurrentContext().suspendLogging(); 
      Object result = invocation.proceed(); 
      ThreadExecutionContext.getCurrentContext().resumeLogging(); 
      return result; 
     } 

     ThreadExecutionContext.startNestedLevel(); 
     SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss.SSS"); 
     Logger.log("Timestamp: " + dateFormat.format(new Date())); 

     String toString = invocation.getThis().toString(); 
     Logger.log("Class: " + toString.substring(0, toString.lastIndexOf('@'))); 

     Logger.log("Method: " + getMethodName(method)); 
     Logger.log("Parameters: "); 
     for(Object arg : invocation.getArguments()) { 
      Logger.log(arg); 
     } 

     long before = System.currentTimeMillis(); 
     try { 
      Object result = invocation.proceed(); 
      Logger.log("Return: "); 
      Logger.log(result); 
      return result; 
     } finally { 
      long after = System.currentTimeMillis(); 
      Logger.log("Total execution time (ms): " + (after - before)); 
      ThreadExecutionContext.endNestedLevel(); 
     } 
    } 

    // Just formats a method name, with parameter and return types 
    private String getMethodName(
     Method method) 
    { 
     StringBuffer methodName = new StringBuffer(method.getReturnType().getSimpleName() + " " 
      + method.getName() + "("); 
     Class<?>[] parameterTypes = method.getParameterTypes(); 

     if(parameterTypes.length == 0) { 
      methodName.append(")"); 
     } else { 
      int index; 
      for(index = 0; index < (parameterTypes.length - 1); index++) { 
       methodName.append(parameterTypes[ index ].getSimpleName() + ", "); 
      } 
      methodName.append(parameterTypes[ index ].getSimpleName() + ")"); 
     } 
     return methodName.toString(); 
    } 
} 

고마워요!

답변

3

여기에 일치하지 않는 항목이 있습니다. 해당 항목이 $ProxyXX 인 경우 인터페이스가 있음을 나타냅니다. 인터페이스가 없는지 확인하십시오. 일부 다른 노트 : 포인트 컷에

  • 대상 개체가 이미 (x instanceof Advised)을 사용하여 프록시 경우 확인할 수는, 당신은 프록시 전략 행된을 정의하는 <aop:scoped-proxy />을 사용할 수 있습니다 Advised

  • 으로 캐스팅 할 수 있습니다 콩

+0

글쎄, CGLIB 프록시가 일부 인터페이스를 구현 한 것 같습니다. 하지만이게 제 통제에서 벗어난 것 같아요, 그렇죠? 필자가 작성한 원래 클래스는 인터페이스를 구현하지 않으며 클래스를 확장하지도 않습니다 (물론 Object는 제외). Advised를 확인하는 것이 도움이 될지 모르겠다. 왜냐하면이 클래스는 Spring에서 (@Transactional로 인해) 조언을 받았기 때문에 내가 쓴 Pointcut이 아니라. 를 살펴보고 도움이되는지 확인하겠습니다. 감사! –

+0

인터셉터 코드를 표시 할 수 있습니까? – Bozho

+0

인터셉터의 출처로 내 질문을 편집했습니다. 어떤 단서? –

13

나는 Bozho가 제안한 'scoped-proxy'를 사용하여 해결책을 찾았다. 나는 거의 유일한 주석을 사용하고 있기 때문에

내 ServiceExecutor 클래스는 이제 다음과 같습니다 : 이제 모든 것이 잘 작동 할 seens

@Component 
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 
public class ServiceExecutor 
{ 
    @Transactional 
    public Object execute(...) 
    { 
     ... 
    } 
} 

까지. 왜 인터페이스를 구현하지 않기 때문에 CGLIB를 사용하여이 클래스를 프록시해야하는지 Spring에게 명시 적으로 알려줘야하는지 모르겠습니다. 어쩌면 그것은 버그 야, 나도 몰라.

감사합니다. Bozho.

관련 문제