Regain-Spring-AOP

Java

Терминология АОП

Описание концепции и связанных терминов АОП см.Полностью покорите теорию Spring АОПРезюме очень хорошее, эта статья будет посвящена анализу процесса реализации АОП.

Пример использования

определить интерфейс

public interface UserService {
    void say ();
}

Класс реализации интерфейса выглядит следующим образом:

public class UserServiceImpl implements UserService {
    public void say() {
        System.out.println("do say method");
    }
}

определить уведомление

public class UserAdvice implements MethodBeforeAdvice {

    public void before(Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do before advice ....");
    }
}

Настроить АОП

<beans>
	<!-- 配置接口实现类 -->
    <bean id="userService" class="org.springframework.aop.UserServiceImpl" />

    <!-- 配置通知类 -->
    <bean id="userAdvice" class="org.springframework.aop.UserAdvice" />

    <!--代理类-->
    <bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--要代理的接口 创建代理对象时需要-->
        <!-- 配置该属性会采用 jdk 动态代理,反之采用 cglib -->
        <property name="proxyInterfaces">
            <value>org.springframework.aop.UserService</value>
        </property>
        <!--拦截器名字,也就是我们定义的通知类,可配置多个通知类 -->
        <property name="interceptorNames">
            <list>
                <value>userAdvice</value>
            </list>
        </property>
        <!--目标类,就是我们业务的实现类-->
        <property name="target">
            <ref bean="userService"/>
        </property>
    </bean>
</beans>

тестовое задание

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/org/springframework/aop/aop.xml");

UserService userService = (UserService) ctx.getBean("userProxy");

userService.say();

Результат выполнения следующий:

do before advice ....
do say method

Судя по результатам выполнения, предварительное уведомление улучшило метод интерфейса. Давайте взглянем на конкретную реализацию Spring AOP.

Анализ реализации

Как видно из приведенного выше примера, конфигурация Spring AOP в основном основана на классах.ProxyFactoryBean, то мы используем это как запись для анализа его реализации.

Структура класса ProxyFactoryBean

ProxyFactoryBean

Создайте цепочку фасетов

отProxyFactoryBeanструктура класса, мы обнаруживаем, что он реализует интерфейсBeanFactoryAware, что означает, что метод будет вызываться во время его созданияsetBeanFactory; Исходный код выглядит следующим образом:

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
	// 设置 beanFactory
	this.beanFactory = beanFactory;
	logger.debug("Set BeanFactory. Will configure interceptor beans...");
	// 创建 advisor chain
	createAdvisorChain();
	logger.info("ProxyFactoryBean config: " + this);
	if (singleton) {
		// Eagerly initialize the shared singleton instance
		getSingletonInstance();
		// We must listen to superclass advice change events to recache singleton
		// instance if necessary
		addListener(this);
	}
}

существуетsetBeanFactoryПомимо установки методаbeanFactory, и еще одно важное действиеcreateAdvisorChainСоздайте цепочку советников (которую также можно понимать как цепочку фасетов). Итак, давайте рассмотрим, как создать цепочку советников.

