springAop 원칙의 Advisor 인터페이스 제품군 소개 및 Spring 트랜잭션 주석 @Transactional과 관련된 Advisor 분석 소스 코드에서 Advisor를 Bean과 연결하는 방법

1. 인터페이스 개요

 Advisor : AOP 알림 (연결 지점에서 수행 된 작업)에 대한 기본 인터페이스와 권장 사항의 적용 가능성을 결정하는 필터 (예 : 진입 점)가 포함되어 있습니다. 이 인터페이스는 Spring 사용자가 사용하기위한 것이 아니라 다양한 유형의 알림을 지원할 수있는 융통성을 제공합니다. Spring AOP는 AOP Alliance interception API를 준수하는 method interception을 통한 주변 알림 전달을 기반으로합니다. Advisor 인터페이스는 인터 셉션 구현을 사용할 필요없이 알림 전후와 같은 다양한 유형의 권장 사항을 지원합니다.

PointcutAdvisor : pointcut에 의해 구동되는 모든 Advisor의 수퍼 인터페이스입니다. 이것은 소개 어드바이저를 제외한 거의 모든 어드바이저를 포함합니다. PointcutAdvisor에는 컷 포인트 인 Pointcut 멤버 변수가 포함되어 있으며 Advisor 인터페이스에 Advice를 추가하면 완전한 측면을 형성합니다.

StaticMethodMatcherPointcutAdvisor : 기본적으로 모든 대상 클래스와 일치하는 정적 메서드 일치 자 컷 포인트에 의해 정의 된 측면입니다. (Shiro 권한 디자인은이 클래스를 상속하여 이루어집니다).

InstantiationModelAwarePointcutAdvisor :이 인터페이스에는 유일한 구현 클래스 InstantiationModelAwarePointcutAdvisorImpl이 있습니다. 이 클래스는 프로젝트에서 aspectj 주석을 통해 일반적으로 구현하는 aspect가 패키징 된 후 Advisor입니다.

AspectJExpressionPointcutAdvisor : Pointcut의 측면을 정의하기 위해 AspectJ pointcut 표현식에 사용됩니다.

AbstractPointcutAdvisor : PointcutAdvisor가 구현 한 추상 기본 클래스로, 특정 pointcut / notification 또는 자유롭게 구성 가능한 pointcut / notification을 반환하는 하위 클래스 일 수 있습니다.

둘째, Spring은 Bean이 프록시되어야하는지 여부를 어떻게 결정합니까?

Spring이 빈을 생성 한 후에는 현재 빈에 대한 프록시 객체를 생성해야하는지 여부를 결정해야합니다. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory 클래스의 doCreateBean 메소드는 모든 BeanPostProcessor의 postProcessAfterInitialization을 호출하는 applyBeanPostProcessorsAfterInitialization 메소드를 호출합니다. org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator는 BeanPostProcessor이고,이 클래스의 postProcessAfterInitialization은 wrapIfNecessary 메소드를 호출합니다. 즉, Bean을 프록시 객체로 래핑해야하는지 여부입니다. 프록시가 필요한지 여부의 원칙은 Bean에 연관된 Advisor가 있는지 여부와 프록시가 있는지 여부입니다.

셋째, Spring은 어떻게 모든 Advisor 소스 코드를 찾고 어떤 Advisor와 빈이 연관되어 있는지 확인하는 방법은 무엇입니까?

wrapIfNecessary 소스 코드의 일부

// 조언이 있으면 프록시를 만듭니다.
 // 빈에 대한 어드바이저를 가져옵니다.
Object [] specificInterceptors = getAdvicesAndAdvisorsForBean (bean.getClass (), beanName, null);
if (specificInterceptors! = DO_NOT_PROXY) {
   this.advisedBeans.put (cacheKey, Boolean.TRUE);
   객체 프록시 = createProxy (
         bean.getClass (), beanName, specificInterceptors, 새로운 SingletonTargetSource (bean));
   this.proxyTypes.put (cacheKey, proxy.getClass ());
   반환 프록시;
}
protected List <Advisor> findEligibleAdvisors (Class <?> beanClass, String beanName) {
   // 모든 Advisor 찾기
   List <Advisor> 후보자 조언자 = findCandidateAdvisors ();
  // 모든 Advisor에 적용될 수있는 Bean의 Advisor를 찾습니다.
   List <Advisor> QualifiedAdvisors = findAdvisorsThatCanApply (candidateAdvisors, beanClass, beanName);
   extendAdvisors (eligibleAdvisors);
   if (! eligibleAdvisors.isEmpty ()) {
      QualifiedAdvisors = sortAdvisors (eligibleAdvisors);
   }
   적격 고문을 반환하십시오.
}

