Как Spring реализует АОП, пожалуйста, перестаньте говорить о cglib!

Spring

1. Начните с аннотаций, чтобы найти соответствующие основные классы

В своей недавней работе я реализовал функции АОП на основе аннотаций.Обычно используемая аннотация для включения АОП — @EnableAspectJAutoProxy, и мы начнем с нее.


Шаги описанного выше процесса анимации:
@EnableAspectJAutoProxy
--> AspectJAutoProxyRegistrar
-->AopConfigUtils .registerAspectJAnnotationAutoProxyCreatorIfNecessary
-->AnnotationAwareAspectJAutoProxyCreator.class

AnnotationAwareAspectJAutoProxyCreator просматривает свои китайские аннотации (ниже) и определяет, что это основной класс АОП! --Вэнь Анши 20191020

/**
1.AspectJAwareAdvisorAutoProxyCreator的子类
,用于处理当前应用上下文中的注解切面
2.任何被AspectJ注解的类将自动被识别。
3.若SpringAOP代理模式可以识别,优先使用Spring代理模式。
4.它覆盖了方法执行连接点
5.如果使用<aop:include>元素,
  则只有名称与include模式匹配的@aspectj bean才被视为切面
  ,并由spring自动代理。
6. Spring Advisors的处理请查阅,
org.springframework.aop
.framework.autoproxy.AbstractAdvisorAutoProxyCreator
 */
@SuppressWarnings("serial")
public class AnnotationAwareAspectJAutoProxyCreator 
extends AspectJAwareAdvisorAutoProxyCreator {
    //...省略实现
    }注解切面

Хотя был найден основной класс, основной метод не найден! Далее мы пытаемся нарисовать диаграмму классов, чтобы определить основной метод.

2. Нарисуйте диаграмму основного класса и угадайте основной метод

Частичная диаграмма классов AnnotationAwareAspectJAutoProxyCreator.

AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator

Из диаграммы классов видно, что AnnotationAwareAspectJAutoProxyCreator реализует BeanPostProcessor, и функция АОП должна выполняться после создания bean-компонента Предполагается, что AnnotationAwareAspectJAutoProxyCreator реализует BeanPostProcessor's postProcessAfterInitialization (пост-обработка созданного bean-компонента) и является основным методом. Обратите внимание на метод postProcessAfterInitialization, реализованный AnnotationAwareAspectJAutoProxyCreator, который на самом деле находится в родительском классе AbstractAutoProxyCreator.

//AbstractAutoProxyCreator中的postProcessAfterInitialization实现
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
 throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

Выяснилось, что подозрительный метод wrapIfNecessary был найден, а исходный код был следующим, и был найден метод createProxy. Убедитесь, что вы находитесь в правильном месте.