private void createAdvisorChain() throws AopConfigException, BeansException {
	// 检测是否配置了 interceptorNames, 也就是是否配置相关 advice 通知; 若没有配置直接返回
	if (this.interceptorNames == null || this.interceptorNames.length == 0) {
		//throw new AopConfigException("Interceptor names are required");
		return;
	}
	
	// Globals can't be last
	if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX)) {
		throw new AopConfigException("Target required after globals");
	}

	// Materialize interceptor chain from bean names
	for (int i = 0; i < this.interceptorNames.length; i++) {
		String name = this.interceptorNames[i];
		logger.debug("Configuring interceptor '" + name + "'");
		// 判断 interceptor name 是否以 * 结尾
		if (name.endsWith(GLOBAL_SUFFIX)) {
			if (!(this.beanFactory instanceof ListableBeanFactory)) {
				throw new AopConfigException("Can only use global advisors or interceptors with a ListableBeanFactory");
			}
			else {
				addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
				                 name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
			}
		}
		else {
			// add a named interceptor
			// 获取 advice bean
			Object advice = this.beanFactory.getBean(this.interceptorNames[i]);
			// 将 advisor 加入到链表中
			addAdvisor(advice, this.interceptorNames[i]);
		}
	}
}
private void addAdvisor(Object next, String name) {
	logger.debug("Adding advisor or TargetSource [" + next + "] with name [" + name + "]");
	// We need to add a method pointcut so that our source reference matches
	// what we find from superclass interceptors.
	// 查找 advice 通知匹配的 pointcut, 并创建一个 advisor
	Object advisor = namedBeanToAdvisorOrTargetSource(next);
	if (advisor instanceof Advisor) {
		// if it wasn't just updating the TargetSource
		logger.debug("Adding advisor with name [" + name + "]");
		addAdvisor((Advisor) advisor);
		this.sourceMap.put(advisor, name);
	}
	else {
		logger.debug("Adding TargetSource [" + advisor + "] with name [" + name + "]");
		setTargetSource((TargetSource) advisor);
		// save target name
		this.targetName = name;
	}
}

отaddAdvisorВ методе видно, что перед добавлением советника нужно сначала создать советник, и метод будет вызыватьсяnamedBeanToAdvisorOrTargetSource

private Object namedBeanToAdvisorOrTargetSource(Object next) {
	try {
		// 将 advice 包装成一个 advisor
		Advisor adv = GlobalAdvisorAdapterRegistry.getInstance().wrap(next);
		return adv;
	}
	catch (UnknownAdviceTypeException ex) {
		
	}
}

namedBeanToAdvisorOrTargetSourceМетод вызовет одноэлементный режимGlobalAdvisorAdapterRegistryМетодыwrapоберните совет как советник; viewingwrapПеред реализациейGlobalAdvisorAdapterRegistryЧто это делает.

public class GlobalAdvisorAdapterRegistry extends DefaultAdvisorAdapterRegistry {
	
	private static GlobalAdvisorAdapterRegistry instance = new GlobalAdvisorAdapterRegistry();
	
	public static GlobalAdvisorAdapterRegistry getInstance() {
		return instance;
	}

	private GlobalAdvisorAdapterRegistry() {
	}	
}

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry {
	
	private List adapters = new LinkedList();
	
	public DefaultAdvisorAdapterRegistry() {
		// register well-known adapters
		registerAdvisorAdapter(new BeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}
}

сверхуGlobalAdvisorAdapterRegistryРеализация может видеть, что она использует шаблон singleton и наследует классDefaultAdvisorAdapterRegistryЕсть 3 встроенных адаптера рекомендаций для подбора рекомендаций во время строительства. Давайте посмотрим, как этоwrapОбертка для совета.

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
	if (adviceObject instanceof Advisor) {
		return (Advisor) adviceObject;
	}
	
	if (!(adviceObject instanceof Advice)) {
		throw new UnknownAdviceTypeException(adviceObject);
	}
	Advice advice = (Advice) adviceObject;
	
	if (advice instanceof Interceptor) {
		// So well-known it doesn't even need an adapter
		return new DefaultPointcutAdvisor(advice);
	}

	// 遍历内置的 advice adapters
	for (int i = 0; i < this.adapters.size(); i++) {
		// Check that it is supported
		AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
		// 判断当前 adapter 是否支付当前 advice
		if (adapter.supportsAdvice(advice)) {
			// 如果支持的话,返回一个 DefaultPointcutAdvisor
			return new DefaultPointcutAdvisor(advice);
		}
	}
	throw new UnknownAdviceTypeException(advice);
}

отwrapРеализация может обнаружить, что если совет соответствует адаптеру, адаптер будет создан.DefaultPointcutAdvisorэкземпляр и возврат;

