Углубленный анализ исходного кода Spring AOP

Spring

Поскольку SpringAOP будет использовать springIOC для управления bean-компонентами, студенты, которые мало что знают о springIOC, могут обратиться к моей предыдущей статье.Углубленный анализ исходного кода springIOC.

Версия исходного кода, используемая в этой статье, — 5.2.x. Чтобы лучше понять springAOP, мы используем метод xml.В реальной разработке используется большая часть аннотаций.Опыт подсказывает мне, что с точки зрения понимания исходного кода конфигурация xml является лучшей.

Чтение предлагает: потяните исходный код весеннего официального сайта и контролировать мой анализ, это самый высокий, а не лошадь.

Эта статья разделена на две части.Первая часть представляет некоторые предварительные знания о Spring AOP.Цель этой части состоит в том, чтобы позволить читателям, которые не знакомы с Spring AOP, понять некоторые базовые знания Spring AOP. В центре внимания этой статьи находится вторая часть, в которой будет проведен углубленный анализ исходного кода SpringAOP.

Введение в связанные термины Spring AOP

  • Точка присоединения

    Так называемые точки соединения — это точки, которые перехватываются. В Spring эти точки относятся к методам, потому что Spring поддерживает только точки соединения типа method

    Соответствующий класс:

    public interface Joinpoint {
    
      // 执行拦截器链中的下一个拦截器逻辑
    	Object proceed() throws Throwable;
    
    	Object getThis();
    
    	AccessibleObject getStaticPart();
    
    }
    
  • Точечная резка

    Так называемая точка входа относится к определению того, какие точки соединения мы хотим перехватить.

    Соответствующий класс:

    public interface Pointcut {
    
        //返回一个类型过滤器
        ClassFilter getClassFilter();
    
        // 返回一个方法匹配器
        MethodMatcher getMethodMatcher();
    
        Pointcut TRUE = TruePointcut.INSTANCE;
    }
    

    Соответствующие интерфейсы ClassFilter и MethodMatcher:

    public interface ClassFilter {
        boolean matches(Class<?> clazz);
        ClassFilter TRUE = TrueClassFilter.INSTANCE;
    
    }
    
    public interface MethodMatcher {
        boolean matches(Method method, Class<?> targetClass);
        boolean matches(Method method, Class<?> targetClass, Object... args);
        boolean isRuntime();
        MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
    }
    

    Когда мы пишем код АОП, мы обычно выбираем точку соединения с выражением pointcut.Выражение pointcut обрабатывает класс: AspectJExpressionPointcut.Схема отношений наследования этого класса выглядит следующим образом:

Вы можете видеть, что AspectJExpressionPointcut наследует интерфейсы ClassFilter и MethodMatcher и имеет метод match, поэтому он может выбирать точки подключения.

  • Совет (уведомление/улучшение)

    Так называемое уведомление означает, что после перехвата точки соединения необходимо сделать уведомление.Уведомление делится на предварительное уведомление (AspectJMethodBeforeAdvice), пост-уведомление (AspectJAfterReturningAdvice), уведомление об исключении (AspectJAfterThrowingAdvice), окончательное уведомление (AspectAfterAdvice), объемное уведомление (AspectJAroundAdvice)

  • Введение

    Введение — это особый вид уведомлений. Без изменения кода класса Введение может динамически добавлять некоторые методы или поля в класс во время выполнения.

  • Цель

    Целевой объект прокси, например класс UserServiceImpl.

  • Ткачество

    Относится к процессу применения улучшений к целевым объектам для создания новых прокси-объектов. Spring реализует переплетение, реализуя интерфейс постпроцессора BeanPostProcessor. То есть после инициализации bean-компонента создается прокси-объект для целевого объекта и передается контейнеру springIOC для принятия на себя, так что целевой объект, полученный в контейнере, является расширенным прокси-объектом.

  • Прокси (агент)

    После того, как категория вписана в армированный AOP, чтобы получить результат прокси-класса

  • Советник (Уведомитель, Советник)

    Похоже на: Аспект

  • Аспект

    Это комбинация pointcut и уведомления, соответствующая PointcutAdvisor весной, которая может получать как pointcut, так и уведомление. Как показано в следующем классе:

    public interface Advisor {
    
        Advice getAdvice();
        boolean isPerInstance();
    }
    
    public interface PointcutAdvisor extends Advisor {
    
        Pointcut getPointcut();
    }
    

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