protected Object wrapIfNecessary
        (Object bean, String beanName, Object cacheKey) {
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   if (isInfrastructureClass(bean.getClass()) 
                   || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // 创建代理
   Object[] specificInterceptors = 
       getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      Object proxy = createProxy(
            bean.getClass(), beanName, 
            specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

То есть AnnotationAwareAspectJAutoProxyCreator реализует метод postProcessAfterInitialization BeanPostProcessor, в котором wrapIfNecessary реализует функцию AOP. В wrapIfNecessary есть 2 основных метода.

  • getAdvicesAndAdvisorsForBean получает текущие усилители сопоставления компонентов
  • createProxy создает прокси для текущего компонента
    Чтобы понять суть процесса, необходимо проанализировать эти два метода.

3. Ознакомьтесь с ключевыми методами и поймите основные процессы

3.1 getAdvicesAndAdvisorsForBean получает энхансеры, соответствующие текущему компоненту

Просмотрите исходный код следующим образом, реализация по умолчанию находится в AbstractAdvisorAutoProxyCreator.

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, 
      @Nullable TargetSource targetSource) {
   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}

Проверьте метод findEligibleAdvisors и сделайте 3 вещи

  • Найти все энхансеры, то есть все bean-компоненты с аннотацией @Aspect
  • Найти соответствующий энхансер, который основан на@Before, @After и другие выражения аннотации сопоставляются с текущим bean-компонентом и предоставляют совпадающие.
  • Расширение и сортировка совпадающих усилителей заключается в сортировке в соответствии со значением данных @Order или getOrder PriorityOrdered, чем меньше, тем лучше.
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   //找所有增强器
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   //找所有匹配的增强器
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
       //排序
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}

AnnotationAwareAspectJAutoProxyCreator переписывает findCandidateAdvisors, посмотрим что реализовано

3.1.1 findCandidateAdvisors находит все энхансеры, то есть все bean-компоненты, аннотированные @Aspect

@Override
protected List<Advisor> findCandidateAdvisors() {
   // Add all the Spring advisors found according to superclass rules.
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Build Advisors for all AspectJ aspects in the bean factory.
   if (this.aspectJAdvisorsBuilder != null) {
      //@Aspect注解的类在这里除了
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
   return advisors;
}

Из этого метода мы можем видетьСпособ обработки аннотации @aspect: this.aspectjadvisorsbuilder.buildascectjadvisors (). Этот метод заключается в следующем:

public List<Advisor> buildAspectJAdvisors() {
   List<String> aspectNames = this.aspectBeanNames;

   if (aspectNames == null) {
      synchronized (this) {
         aspectNames = this.aspectBeanNames;
         if (aspectNames == null) {
            List<Advisor> advisors = new ArrayList<>();
            aspectNames = new ArrayList<>();
            //找到所有BeanName
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  this.beanFactory, Object.class, true, false);
            for (String beanName : beanNames) {
               if (!isEligibleBean(beanName)) {
                  continue;
               }
               // 必须注意,bean会提前暴露,并被Spring容器缓存,但是这时还不能织入。
               Class<?> beanType = this.beanFactory.getType(beanName);
               if (beanType == null) {
                  continue;
               }
               if (this.advisorFactory.isAspect(beanType)) {
                  //找到所有被@Aspect注解的类
                  aspectNames.add(beanName);
                  AspectMetadata amd = new AspectMetadata(beanType, beanName);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                     MetadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                     //解析封装为Advisor返回
                     List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                     if (this.beanFactory.isSingleton(beanName)) {
                        this.advisorsCache.put(beanName, classAdvisors);
                     }
                     else {
                        this.aspectFactoryCache.put(beanName, factory);
                     }
                     advisors.addAll(classAdvisors);
                  }
                  else {
                     // Per target or per this.
                     if (this.beanFactory.isSingleton(beanName)) {
                        throw new IllegalArgumentException("Bean with name '" + beanName +
                              "' is a singleton, but aspect instantiation model is not singleton");
                     }
                     MetadataAwareAspectInstanceFactory factory =
                           new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                     this.aspectFactoryCache.put(beanName, factory);
                     advisors.addAll(this.advisorFactory.getAdvisors(factory));
                  }
               }
            }
            this.aspectBeanNames = aspectNames;
            return advisors;
         }
      }
   }

   if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
   List<Advisor> advisors = new ArrayList<>();
   for (String aspectName : aspectNames) {
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
      if (cachedAdvisors != null) {
         advisors.addAll(cachedAdvisors);
      }
      else {
         MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
         advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
   }
   return advisors;
}

Этот метод можно обобщить следующим образом:

  • Найти все BeanNames
  • Отфильтровать классы, аннотированные с помощью @Aspect, на основе BeanName
  • Для методов в классе, аннотированных Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class, отсортируйте по приведенному выше порядку аннотации, а затем по имени метода, каждый метод соответствует советнику.

3.2 createProxy создает прокси для текущего компонента.

3.2.1 2 способа создания прокси

Как мы все знаем, есть два широко используемых способа создания прокси: создание JDK и CGLIB Давайте рассмотрим примеры создания прокси в этих двух.

3.2.1 .1 Пример создания прокси jdk

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxyMain {

    public static void main(String[] args) {
        JDKProxyTestInterface target = new JDKProxyTestInterfaceImpl();
        // 根据目标对象创建代理对象
        JDKProxyTestInterface proxy =
         (JDKProxyTestInterface) Proxy
         .newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), 
                new JDKProxyTestInvocationHandler(target));
        // 调用代理对象方法
        proxy.testProxy();
    }

    interface JDKProxyTestInterface {
        void testProxy();
    }
    static class JDKProxyTestInterfaceImpl 
                    implements JDKProxyTestInterface {
        @Override
        public void testProxy() {
            System.out.println("testProxy");
        }
    }
   static class JDKProxyTestInvocationHandler 
                           implements InvocationHandler {
        private  Object target;
        public JDKProxyTestInvocationHandler(Object target){
            this.target=target;
        }
        @Override
        public Object invoke(Object proxy, Method method,
                             Object[] args) throws Throwable {
            System.out.println("执行前");
            Object result=  method.invoke(this.target,args);
            System.out.println("执行后");
            return result;
        }
    }

3.2.1.2 Пример cglib для создания прокси

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyTest {

   static class CglibProxyService {
        public  CglibProxyService(){
        }
        void sayHello(){
            System.out.println(" hello !");
        }
    }

    static class CglibProxyInterceptor implements MethodInterceptor{
        @Override
        public Object intercept(Object sub, Method method,
             Object[] objects, MethodProxy methodProxy)
                                          throws Throwable {
            System.out.println("before hello");
            Object object = methodProxy.invokeSuper(sub, objects);
            System.out.println("after hello");
            return object;
        }
    }

    public static void main(String[] args) {
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(CglibProxyService.class);
        // 设置enhancer的回调对象
        enhancer.setCallback(new CglibProxyInterceptor());
        // 创建代理对象
        CglibProxyService proxy= (CglibProxyService)enhancer.create();
        System.out.println(CglibProxyService.class);
        System.out.println(proxy.getClass());
        // 通过代理对象调用目标方法
        proxy.sayHello();
    }
}