public class DefaultPointcutAdvisor implements PointcutAdvisor, Ordered {

	private int order = Integer.MAX_VALUE;

	private Pointcut pointcut;
	
	private Advice advice;
	
	public DefaultPointcutAdvisor() {
	}
	
	public DefaultPointcutAdvisor(Advice advice) {
		this(Pointcut.TRUE, advice);
	}
	
	public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
		this.pointcut = pointcut;
		this.advice = advice;
	}
}

/**
 * Canonical instance that matches everything.
 * 默认匹配所有的类及类下的所有方法
 */
Pointcut TRUE = new Pointcut() {

	public ClassFilter getClassFilter() {
		return ClassFilter.TRUE;
	}

	public MethodMatcher getMethodMatcher() {
		return MethodMatcher.TRUE;
	}

	public String toString() {
		return "Pointcut.TRUE";
	}
};

отDefaultPointcutAdvisorНа примере видно, что процесс создания советника (аспекта) на самом деле является процессом привязки совета (advice) и поинтреза (pointcut); в то же время по умолчанию в Spring AOP поинткут перехватывает все методы под всеми классами.

Проще говоря, это означает, какие методы при каких классах будут перехватываться текущим аспектом, и какие улучшения будут использоваться в процессе перехвата (предварительное уведомление, уведомление о возврате, уведомление об исключении).

На этом процесс создания цепочки советников завершен, и процесс примерно такой:

  • Итерация по именам перехватчиков (также известный как совет совета)
  • получить совет боб
  • Определить, соответствует ли совет встроенному советникуAdapter, и если да, то создать DefaultPointcutAdvisor (по умолчанию перехватывать все методы всех классов) и добавить его в связанный список

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

отProxyFactoryBeanИмя класса и структура класса, найдите его интерфейс реализацииFactoryBean, то есть когдаgetBeanметод будет вызван, когдаgetObject, исходный код выглядит следующим образом:

public Object getObject() throws BeansException {
	// 默认单例
	return (this.singleton) ? getSingletonInstance() : newPrototypeInstance();
}

private Object getSingletonInstance() {
	if (this.singletonInstance == null) {
		// This object can configure the proxy directly if it's
		// being used as a singleton.
		this.singletonInstance = createAopProxy().getProxy();
	}
	return this.singletonInstance;
}

protected synchronized AopProxy createAopProxy() {
	if (!isActive) {
		activate();
	}
	
	return getAopProxyFactory().createAopProxy(this);
}
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
	// 是否采用 cglib 代理
	boolean useCglib = advisedSupport.getOptimize() || advisedSupport.getProxyTargetClass() || advisedSupport.getProxiedInterfaces().length == 0;
	if (useCglib) {
		return CglibProxyFactory.createCglibProxy(advisedSupport);
	}
	else {
		// Depends on whether we have expose proxy or frozen or static ts
		return new JdkDynamicAopProxy(advisedSupport);
	}
}
public Object getProxy(ClassLoader cl) {
	logger.debug("Creating JDK dynamic proxy");
	Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
	return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
}

ProxyFactoryBeanОценив конфигурацию proxyTargetClass и interfaceNames, выберите использование cglib или jdk для создания целевого прокси-объекта.

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