Поймите концепции AOP, Spring Aop, Aspectj

Что такое АОП

АОП — это аббревиатура Аспектно-ориентированного программирования, что означает: ориентированный наразделпрограммирование. Подробности см. в соответствующих определениях.

Spring AOP

  • На основе реализации динамического прокси, если используется интерфейс, он реализуется с помощью динамического прокси JDK; если интерфейс не реализован, для его реализации используется CGLIB.
  • Динамические прокси, с другой стороны, разработаны на основе отражения.
  • Spring обеспечивает поддержку AspectJ и реализует набор чистых Spring AOP.

На основе springAOP при разборе исходного кода.

Принцип показан на следующем рисунке:

Может быть, это все еще немного абстрактно. Давайте напишем пример, чтобы помочь понять:

СоздаватьPersonинтерфейс:

public interface Person {

    public void eat();
}

Создайте класс реализации интерфейса:PersonImpl

public class PersonImpl implements Person {

    @Override
    public void eat() {
        System.out.println("我要吃饭");
    }
}

Когда я был ребенком, мама говорила нам мыть руки перед едой, как это реализовать с помощью кода?

Определите EatHandler, достигните InvocationHandler

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

public class EatHandler implements InvocationHandler {

    // 要代理的目标对象
    private Object target;

    public EatHandler(Object target) {
        this.target = target;
    }
    public Object proxyInstance() {
        // 第一个参数就是要加载代理对象的类加载器,
        // 第二个参数就是要代理类实现的接口
        // 第三个参数就是EatHandler
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @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;
    }
}

Приходите на пробный урок:

public class ProxyMain {
    public static void main(String[] args) {
        EatHandler eatHandler = new EatHandler(new PersonImpl());
        Person person = (Person) eatHandler.proxyInstance();
        person.eat();
    }
}

вывод:

吃饭前要洗手
我要吃饭
吃饭后要洗碗

Видно, что до и после "Хочу есть" мы вставили функции "мыть руки перед едой" и "мыть посуду после еды"

Как вставляется?

Принцип заключается в том, что JDK помогает нам создать прокси-класс для целевого объекта (EatHandler). Этот прокси-класс вставляет нужные нам функции. Когда мы вызываем person.eat();, прокси-класс работает вместо исходного объекта, поэтому как добиться, чтобы вставить потребность в функциональности, которую мы хотим. Это должно быть понятно. Вставьте сгенерированный прокси-объект ниже:

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

// 
// Decompiled by Procyon v0.5.36
// 

public final class PersonImpl$Proxy1 extends Proxy implements Person
{
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    
    public PersonImpl$Proxy1(final InvocationHandler h) {
        super(h);
    }
   
    public final boolean equals(final Object o) {
        try {
            return (boolean)super.h.invoke(this, PersonImpl$Proxy1.m1, new Object[] { o });
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw new UndeclaredThrowableException(undeclaredThrowable);
        }
    }
    