findCandidateAdvisors ()의 소스 코드는 간단히 요약 할 수 있습니다. 컨테이너에서 Advisor를 구현하는 모든 클래스를 가져오고 aspectJ에서 생성 한 Advisor를 추가합니다.

findAdvisorsThatCanApply (candidateAdvisors, beanClass, beanName)의 소스 코드를 기록하고 살펴보십시오.

보호 된 List <Advisor> findAdvisorsThatCanApply (
      List <Advisor> 후보자 조언자, 클래스 <?> beanClass, 문자열 beanName) {
   // ThreadLocal에 현재 beanName 표시
   ProxyCreationContext.setCurrentProxiedBeanName (beanName);
   {
      return AopUtils.findAdvisorsThatCanApply (candidateAdvisors, beanClass);
   }
   드디어 {
     // ThreadLocal에서 현재 beanName 제거
      ProxyCreationContext.setCurrentProxiedBeanName (null);
   }
}

AopUtils findAdvisorsThatCanApply 소스 코드 분석

public static List <Advisor> findAdvisorsThatCanApply (List <Advisor> 후보자 조언자, 클래스 <?> clazz) {
   if (candidateAdvisors.isEmpty ()) {
      후보자 조언자 반환;
   }
   List <Advisor> compatibleAdvisors = new ArrayList <> ();
   for (Advisor 후보자 : 후보자 조언자) {
      // 소개 측면인지 여부 및 적용 가능 여부
      if (candidate instanceof IntroductionAdvisor && canApply (candidate, clazz)) {
         QualifiedAdvisors.add (후보);
      }
   }
   // 현재 사용되지 않으므로 값은 false입니다.
   부울 hasIntroductions =! QualifiedAdvisors.isEmpty ();
   for (Advisor 후보자 : 후보자 조언자) {
      if (IntroductionAdvisor 후보 인스턴스) {
         // 이미 처리됨
         계속하다;
      }
      if (canApply (candidate, clazz, hasIntroductions)) {
         QualifiedAdvisors.add (후보);
      }
   }
   적격 고문을 반환하십시오.
}

BeanFactoryTransactionAttributeSourceAdvisor (트랜잭션 주석과 연관된 어드바이저)를 예로 들어 다음 소스 코드를 분석하십시오. BeanFactoryTransactionAttributeSourceAdvisor의 컷 포인트는 TransactionAttributeSourcePointcut이고, TransactionAttributeSourcePointcut은 StaticMethodMatcherPointcut을 상속하고, StaticMethodMatcherPointcut에 해당하는 ClassFilter는 TrueClassFilter이므로 모든 클래스가 한곳에서 매칭됩니다.

StaticMethodMatcherPointcut은 StaticMethodMatcher를 상속하므로 TransactionAttributeSourcePointcut의 MethodMatcher는 TransactionAttributeSourcePointcut 자체입니다. 그래서 2에서 실행되는 것은 TransactionAttributeSourcePointcut의 match (Method method, Class <?> targetClass) 메소드입니다.

public static boolean canApply (Pointcut pc, Class <?> targetClass, boolean hasIntroductions) {
   Assert.notNull (pc, "Pointcut은 null이 아니어야합니다.");  
   //1
  if (! pc.getClassFilter (). matches (targetClass)) {
      거짓 반환;
   }

   MethodMatcher methodMatcher = pc.getMethodMatcher ();
   if (methodMatcher == MethodMatcher.TRUE) {
      // 어쨌든 어떤 메소드와 일치한다면 메소드를 반복 할 필요가 없습니다 ...
      true를 반환하십시오.
   }

   IntroductionAwareMethodMatcher IntroductionAwareMethodMatcher = null;
   if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      IntroductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
   }
  // 현재 클래스와 상위 클래스가 구현 한 모든 인터페이스를 찾습니다.
   Set <Class <? >> 클래스 = new LinkedHashSet <> ();
   if (! Proxy.isProxyClass (targetClass)) {
      classes.add (ClassUtils.getUserClass (targetClass));
   }
   classes.addAll (ClassUtils.getAllInterfacesForClassAsSet (targetClass));

   for (Class <?> clazz : 클래스) {
      // 부모 클래스에서 상속 된 메서드가 포함 된 메서드 목록 가져 오기
      Method [] methods = ReflectionUtils.getAllDeclaredMethods (clazz);
      for (Method method : methods) {
        // 2
         if (introductionAwareMethodMatcher! = null?
               IntroductionAwareMethodMatcher.matches (method, targetClass, hasIntroductions) :
               methodMatcher.matches (method, targetClass)) {
            true를 반환하십시오.
         }
      }
   }

   거짓 반환;
}