3.2.1.3 Разница между jdk и cglib при создании прокси

тип jdk создает динамический прокси CGLIB создает динамический агент
принцип Динамический прокси-сервер Java использует механизм отражения для создания анонимного класса, реализующего интерфейс прокси-сервера, и вызывает InvokeHandler для его обработки перед вызовом конкретного метода. Динамический прокси-сервер cglib использует пакет с открытым исходным кодом asm, загружает файл класса класса прокси-объекта и создает подкласс, изменяя его байт-код для его обработки.
основной класс Прокси создает прокси, используя механизм отражения, для создания интерфейса перехватчика метода InvocationHandler анонимного класса, который реализует интерфейс прокси и должен реализовать метод вызова. net.sf.cglib.proxy.Enhancer: основной класс расширения, который динамически создает экземпляры подкласса класса делегата с помощью технологии байт-кода.net.sf.cglib.proxy.MethodInterceptor: интерфейс перехватчика метода, который должен реализовать метод перехвата
ограничение Только классы, которые реализуют интерфейс, могут быть проксированы Вы не можете проксировать классы с окончательной модификацией и не можете обрабатывать методы с окончательной модификацией.

3.2.2 Как Spring выбирает, какой метод использовать

Spring выбирает способ проксирования в DefaultAopProxyFactory.

public class DefaultAopProxyFactory implements AopProxyFactory, 
                                                        Serializable {
   @Override
   public AopProxy createAopProxy(AdvisedSupport config) 
                                   throws AopConfigException {
      if (config.isOptimize() 
          || config.isProxyTargetClass()
          || hasNoUserSuppliedProxyInterfaces(config)) {
         Class<?> targetClass = config.getTargetClass();
         if (targetClass == null) {
            throw new AopConfigException(
            "TargetSource cannot determine target class: " 
            +"Either an interface or a target "+
           " is required for proxy creation.");
         }
         if (targetClass.isInterface() 
                 || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
         }
         return new ObjenesisCglibAopProxy(config);
      }
      else {
         return new JdkDynamicAopProxy(config);
      }
   }
   //...
   }
  • config.isOptimize() Просматривая комментарии к исходному коду, я обнаружил, что речь идет о том, следует ли использовать активную стратегию при настройке прокси-сервера cglib. Обычно это значение не рекомендуется!
  • Config.isproxyTargetClass() — это атрибут proxyTargetClass в @EnableAspectJautoproxy.

//exposeProxy=true AopContext может получить доступ, proxyTargetClass=true CGLIB генерирует прокси @EnableAspectJAutoProxy (exposeProxy = true, proxyTargetClass = true)

  • hasNoUserSuppliedProxyInterfaces Наличие прокси-интерфейса

Резюме Как выбрать способ создания прокси:

  1. Если установлено значение proxyTargetClass=true, это должен быть прокси-сервер CGLIB.
  2. Если proxyTargetClass=false, целевой объект реализует интерфейс и использует прокси-сервер JDK.
  3. Если вы не реализуете интерфейс, перейдите в агент CGLIB

4. Резюме

Как Spring реализует АОП? , ты можешь сказать:

  1. AnnotationAwareAspectJAutoProxyCreator — это основной класс обработки АОП.
  2. AnnotationAwareAspectJAutoProxyCreator реализует BeanProcessor, основным методом которого является postProcessAfterInitialization.
  3. Основная реализация разделена на 2 этапа
    getAdvicesAndAdvisorsForBean получает текущие усилители сопоставления компонентов createProxy создает прокси для текущего компонента
  4. Основная логика GetAdvicesAndAdvisorSforberbean выглядит следующим образом
    а) Найдите все энхансеры, то есть все бины, аннотированные @Aspect.
    б) Найдите соответствующий энхансер, то есть в соответствии с выражениями в аннотациях, таких как @Before, @After, сопоставьте текущий компонент и выставьте соответствующие.
    в) Расширение и сортировка совпадающих энхансеров заключается в сортировке в соответствии со значением данных @Order или getOrder для PriorityOrdered, чем меньше, тем выше.
  5. createProxy имеет 2 метода создания: прокси JDK или CGLIB.
    А. Если установлено значение proxyTargetClass=true, это должен быть прокси-сервер CGLIB.
    б. Если proxytargetcass = false, целевой объект реализует интерфейс, пройти агент JDK
    в) Если интерфейс не реализован, пройдите через прокси CGLIB