    public final void eat() {
        try {
            super.h.invoke(this, PersonImpl$Proxy1.m3, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw new UndeclaredThrowableException(undeclaredThrowable);
        }
    }
    
    public final String toString() {
        try {
            return (String)super.h.invoke(this, PersonImpl$Proxy1.m2, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw new UndeclaredThrowableException(undeclaredThrowable);
        }
    }
    
    public final int hashCode() {
        try {
            return (int)super.h.invoke(this, PersonImpl$Proxy1.m0, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw new UndeclaredThrowableException(undeclaredThrowable);
        }
    }
    
    static {
        try {
            PersonImpl$Proxy1.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
          // 利用反射获取目标对象的Method方法
            PersonImpl$Proxy1.m3 = Class.forName("Person").getMethod("eat", (Class<?>[])new Class[0]);
            PersonImpl$Proxy1.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
            PersonImpl$Proxy1.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
        }
        catch (NoSuchMethodException ex) {
            throw new NoSuchMethodError(ex.getMessage());
        }
        catch (ClassNotFoundException ex2) {
            throw new NoClassDefFoundError(ex2.getMessage());
        }
    }
}

Мы видим, что JDK действительно генерирует для нас прокси-объект, который наследует объект Proxy и реализует интерфейс Person. Мы видим, что equals(), toString(), hashCode() и наш метод eat() генерируются в прокси-объекте. Посмотрите, есть ли такой код в методе eat super.h.invoke(this, PersonImpl$Proxy1.m3, null); среди них h — это EatHandle, который мы определили. Мы видим, что когда мы вызываем person.eat(), мы фактически вызываем метод eat() прокси-объекта. Сначала мы вызываем метод вызова в EatHandle. Вот добавленный нами расширенный код, который реализует роль прокси-объекта. . . . Вы должны понимать это.

AspectJ

  • AspectJЭто среда АОП, реализованная Java, которая может выполнять компиляцию АОП для кода Java (обычно во время компиляции), так что код Java имеет функцию АОП AspectJ (конечно, требуется специальный компилятор)
  • Можно сказать, что Aspectj в настоящее время является самым зрелым и самым богатым языком в рамках AOP. Более удачно, Aspectj полностью совместима с программой Java, которая почти не легко связана, поэтому она очень проста для инженеров с базами программирования Java.
  • ApectJ использует метод статического плетения. ApectJ в основном используетвремя компиляцииСплетение, в течение этого периода, используйте компилятор AspectJ acj (аналогичный javac) для компиляции класса аспекта в байт-код класса, а затем сплетение, когда компилируется целевой класс java, то есть сначала скомпилируйте класс аспекта, а затем скомпилируйте целевой класс .

Как показано ниже:

Анализ исходного кода

С предвосхищением предыдущих базовых знаний мы можем, наконец, перейти к этапу анализа исходного кода. Давайте проверим наши идеи. Читателям предлагается открыть исходный код Spring.

Этот этап делится на три шага:

  • Анализ процесса парсинга конфигурационных файлов АОП
  • Создание и анализ прокси-объектов
  • Анализ процесса выполнения прокси-объекта

Анализ процесса парсинга конфигурационных файлов АОП

Нам необходимо завершить парсинг следующих конфигурационных файлов:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- 配置目标对象 -->
	<bean class="com.sjc.spring.aop.target.UserServiceImpl"></bean>
	<!-- 配置通知类 -->
	<bean id="myAdvice" class="com.sjc.spring.aop.advice.MyAdvice"></bean>
	<!-- AOP配置 -->
	<aop:config>
	
		<!-- <aop:advisor advice-ref="" /> -->
		<aop:pointcut expression="" id="" />
		<aop:aspect ref="myAdvice">
			<aop:before method="before"
				pointcut="execution(* *..*.*ServiceImpl.*(..))" />
			<aop:after method="after"
				pointcut="execution(* *..*.*ServiceImpl.*(..))" />
		</aop:aspect>
	</aop:config>

</beans>

Вход: DefaultBeanDefinitionDocumentReader # parseBeanDefinitions

Мы также анализировали этот шаг при разборе исходного кода SpringIOC в предыдущей статье, давайте перейдем к

delegate.parseCustomElement(ele);

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// 加载的Document对象是否使用了Spring默认的XML命名空间(beans命名空间)
		if (delegate.isDefaultNamespace(root)) {
			// 获取Document对象根元素的所有子节点(bean标签、import标签、alias标签和其他自定义标签context、aop等)
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					// bean标签、import标签、alias标签,则使用默认解析规则
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else { //像context标签、aop标签、tx标签,则使用用户自定义的解析规则解析元素节点
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

Вводим: BeanDefinitionParserDelegate#parseCustomElement

Этот метод в основном делает две вещи:

  • Получите URI пространства имен (то есть получите значение атрибута xmlns:aop или xmlns:context тега bean-компонентов)
  • Получите соответствующий обработчик класса обработки в соответствии с namespaceUri.
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		// 获取命名空间URI(就是获取beans标签的xmlns:aop或者xmlns:context属性的值)
		// http://www.springframework.org/schema/aop
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		// 根据不同的命名空间URI,去匹配不同的NamespaceHandler(一个命名空间对应一个NamespaceHandler)
		// 此处会调用DefaultNamespaceHandlerResolver类的resolve方法
		// 两步操作:查找NamespaceHandler 、调用NamespaceHandler的init方法进行初始化(针对不同自定义标签注册相应的BeanDefinitionParser)
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

Заходим в NamespaceHandlerSupport#parse

Этот метод, который нас в основном интересует, — это метод parser.parse(), findParserForElement можно использовать как ветвь для понимания, если читателю интересно.

public BeanDefinition parse(Element element, ParserContext parserContext) {
		// NamespaceHandler里面初始化了大量的BeanDefinitionParser来分别处理不同的自定义标签
		// 从指定的NamespaceHandler中,匹配到指定的BeanDefinitionParser
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		// 调用指定自定义标签的解析器,完成具体解析工作
		return (parser != null ? parser.parse(element, parserContext) : null);
	}

BeanDefinitionParser осознает, что есть много класс, где мы профилируем на ConfigBeanDefinitionParser для достижения.

Мы переходим к ConfigBeanDefinitionParser#parse();

Этот метод в основном производит три ветви, которые нас больше беспокоят:

  • configureAutoProxyCreator(parserContext, element);

    Главное — сгенерировать BeanDefinition класса AspectJAwareAdvisorAutoProxyCreator и зарегистрировать его в IOC-контейнере, этот класс используется для создания прокси-объектов АОП.

  • parsePointcut(elt, parserContext);

    Создайте объект BeanDefinition AspectJExpressionPointcut и зарегистрируйте его. Этот AspectJExpressionPointcut используется для разбора нашего выражения pointcut Читатели могут увидеть диаграмму классов AspectJExpressionPointcut, которую мы представили в начале.

  • parseAspect(elt, parserContext);

    Этот шаг в основном предназначен для разбораaop:aspectИнкапсуляция тегов.Смотрим на основную ветку.

public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		// 向IoC容器中注册 AspectJAwareAdvisorAutoProxyCreator 类的BeanDefinition:(用于创建AOP代理对象的)
		// BeanPostProcessor可以对实例化之后的bean进行一些操作
		// AspectJAwareAdvisorAutoProxyCreator 实现了BeanPostProcessor接口,可以对目标对象实例化之后,创建对应的代理对象
		configureAutoProxyCreator(parserContext, element);

		// 获取<aop:config>标签的子标签<aop:aspect>、<aop:advisor> 、<aop:pointcut>
		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			// 获取子标签的节点名称或者叫元素名称
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
				// 解析<aop:pointcut>标签
				// 产生一个AspectJExpressionPointcut的BeanDefinition对象,并注册
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
				// 解析<aop:advisor>标签
				// 产生一个DefaultBeanFactoryPointcutAdvisor的BeanDefinition对象,并注册
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
				// 解析<aop:aspect>标签
				// 产生了很多BeanDefinition对象
				// aop:after等标签对应5个BeanDefinition对象
				// aop:after标签的method属性对应1个BeanDefinition对象
				// 最终的AspectJPointcutAdvisor BeanDefinition类
				parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}

Мы переходим к ConfigBeanDefinitionParser#parseAspect

Этот метод начинает синтаксический анализaop:aspectЭтикетка,

В основном нас интересует метод parseAdvice.

private void parseAspect(Element aspectElement, ParserContext parserContext) {
		// 获取<aop:aspect>标签的id属性值
		String aspectId = aspectElement.getAttribute(ID);
		// 获取<aop:aspect>标签的ref属性值,也就是增强类的引用名称
		String aspectName = aspectElement.getAttribute(REF);

		try {
			this.parseState.push(new AspectEntry(aspectId, aspectName));
			List<BeanDefinition> beanDefinitions = new ArrayList<>();
			List<BeanReference> beanReferences = new ArrayList<>();
			// 处理<aop:aspect>标签的<aop:declare-parents>子标签
			List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
			for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
				Element declareParentsElement = declareParents.get(i);
				beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
			}

			// We have to parse "advice" and all the advice kinds in one loop, to get the
			// ordering semantics right.
			// 获取<aop:aspect>标签的所有子标签
			NodeList nodeList = aspectElement.getChildNodes();
			boolean adviceFoundAlready = false;
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				// 判断是否是<aop:before>、<aop:after>、<aop:after-returning>、<aop:after-throwing method="">、<aop:around method="">这五个标签
				if (isAdviceNode(node, parserContext)) {
					if (!adviceFoundAlready) {
						adviceFoundAlready = true;
						if (!StringUtils.hasText(aspectName)) {
							parserContext.getReaderContext().error(
									"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
									aspectElement, this.parseState.snapshot());
							return;
						}
						beanReferences.add(new RuntimeBeanReference(aspectName));
					}
					// 解析<aop:before>等五个子标签
					// 方法主要做了三件事:
					//		1、根据织入方式(before、after这些)创建RootBeanDefinition,名为adviceDef即advice定义
					//		2、将上一步创建的RootBeanDefinition写入一个新的RootBeanDefinition,构造一个新的对象,名为advisorDefinition,即advisor定义
					//		3、将advisorDefinition注册到DefaultListableBeanFactory中
					AbstractBeanDefinition advisorDefinition = parseAdvice(
							aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
					beanDefinitions.add(advisorDefinition);
				}
			}

			AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
					aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
			parserContext.pushContainingComponent(aspectComponentDefinition);

			List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
			for (Element pointcutElement : pointcuts) {
				parsePointcut(pointcutElement, parserContext);
			}

			parserContext.popAndRegisterContainingComponent();
		}
		finally {
			this.parseState.pop();
		}
	}

Мы переходим к ConfigBeanDefinitionParser#parseAdvice

Этот метод в основном делает следующее: разборaop:beforeдождитесь пяти вкладок

1. Создадим RootBeanDefinition по методу плетения (до, после), с именем советаDef, то есть определение совета 2. Запишите RootBeanDefinition, созданный на предыдущем шаге, в новый RootBeanDefinition и создайте новый объект с именем AdvisorDefinition, который является определением советника. 3. Зарегистрируйте AdvisorDefinition в DefaultListableBeanFactory.

private AbstractBeanDefinition parseAdvice(
			String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
			List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

		try {
			this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));

			// create the method factory bean
			// 创建方法工厂Bean的BeanDefinition对象:用于获取Advice增强类的Method对象, <aop:brefore method="before">中的method
			RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
			// 设置MethodLocatingFactoryBean的targetBeanName为advice类的引用名称,也就是<aop:aspect ref="myAdvice">中的myAdvice
			methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
			// 设置MethodLocatingFactoryBean的methodName为<aop:after>标签的method属性值(也就是method="before"中的before,作为advice方法名称)
			methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
			methodDefinition.setSynthetic(true);

			// create instance factory definition
			// 创建实例工厂BeanDefinition:用于创建增强类的实例,也就是<aop:aspect ref="myAdvice">中的myAdvice
			RootBeanDefinition aspectFactoryDef =
					new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
			// 设置SimpleBeanFactoryAwareAspectInstanceFactory的aspectBeanName为advice类的引用名称
			aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
			aspectFactoryDef.setSynthetic(true);

			//以上的两个BeanDefinition的作用主要是通过反射调用Advice对象的指定方法
			// method.invoke(obj,args)

			// register the pointcut
			// 通知增强类的BeanDefinition对象(核心),也就是一个<aop:before>等对应一个adviceDef
			AbstractBeanDefinition adviceDef = createAdviceDefinition(
					adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
					beanDefinitions, beanReferences);

			// configure the advisor
			// 通知器类的BeanDefinition对象, 对应<aop:aspect>
			RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
			advisorDefinition.setSource(parserContext.extractSource(adviceElement));
			// 给通知器类设置Advice对象属性值
			advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
			if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
				advisorDefinition.getPropertyValues().add(
						ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
			}

			// register the final advisor
			// 将advisorDefinition注册到IoC容器中
			parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

			return advisorDefinition;
		}
		finally {
			this.parseState.pop();
		}
	}

На этом анализ этикетки завершен.

Создание и анализ прокси-объектов

Прежде чем анализировать создание прокси-объектов АОП, давайте посмотрим на схему структуры наследования класса:

Давайте посмотрим на класс AbstractAutoProxyCreator, который имеет следующие методы:

postProcessBeforeInitialization