Вышеприведенное кратко описывает создание прокси-объекта, затем давайте посмотрим, как прокси выполняется, когда мы вызываем целевой метод.В качестве примера возьмем динамический прокси jdk:

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

	TargetSource targetSource = advised.targetSource;
	Class targetClass = null;
	Object target = null;		
	
	try {
		// Try special rules for equals() method and implementation of the
		// Advised AOP configuration interface
		
		// Short-circuit expensive Method.equals() call, as Object.equals() isn't overloaded
		if (method.getDeclaringClass() == Object.class && "equals".equals(method.getName())) {
			// What if equals throws exception!?

			// This class implements the equals() method itself
			return new Boolean(equals(args[0]));
		}
		else if (Advised.class == method.getDeclaringClass()) {
			// Service invocations on ProxyConfig with the proxy config
			return AopProxyUtils.invokeJoinpointUsingReflection(this.advised, method, args);
		}
		
		Object retVal = null;
		
		// May be null. Get as late as possible to minimize the time we "own" the target,
		// in case it comes from a pool.
		// 目标实现类
		target = targetSource.getTarget();
		if (target != null) {
			targetClass = target.getClass();
		}
		
		if (this.advised.exposeProxy) {
			// Make invocation available if necessary
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
	
		// Get the interception chain for this method
		// 获取目标类,执行方法的 interception chain
		List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
				this.advised, proxy, 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
		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
			retVal = AopProxyUtils.invokeJoinpointUsingReflection(target, method, args);
		}
		else {
			
			invocation = new ReflectiveMethodInvocation(proxy, target,
								method, args, targetClass, chain);
									
			// Proceed to the joinpoint through the interceptor chain
			// 方法调用
			retVal = invocation.proceed();
		}
		
		// Massage return value if necessary
		if (retVal != null && retVal == target) {
			retVal = proxy;
		}
		return retVal;
	}
	finally {
	}
}

Во-первых, давайте посмотрим, как получить перехватчик, соответствующий текущему методу, см.calculateInterceptorsAndDynamicInterceptionAdviceРеализация выглядит следующим образом:

public static List calculateInterceptorsAndDynamicInterceptionAdvice(Advised config, Object proxy, Method method, Class targetClass) {
	// 用于存储拦截器
	List interceptors = new ArrayList(config.getAdvisors().length);
	// 遍历 advisor (切面)
	for (int i = 0; i < config.getAdvisors().length; i++) {
		Advisor advisor = config.getAdvisors()[i];
		if (advisor instanceof PointcutAdvisor) {
			// Add it conditionally
			PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
			// 判断当前 target class 是否当前 pointcut
			if (pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
				// 获取 advisor 对应的 method interceptor 
				MethodInterceptor interceptor = (MethodInterceptor) GlobalAdvisorAdapterRegistry.getInstance().getInterceptor(advisor);
				MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
				// 判断当前 method 是否匹配 pointcut
				if (mm.matches(method, targetClass)) {
					if (mm.isRuntime()) {
						// Creating a new object instance in the getInterceptor() method
						// isn't a problem as we normally cache created chains
						interceptors.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm) );
					}
					else {							
						// 将拦截器加入链表中
						interceptors.add(interceptor);
					}
				}
			}
		}
		else if (advisor instanceof IntroductionAdvisor) {
			IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
			if (ia.getClassFilter().matches(targetClass)) {
				MethodInterceptor interceptor = (MethodInterceptor) GlobalAdvisorAdapterRegistry.getInstance().getInterceptor(advisor);
				interceptors.add(interceptor);
			}
		}
	}	// for
	return interceptors;
}	// calculateInterceptorsAndDynamicInterceptionAdvice

Давайте подробно рассмотрим, как найти перехватчик, соответствующий советнику, который также аналогичен приведенному выше.wrapАналогично, следующим образом:

public Interceptor getInterceptor(Advisor advisor) throws UnknownAdviceTypeException {
	Advice advice = advisor.getAdvice();
	if (advice instanceof Interceptor) {
		return (Interceptor) advice;
	}

	// 遍历内置的 advisor adapter
	for (int i = 0; i < this.adapters.size(); i++) {
		AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
		// 是否匹配当前 advice
		if (adapter.supportsAdvice(advice)) {
			// 匹配的话返回 interceptor
			return adapter.getInterceptor(advisor);
		}
	}
	throw new UnknownAdviceTypeException(advisor.getAdvice());
}

До сих пор мы нашли много разAdvisorAdapterРисунок, давайте посмотрим на его конкретную реализацию, чтобыBeforeAdviceAdapterНапример:

class BeforeAdviceAdapter implements AdvisorAdapter {