TransactionAttributeSourcePointcut 的 matches (Method method, Class <?> targetClass) 源码 :

@우세하다
public boolean matches (Method method, Class <?> targetClass) {
   if (TransactionalProxy.class.isAssignableFrom (targetClass) ||
         PlatformTransactionManager.class.isAssignableFrom (targetClass) ||
         PersistenceExceptionTranslator.class.isAssignableFrom (targetClass)) {
      거짓 반환;
   }
   TransactionAttributeSource bag = getTransactionAttributeSource ();
   return (tas == null || tas.getTransactionAttribute (method, targetClass)! = null);
}

먼저 TransactionalProxy, PlatformTransactionManager, PersistenceExceptionTranslator가 현재 클래스의 부모인지 판단하고, 그렇다면 false를 반환합니다. TransactionAttributeSourcePointcut은 실제로 추상 클래스이고 TransactionAttributeSourcePointcut의 getTransactionAttributeSource도 추상 메서드라는 것을 알았습니다. BeanFactoryTransactionAttributeSourceAdvisor의 소스 코드에서 BeanFactoryTransactionAttributeSourceAdvisor가 TransactionAttributeSourcePointcut을 정의 할 때이 메소드가 다시 작성되었음을 알 수 있습니다.

개인 최종 TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut () {
   @우세하다
   @Nullable
   protected TransactionAttributeSource getTransactionAttributeSource () {
      return transactionAttributeSource;
   }
};

반환 된 TransactionAttributeSource는 BeanFactoryTransactionAttributeSourceAdvisor의 멤버 변수입니다. BeanFactoryTransactionAttributeSourceAdvisor는 @bean (ProxyTransactionManagementConfiguration의 transactionAdvisor () 메소드)을 통해 Spring에 의해 구성됩니다.

@Bean (이름 = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role (BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor () {
   BeanFactoryTransactionAttributeSourceAdvisor 어드바이저 = new BeanFactoryTransactionAttributeSourceAdvisor ();
   advisor.setTransactionAttributeSource (transactionAttributeSource ());
   advisor.setAdvice (transactionInterceptor ());
   if (this.enableTx! = null) {
      advisor.setOrder (this.enableTx. <Integer> getNumber ( "order"));
   }
   반환 고문;
}

@콩
@Role (BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource () {
   return new AnnotationTransactionAttributeSource ();
}

따라서 최종 TransactionAttributeSource는 AnnotationTransactionAttributeSource입니다. AnnotationTransactionAttributeSource는 getTransactionAttribute (method, targetClass)를 호출하여 실제로 부모 클래스 AbstractFallbackTransactionAttributeSource의 메서드를 호출합니다.

public TransactionAttribute getTransactionAttribute (Method method, @Nullable Class <?> targetClass) {
   if (method.getDeclaringClass () == Object.class) {
      null을 반환합니다.
   }

   // 먼저 캐시 된 값이 있는지 확인합니다.
   객체 cacheKey = getCacheKey (method, targetClass);
   TransactionAttribute cached = this.attributeCache.get (cacheKey);
   if (캐시 됨! = null) {
      // 값은 트랜잭션 속성이 없음을 나타내는 표준 값이거나
      // 또는 실제 트랜잭션 속성입니다.
      if (캐시 됨 == NULL_TRANSACTION_ATTRIBUTE) {
         null을 반환합니다.
      }
      else {
         캐시 된 반환;
      }
   }
   else {
      // 해결해야합니다.
      TransactionAttribute txAttr = computeTransactionAttribute (method, targetClass);
      // 캐시에 넣습니다.
      if (txAttr == null) {
         this.attributeCache.put (cacheKey, NULL_TRANSACTION_ATTRIBUTE);
      }
      else {
         String methodIdentification = ClassUtils.getQualifiedMethodName (method, targetClass);
         if (txAttr instanceof DefaultTransactionAttribute) {
            ((DefaultTransactionAttribute) txAttr) .setDescriptor (methodIdentification);
         }
         if (logger.isTraceEnabled ()) {
            logger.trace ( "트랜잭션 메서드 '"+ methodIdentification + "'속성 추가 :"+ txAttr);
         }
         this.attributeCache.put (cacheKey, txAttr);
      }
      return txAttr;
   }
}

computeTransactionAttribute (method, targetClass)는 AnnotationTransactionAttributeSource에서 findTransactionAttribute (Method method) 및 findTransactionAttribute (Class <?> clazz)를 계속 호출하여 메서드 또는 클래스에 트랜잭션 주석이 있는지 여부를 찾습니다.

 

 

추천

출처blog.csdn.net/sinat_33472737/article/details/105414097