 postProcessAfterInitialization----AOP功能入口

 postProcessBeforeInstantiation 

postProcessAfterInstantiation 

postProcessPropertyValues

Находим запись анализа: AbstractAutoProxyCreator#postProcessAfterInitialization

@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				// 使用动态代理技术,产生代理对象
				// bean : 目标对象
				// beanName :目标对象名称
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

Введите AbstractAutoProxyCreator#wrapIfNecessary

Этот метод выполняет три основные функции:

  1. Найдите набор объектов советника, связанных с прокси-классом, и найдите его из IOC, потому что на предыдущем шаге мы разобрали файл конфигурации, инкапсулировали соответствующую информацию в несколько объектов и сохранили их в IOC-контейнере.
  2. Создание прокси-объектов через динамический прокси-сервер jdk или динамический прокси-сервер cglib
  3. Поместить тип прокси в кеш
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// Advice/Pointcut/Advisor/AopInfrastructureBean接口的beanClass不进行代理以及对beanName为aop内的切面名也不进行代理
		// 此处可查看子类复写的shouldSkip()方法
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		// 查找对代理类相关的advisor对象集合,此处就与point-cut表达式有关了
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		// 对相应的advisor不为空才采取代理
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 通过jdk动态代理或者cglib动态代理,产生代理对象
			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;
	}

