Каждая мелочь, которую он делает, похожа на спасительную соломинку, и когда я смотрю на это однажды, эй, моя дорогая, он уже держит высокое дерево, которое я могу смотреть на
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));
}
}
Логика поиска: найти все реализацииAdvisor
Beandefiniton; кэшировать их 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;
}
}
}
Анализ процесса сборки:
- Найти все BeanDefinitions контейнера
- Обход этих BeanDefinitions
- определить, является ли он
isAspect
класс: этот шаг(AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
Проверьте, если этоAspect
вызывать - впредь
Aspect
Извлеките все предложения из класса и зарегистрируйте их как bean-компоненты. - Стандартный кеш (
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;
}
обработать:
- стать первым
Aspect
В аннотированном классе все методы, которые могут быть подсказками:getAdvisorMethods
получено неPointcut
Аннотированные методы, это могут быть методы-подсказки (включая родительские классы, методы интерфейса) - Переберите эти методы:
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
внушающий
- получать
@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
отметка
Мы уже знаем процесс получения этих двух видов рекомендаций. Теперь, когда у вас есть подсказчик, следующим шагом будет создание прокси для целевого метода, который будет принимать эти предложения. Здесь есть две проблемы:
- Когда создается прокси?
- Как создать прокси?
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, и связь указывает на базовую основу. Понимание АОП не сложно.
Десять тысячеэтажных многоэтажек поднимаются из-под земли, обхватывают корнями и стволами, и можно подсмотреть их тайны
И наоборот, когда мы проектируем архитектуру, мы должны перечислить наше ядро и выполнить проектирование верхнего уровня вокруг ядра.
первая кисть
- Статический прокси и динамический прокси JDK и динамический прокси CGLIB
- Серия исходных кодов Spring 8: Создание прокси для анализа исходного кода АОП
- Spring Source Series 9: создание прокси-сервера транзакции
- Серия исходных кодов Spring 10: выполнение прокси-объектов AOP
- Spring source series 11: Выполнение прокси-объектов транзакций