Исходный код АОП второй кисти

Java

Каждая мелочь, которую он делает, похожа на спасительную соломинку, и когда я смотрю на это однажды, эй, моя дорогая, он уже держит высокое дерево, которое я могу смотреть на

1. Статический прокси-сервер, динамический прокси-сервер JDK и динамический прокси-сервер CGLB.2. Система АОП2.1 Понимание основных понятий2.2 Spring предоставляет рабочие компоненты2.2.1. Предложения2.2.2. Поиск консультанта2.2.3. Когда агент создан2.2.4 Создание агента2.2.5. Исполнение агента3. Резюме

Однажды: Лидер предложил добавить журнал операций в метод операции удаления.

Какую работу выполняет АОП, чтобы выполнить эту волю? ?

Все это начинается с агента

1. Статический прокси-сервер, динамический прокси-сервер JDK и динамический прокси-сервер CGLB.

1. Статический прокси:

  • Жестко закодированный статический прокси: Проще говоря, он достигает эффекта прокси за счет дизайна кода.
  • Плетение компиляции AspectJ: компилировать содержимое в байт-код во время компиляции,

Важно: вплетайте содержимое в прокси-объекты во время компиляции.

2. Динамический прокси: достигните цели динамического прокси, динамически генерируя байт-коды нового класса.

  • Динамический прокси JDK: режим Proxy+InvocationHandler
  • Динамический прокси CGLB: Enhancer + MethodInterceptor(CallBack)

Важно: Генерируется новый байт-код, динамический прокси

Подробности читайте в моемСтатический прокси и динамический прокси JDK и динамический прокси CGLIB

2. Система АОП

2.1 Понимание основных понятий

Давайте вернемся к этой фразе领导建议给删除操作方法添加操作日志

Сюда входят пункты:

  • 谁提的:Свинец
  • 给谁提的: операция удаления
  • 提的什么建议: добавить журнал операций

На данный момент было представлено три интерфейса:

  • Advisor: подсказчик
  • Pointcut: Бросьте, каким людям вы даете советы?
  • Advise: совет, уведомление (предлагаемый контент)

Конечно, есть несколько скрытых ролей, и в системе АОП тоже есть соответствующие определения:

  • TargetSource: цель
  • Joinpoint: точка соединения.Когда лидер упоминает, что предложение относится к методу, точка соединения является телом инкапсуляции информации этого метода. существуетJoinpointМы можем получить информацию о целевом методе в
  • Advised: Когда человеку советуют добиться успеха, его можно рассматривать как Советуемого.

Отношения между ними:

  • Advisor= Pointcut + Advise
  • Advised = N * Advisor : человека могут консультировать несколько советников.

Эти концепции составляют общую концептуальную основу системы АОП.

На данный момент: мы достигаем консенсуса,

  • Когда я занимаюсь АОП-программированием, на самом деле это процесс внесения предложений.
  • Когда целевому объекту будет сделано действительное предложение, для него будет создан прокси

2.2 Spring предоставляет рабочие компоненты

Руководствуясь спецификацией, остальное — реализовать спецификацию, что предлагает Spring?

Взяв в качестве примера наш самый знакомый аспект программирования, транзакцию, давайте посмотрим на процесс работы АОП-системы.

2.2.1. Предложения

Аспекты, суть бизнеса заключается в том, чтобы давать рекомендации. Например, ниже я даюcn.wqd.aopМетоды в пакете предполагают ведение журнала, а транзакции предполагают управление транзакциями для методов.