	/**
	 * @see org.springframework.aop.framework.adapter.AdvisorAdapter#supportsAdvice(java.lang.Object)
	 */
	public boolean supportsAdvice(Advice advice) {
		// 匹配 MethodBeforeAdvice
		return advice instanceof MethodBeforeAdvice;
	}

	/**
	 * @see org.springframework.aop.framework.adapter.AdvisorAdapter#getInterceptor(org.springframework.aop.Advisor)
	 */
	public Interceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		// 返回 MethodBeforeAdviceInterceptor
		return new MethodBeforeAdviceInterceptor(advice) ;
	}

}

пройти черезAdvisorAdapterОчень разумно сочетать Advice и Interceptor, а также обнаружить, что связь между ними является взаимно-однозначным соответствием.

Давайте посмотрим на реальный процесс вызова метода,ReflectiveMethodInvocationМетоды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();
	}

	// 获取下一个拦截器
	Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
		// Evaluate dynamic method matcher here: static part will already have
		// been evaluated and found to match
		InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
		if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
			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
		// 执行拦截器
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

Посмотрите нижеMethodInterceptorРеализации: предварительное уведомление, уведомление о возврате, уведомление об исключении.

public Object invoke(MethodInvocation mi) throws Throwable {
	// 目标方法前执行
	advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
	return mi.proceed();
}

public Object invoke(MethodInvocation mi) throws Throwable {
	// 先执行目标方法
	Object retval = mi.proceed();
	// 后置处理
	advice.afterReturning(retval, mi.getMethod(), mi.getArguments(), mi.getThis() );
	return retval;
}

public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		// 执行目标方法
		return mi.proceed();
	}
	catch (Throwable t) {
		// 异常处理
		Method handlerMethod = getExceptionHandler(t);
		if (handlerMethod != null) {
			invokeHandlerMethod(mi, t, handlerMethod);
		}
		throw t;
	}
}

На данный момент процесс выполнения прокси-объекта Spring AOP завершился, и его процесс можно примерно резюмировать следующим образом:

  • Получить цепочку перехватчиков текущего целевого метода
    1. Пройдитесь по советнику, чтобы определить, соответствуют ли текущий целевой класс и целевой метод ponitcut, соответствующему советнику.
    2. Сопоставьте соответствующий советник Adapter с помощью соответствующего совета соответствующего советника, а затем получите соответствующий метод Interceptor.
  • выполнить перехватчик
  • Выполнить целевой метод

резюме

Отношения объектов в Spring AOP резюмируются следующим образом:

  • Advisor: перевод является советником, и это просто Aspect (аспект); он внутренне связан с соответствующим Pointcut (точка входа) и Advice (уведомление).
  • Цепочка советников: цепочка аспектов представляет собой набор из серии аспектов.
  • Совет: Уведомление, представляющее собой расширенную обработку методов перехвата; в версии 1.0 включает в себя BeforeAdivce, AfterReturningAdvice, ThrowsAdvice; ориентировано на пользователей.
  • MethodInterceptor : Перехватчик метода, который является исполнителем Advice; взаимно однозначное соответствие с Advice.
  • AdvisorAdapter : Адаптер Advice, который является связующим звеном между Advice и MethodInterceptor.
  • AdvisorAdapterRegistry: это реестр AdvisorAdapter со встроенными BeforeAdviceAdapter, AfterReturnAdviceAdapter, ThrowsAdviceAdapter; он используется для включения Advisor в Advisor и предоставления MethodInterceptor, соответствующего получению Advice.

яма

При использовании версии Spring 1.0, когда мы настраиваем Advice, можем ли мы поддерживать несколько Advice одновременно? Например:

public class UserAdvice implements MethodBeforeAdvice, AfterReturningAdvice {

    public void before(Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do before advice ....");
    }

    public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do after returning ....");
    }
}

Потом при тестировании обнаружится, что вызывается только before, а afterReturning не вызывается, почему так? (смотрите исходники)