Затем мы вводим AbstractAutoProxyCreator # createProxy

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		// 创建代理工厂对象
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		//如果没有使用CGLib代理
		if (!proxyFactory.isProxyTargetClass()) {
			// 是否可能使用CGLib代理
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				// 查看beanClass对应的类是否含有InitializingBean.class/DisposableBean.class/Aware.class接口
				// 无则采用JDK动态代理,有则采用CGLib动态代理
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		// 获得所有关联的Advisor集合(该分支待补充)
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		// 此处的targetSource一般为SingletonTargetSource
		proxyFactory.setTargetSource(targetSource);
		// 空的实现
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		// 是否设置预过滤模式,此处针对本文为true
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}
		// 获取使用JDK动态代理或者cglib动态代理产生的对象
		return proxyFactory.getProxy(getProxyClassLoader());
	}

Мы вошли в ProxyFactory # getProxy

public Object getProxy(@Nullable ClassLoader classLoader) {
		// 1、创建JDK方式的AOP代理或者CGLib方式的AOP代理
		// 2、调用具体的AopProxy来创建Proxy代理对象
		return createAopProxy().getProxy(classLoader);
	}

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

Вводим: JdkDynamicAopProxy#getProxy

Этот метод в основном делает две вещи:

  1. Получить все прокси-интерфейсы
  2. Вызвать динамический прокси JDK
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		// 获取完整的代理接口
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		// 调用JDK动态代理方法
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