(1. Разрезать лицо

@Aspect
@Component
public class WebLogAcpect {
    private Logger logger = LoggerFactory.getLogger(WebLogAcpect.class);

    //定义切入点,切入点为com.example.aop下的所有函数
    @Pointcut("execution(public * cn.wqd.aop..*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        System.out.println("这里是AOP前置方法");
    }
}

(2. Дела

Service
public class TransactionalService {
    @Autowired
    DataSource dataSource;

    @Autowired
    UserDao userDao;

    @Transactional
    public String save(){
        System.out.println("被事务方法");
        User user = new User("被事务方法",1);
        Map<Object, Object> map = TransactionSynchronizationManager.getResourceMap();
        System.out.println(userDao.getClass().getName());//
        userDao.save(user);
        return "save";
    }

Как использовать АОП нам наиболее привычно, но как они соотносятся с вышеперечисленными ролями? не смотри вниз

2.2.2. Поиск консультанта

Сделано предложение, как Spring узнает, есть ли предложение.

(1.BeanFactoryAdvisorRetrievalHelper

Чтобы найти этих советников в приложении, Spring сначала предоставляетBeanFactoryAdvisorRetrievalHelper, инструмент поиска подсказок: цель состоит в том, чтобы идентифицировать те实现了Advisor接口的Suggesters, зарегистрировать их как Bean и кэшировать.

public class BeanFactoryAdvisorRetrievalHelper {
public List<Advisor> findAdvisorBeans() {
  (1)
  advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
  (2)
  this.cachedAdvisorBeanNames = advisorNames;
  (3)
  advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
}

Логика поиска: найти все реализацииAdvisorBeandefiniton; кэшировать их beanName; вызывать логику создания Bean для создания Bean out.

прямая реализацияAdvisorинтерфейс, который показывает себя как подсказчик.

Но мы обычно не используем такую ​​прямую реализацию.AdvisorПуть.@AspectАннотация, транзакция - это то, что мы обычно используем. Так они Советник?

(2.@AspectСоветник секции

BeanFactoryAspectJAdvisorsBuilder

Пучок@AspectСовет аннотированного класса зачитывается, и Spring предоставляет инструмент построения предложений для этого.BeanFactoryAspectJAdvisorsBuilder
Строитель предложения аспекта.

Буквальный перевод от его названия: это инструмент для создания bean-компонентов Aspect Adviser.

private final AspectJAdvisorFactory advisorFactory;
public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) {
        this(beanFactory, new ReflectiveAspectJAdvisorFactory(beanFactory));
}

Благодаря методу построения мы обнаруживаем, что он создастReflectiveAspectJAdvisorFactory, Как видно из его названия, он работает через механизм отражения.BeanFactoryAspectJAdvisorsBuilderБольшая часть работы фактически выполняетсяReflectiveAspectJAdvisorFactoryЗавершенный.

public List<Advisor> buildAspectJAdvisors() {

synchronized (this) {
    (1)
     String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Object.class, true, false);
    (2)
                    for (String beanName : beanNames) {
          (3)
          if (this.advisorFactory.isAspect(beanType)) {
              MetadataAwareAspectInstanceFactory factory =
                                                    new PrototypeAspectInstanceFactory(this.beanFactory, beanName);         
              (4)
              advisors.addAll(this.advisorFactory.getAdvisors(factory));
          }
    (5)
     this.aspectBeanNames = aspectNames;
                    return advisors;
     }
}

}

Анализ процесса сборки:

  1. Найти все BeanDefinitions контейнера
  2. Обход этих BeanDefinitions
  3. определить, является ли онisAspectкласс: этот шаг(AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);Проверьте, если этоAspectвызывать
  4. впредьAspectИзвлеките все предложения из класса и зарегистрируйте их как bean-компоненты.
  5. Стандартный кеш (spring 很多工具类都注重缓存的使用)

Наиболее важная часть процесса идентификации и извлечения предлагающего находится на этапе 4. Давайте более подробно рассмотрим, что делает этот этап.
Этот шаг производитсяReflectiveAspectJAdvisorFactoryЗавершенный

отAspectОпределите предлагающего в классе и зарегистрируйте компонент

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        for (Method method : getAdvisorMethods(aspectClass)【1】) {
            Advisor advisor = 【2】getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        // Find introduction fields.
        for (Field field : aspectClass.getDeclaredFields()【3】) {
            Advisor advisor = 【4】getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
}

обработать:

  1. стать первымAspectВ аннотированном классе все методы, которые могут быть подсказками:getAdvisorMethodsполучено неPointcutАннотированные методы, это могут быть методы-подсказки (включая родительские классы, методы интерфейса)
  2. Переберите эти методы:getAdvisorПопробуйте решить, являются ли эти методы подсказками или нет, посмотрев, снабжены ли они следующими аннотациями.
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
            Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

Если да, проанализируйте информацию pointcut в аннотации [например: в приведенном выше примере@Before("webLog()")Pointcut, представленный weblog() в аннотации]. Упакован вместе с информацией об этом методе вInstantiationModelAwarePointcutAdvisorImplподсказчик.

В настоящее время

  • Содержание этого методаПредлагаемый контент, метод обернут какAspectJMethodBeforeAdviceпредложение
  • webLog()даТочка отсечки
  • InstantiationModelAwarePointcutAdvisorImplвнушающий
  1. получать@DeclareParentsАтрибуты аннотаций: Здесь также можно настроить советников. отличный отBeforeТакие методы, как аннотации аннотаций, являются усовершенствованием существующих методов. Это усиление называется индуцированным усилением, т.一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能, Это реферальное улучшение

4. Создайте советник для настроенных реферальных улучшений: DeclareParentsAdvisor

На этом этапе предложения, настроенные в аннотированном классе @Aspect, анализируются, и создается подсказчик.

  • Аннотированные классы @Aspect: можно настроить несколько подсказчиков
  • Аннотированный класс @Aspect: можно настроить два улучшения: обычное улучшение и вводное улучшение.

(3. Бизнес-консультант

Давайте посмотрим, как находится советник по сделкам?
Начать аннотацию транзакцииEnableTransactionManagementзарегистрируетProxyTransactionManagementConfigurationкласс конфигурации

public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        return advisor;
    }
  ....
}

Этот класс конфигурации напрямую регистрирует предлагающего:BeanFactoryTransactionAttributeSourceAdvisor
Этот предлагающий: сTransactionInterceptorкак предложениеTransactionAttributeSourcePointcutдля точки среза.

наш@TransactionalРоль аннотаций в целевом методе состоит в том, чтобы пометить, чья пометка? тангенциальный маркер,

Рассмотрим значение нижней точки отсечки: она означает, что усиливать, именно в ееmatchesметод, чтобы соответствовать диапазону

public boolean matches(Method method, Class<?> targetClass) {
        if (TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

TransactionAttributeSourcePointcutточечный разрезTransactionAttributeSourceМетод getTransactionAttribute вычисляет, соответствует ли целевой метод условиям, которые необходимо улучшить, и соответствует ли@Transactionalотметка

Мы уже знаем процесс получения этих двух видов рекомендаций. Теперь, когда у вас есть подсказчик, следующим шагом будет создание прокси для целевого метода, который будет принимать эти предложения. Здесь есть две проблемы:

  1. Когда создается прокси?
  2. Как создать прокси?
2.2.3. Когда агент создан

Эта часть включает в себя жизненный цикл Bean.

Время создания прокси должно быть после того, как целевой объект станет доступен, и тогда будет создан прокси. Если нет целевых объектов, кто является рекомендателем?

AbstractAdvisorAutoProxyCreator

На последнем шаге жизненного цикла создания Бина, то есть после завершения инициализации, он пройдет ее еще раз.BeanPostProcessor.postProcessAfterInitializationметод, выполните другое расширение. В этот момент фасоль приобрела форму.

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        ......
    ......
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

Абстрактный класс BeanPostProcessorAbstractAdvisorAutoProxyCreatorОн будет решать, создавать ли прокси-объект, исходя из того, есть ли предлагающий для текущего целевого объекта.

Смысл этого очень ясен: после создания бина оценивается, есть ли кто-то, кто дает ему совет, и будет создан прокси.

20.05.2020 Исправлено: когда дело доходит до времени создания прокси, на самом деле возникает другая ситуация. В процессе создания bean-компонента ObjectFactory, инкапсулирующая раннюю ссылку на bean-компонент, будет как можно скорее открыта для кэша третьего уровня, а вызов ObjectFactory.getObject() выполнит логику расширения для bean-компонента.

  • При отсутствии циклической ссылки логика в третьем кеше не будет выполняться, и бин будет удален из третьего кеша после его создания.
  • Но при циклической зависимости будет выполняться логика в третьем кеше, при получении бина процесс создания прокси бина будет выполняться заранее, но при этом бин не прошел через жизненный цикл.

Итак, если быть точным,创建的代理时机是在目标对象引用创建后。这个之后可能是属性赋值之前;也可能是Bean赋值属性,执行完初始化方法之后.

Настало время создать прокси

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;
        }

        【1】是否有适合当前Bean的建议者。
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
      【2】创建代理对象
            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;
    }

getAdvicesAndAdvisorsForBean:

  • Сначала выполните логику поиска предложения, упомянутую выше,Найдите подсказки, которые существуют в текущем контексте
  • Определите, сделал ли предлагающий предложения для текущего bean-компонента (улучшение введения будет использовать сопоставление классов, усовершенствование pointcut будет использовать для сопоставления метод pointcut MethodMatcher), и создайте прокси после внесения предложения.
2.2.4 Создание агента

С советниками, с целевыми объектами логично создать прокси. Но создать задействованные функциональные компоненты не так просто.

(1) JdkDynamicAopProxy и CglibAopProxy

  • JdkDynamicAopProxy: это инкапсуляция динамически созданного прокси-сервера JDK, сам JdkDynamicAopProxy являетсяInvocationHandler, и предоставитьgetProxyметод, через отражениеProxyСоздайте прокси.
  • CglibAopProxy: инкапсуляция создания динамического прокси CGLB.getProxyот Энхансер+DynamicAdvisedInterceptorСоздать прокси-объект

Оба улучшения будут иметьAdvisedSupportСвойство содержит внушающего.

private final AdvisedSupport advised;

Эти две инкапсуляции низкого уровня, Spring создает прокси и не вызывает их напрямую, а инкапсулирует три компонента более высокого уровня.

(2) AspectJProxyFactory и ProxyFactoryBean и ProxyFactory

Эти три компонента являютсяAdvisedSupportподкласс .

Эти три компонента можно использовать для создания прокси-объектов, между ними нет существенной разницы, все ониProxyCreatorSupportподкласс . Логический вызов для создания прокси-объекта такжеProxyCreatorSupportсередина.

будет多个建议者РассказыватьProxyCreatorSupport, ProxyCreatorSupport вызовет фабрику политикAopProxyFactoryвыберитеJdkDynamicAopProxyилиCglibAopProxyСоздайте прокси-объект.

protected Object createProxy(
            Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    【1】ProxyFactory组件
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);

      【2】设置建议者
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
      【3】创建代理对象
        return proxyFactory.getProxy(getProxyClassLoader());
    }

На данный момент определенный нами заявитель официально подключен к JDK или CGLB..

Видно, что простой JDK, прокси CGLB. Какой сложный дизайн сделал Spring, чтобы включить эту функциональность.

2.2.5. Исполнение агента

Возьмите в качестве примера динамический прокси JDK, выполните метод вызова

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

[1]
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
[2]
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
[3]
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
  • (1) ОтAdvisedSupportЦепочка предложений, этот процесс проходитAdvisorChainFactoryКогда компонент завершен, предлагающий, который применяется к текущему целевому объекту, удаляется, и создается цепочка предложений по выполнению.
  • (2) Создайте исполнителя MethodInvocation в соответствии с цепочкой рекомендаций и другой информацией.
  • (3) Выполнить, чтобы получить результат

Прокси-класс фактически реализованAdviedИнтерфейс, а класс, сгенерированный памятью, не отображает интерфейс Advied, но его можно просмотреть через экземпляр представления.

3. Резюме

Причина, по которой АОП усложняется, заключается в том, что Spring делает больше вещей над надстройкой. Суть его в динамическом прокси JDK (Proxy+InvocationHandler) и CGLB (Enhancer + MethodInterceptor(CallBack))

Выясните роль компонентов Spring AOP, и связь указывает на базовую основу. Понимание АОП не сложно.

Десять тысячеэтажных многоэтажек поднимаются из-под земли, обхватывают корнями и стволами, и можно подсмотреть их тайны

И наоборот, когда мы проектируем архитектуру, мы должны перечислить наше ядро ​​и выполнить проектирование верхнего уровня вокруг ядра.


первая кисть