На этом анализ сгенерированного объекта прокси-класса завершен.

Анализ процесса выполнения прокси-объекта

Давайте посмотрим на JdkDynamicAopProxy, который реализует InvocationHandler, поэтому мы нашли запись для анализа процесса выполнения прокси-объекта.

Запись: JdkDynamicAopProxy#invoke

Этот метод в основном делает следующее:

  1. Получить всех советников для этого целевого объекта

    Эти советники заказаны, они будут прикованы по порядку

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

  3. Создание экземпляра ReflectiveMethodInvocation вызывает цепочку вызовов для запуска процесса перехвата AOP.

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			
      //...省略若干代码

			Object retVal;

			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			// 获取目标对象
			target = targetSource.getTarget();
			// 获取目标对象的类型
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// Get the interception chain for this method.
			// 获取针对该目标对象的所有增强器(advisor), 这些advisor都是有顺序的,他们会按照顺序进行链式调用
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			// 检查是否我们有一些通知。如果我们没有,我们可以直接对目标类进行反射调用,避免创建MethodInvocation类

			// 调用目标类的方法
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				// 通过反射调用目标对象的方法
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				//我们需要创建一个方法调用
				// proxy:生成的动态代理对象
				// target:目标方法
				// args: 目标方法参数
				// targetClass:目标类对象
				// chain: AOP拦截器执行链,是一个MethodInterceptor的集合
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				// 通过拦截器链进入连接点
				// 开始执行AOP的拦截过程
				retVal = invocation.proceed();
			}

			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}

Рассмотрим процесс получения цепочки вызовов:

Перейти к: AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
		// 创建以方法为单位的缓存key
		MethodCacheKey cacheKey = new MethodCacheKey(method);
		// 从缓存中获取指定方法的advisor集合
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
			// 获取目标类中指定方法的MethodInterceptor集合,该集合是由Advisor转换而来
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}

Вводим, чтобы получить коллекцию MethodInterceptor:

DefaultAdviceChainFactory#getInterceptorsAndDynamicInterceptionAdvice

В основном здесь:

  1. Создайте экземпляр DEFAULTADVISORADAPTERREGISTRY и создайте адаптер MethodbeforeadViceAdapter, AfterReturningAdviceAdapter, ThrowsAdviceAdapter.

    Почему вам нужен адаптер здесь? Давайте посмотрим на следующую диаграмму классов:

Некоторые Advice вообще не имеют отношения к перехватчику MethodInterceptor, и адаптер обязан иметь связь между ними. Здесь используется режим адаптера.Например,мы купили много моделей компьютеров,и используемые напряжения разные.Напряжение в нашем доме 220v.Как мы можем заставить наш компьютер заряжаться?Нано нужен блок питания.адаптер. Вы должны понимать это.

2. Измените все наборы советников, используйте ClassFilter().matches() класса Pointcut для сопоставления и используйте Pointcut().getMethodMatcher() для сопоставления, если оно совпадает, преобразуйте советник в MethodInterceptor,

3. Если вам нужно динамическое сопоставление по параметрам (например, по перегрузке), добавьте InterceptorAndDynamicMethodMatcher в цепочку перехватчиков

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		// advice适配器注册中心
		// MethodBeforeAdviceAdapter:将Advisor适配成MethodBeforeAdvice
		// AfterReturningAdviceAdapter:将Advisor适配成AfterReturningAdvice
		// ThrowsAdviceAdapter: 将Advisor适配成ThrowsAdvice
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		// 返回值集合,里面装的都是Interceptor或者它的子类接口MethodInterceptor
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		// 获取目标类的类型
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		// 是否有引介
		Boolean hasIntroductions = null;

		// 去产生代理对象的过程中,针对该目标方法获取到的所有合适的Advisor集合
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				// 如果该Advisor可以对目标类进行增强,则进行后续操作
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					// 获取方法适配器,该方法匹配器可以根据指定的切入点表达式进行方法匹配
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						// 将advisor转成MethodInterceptor
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						// MethodMatcher接口通过重载定义了两个matches()方法
						// 两个参数的matches() 被称为静态匹配,在匹配条件不是太严格时使用,可以满足大部分场景的使用
						// 称之为静态的主要是区分为三个参数的matches()方法需要在运行时动态的对参数的类型进行匹配
						// 两个方法的分界线就是boolean isRuntime()方法
						// 进行匹配时先用两个参数的matches()方法进行匹配,若匹配成功,则检查boolean isRuntime()的返回值若为
						// true, 则调用三个参数的matches()方法进行匹配(若两个参数的都匹配不中,三个参数的必定匹配不中)

						// 需要根据参数动态匹配(比如重载)
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}

		return interceptorList;
	}

Возвращаемся к JdkDynamicAopProxy#invoke

Введите ReflectiveMethodInvocation#proceed():

Это процесс выполнения цепочки вызова

  1. Если выполнение достигает конца цепочки, точка соединения вызывается напрямую, то есть целевой метод вызывается напрямую.

  2. Наконец, будет вызван метод вызова MethodInterceptor, а файл конфигурации этой статьи вызовет AspectJAfterAdvice, MethodBeforeAdviceInterceptor.

    Посмотрим на AspectJAfterAdvice: вызов метода внутри такой, а цепочка вызовов продолжает вызываться

    public Object invoke(MethodInvocation mi) throws Throwable {
    		try {
    			return mi.proceed();
    		}
    		finally {
    			invokeAdviceMethod(getJoinPointMatch(), null, null);
    		}
    	}
    

Давайте посмотрим: MethodBeforeAdviceInterceptor (перехватчик перед советом)

public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}

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

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

、 Порядок не влияет на окончательный порядок выполнения, и порядок гарантируется цепочкой вызовов.

public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		// 如果执行到链条的末尾, 则直接调用连接点方法 即直接调用目标方法
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		// 获取集合中的MethodInterceptor
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		// 如果是InterceptorAndDynamicMethodMatcher类型(动态匹配)
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			// 这里每一次都去匹配是否适用于这个目标方法
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				// 如果匹配则直接调用MethodInterceptor的invoke方法
				// 注意这里传入的参数是this,我们下面看一下ReflectiveMethodInvocation的类型
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				// 如果不适用于此目标方法,则继续执行下一链条
				// 递归调用
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			// 说明是适用于此目标方法的,直接调用MethodInterceptor的invoke方法
			// 传入this即ReflectiveMethodInvocation实例
			// 传入this进入 这样就可以形成一个调用的链条了
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

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