Spring中AOP相关的API及源码解析,原来AOP是这样子的

Spring中AOP相关的API及源码解析html

本系列文章:java

读源码,咱们能够从第一行读起web

你知道Spring是怎么解析配置类的吗?spring

配置类为何要添加@Configuration注解?app

谈谈Spring中的对象跟Bean,你知道Spring怎么建立对象的吗?ide

这篇文章,咱们来谈一谈Spring中的属性注入 svg

推荐阅读:函数

Spring官网阅读 | 总结篇源码分析

Spring杂谈学习

本系列文章将会带你一行行的将Spring的源码吃透,推荐阅读的文章是阅读源码的基础!

由于本文会涉及到动态代理的相关内容,若是对动态代理不是很了解的话,参考文章:

动态代理学习(一)本身动手模拟JDK动态代理

动态代理学习(二)JDK动态代理源码分析

前言

之因此写这么一篇文章主要是由于下篇文章将结束Spring启动整个流程的分析,从解析配置到建立对象再到属性注入最后再将建立好的对象初始化成为一个真正意义上的Bean。由于下篇文章会设计到AOP,因此提早单独将AOP的相关API及源码作一次解读,这样能够下降阅读源码的障碍,话很少说,咱们进入正文!

一个使用API建立代理的例子

在进入API分析前,咱们先经过两个例子体会下如何使用API的方式来建立一个代理对象,对应示例以下:

  1. 定义通知
public class DmzAfterReturnAdvice implements AfterReturningAdvice {
	@Override
	public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
		System.out.println("after invoke method [" + method.getName() + "],aop afterReturning logic invoked");
	}
}

public class DmzAroundAdvice implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("aroundAdvice invoked");
		return invocation.proceed();
	}
}

public class DmzBeforeAdvice implements MethodBeforeAdvice {
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("before invoke method [" + method.getName() + "],aop before logic invoked");
	}
}

public class DmzIntroductionAdvice extends DelegatingIntroductionInterceptor implements Runnable {
	@Override
	public void run() {
		System.out.println("running!!!!");
	}
}
  1. 切点
public class DmzPointcut implements Pointcut {
	@Override
	@NonNull
	public ClassFilter getClassFilter() {
		// 在类级别上不进行拦截
		return ClassFilter.TRUE;
	}

	@Override
	@NonNull
	public MethodMatcher getMethodMatcher() {
		return new StaticMethodMatcherPointcut() {
			@Override
			public boolean matches(@NonNull Method method, Class<?> targetClass) {
				// 对于toString方法不进行拦截
				return !method.getName().equals("toString");
			}
		};
	}
}
  1. 目标类
public class DmzService {
	@Override
	public String toString() {
		System.out.println("dmzService toString invoke");
		return "dmzService";
	}

	public void testAop(){
		System.out.println("testAop invoke");
	}
}
  1. 测试代码
public class Main {
	public static void main(String[] args) {

		ProxyFactory proxyFactory = new ProxyFactory();

		// 一个Advisor表明的是一个已经跟指定切点绑定了的通知
        // 在这个例子中意味着环绕通知不会做用到toString方法上
		Advisor advisor = new DefaultPointcutAdvisor(new DmzPointcut(), new DmzAroundAdvice());

		// 添加一个绑定了指定切点的环绕通知
		proxyFactory.addAdvisor(advisor);

		// 添加一个返回后的通知
		proxyFactory.addAdvice(new DmzAfterReturnAdvice());

		// 添加一个方法执行前的通知
		proxyFactory.addAdvice(new DmzBeforeAdvice());

		// 为代理类引入一个新的须要实现的接口--Runnable
		proxyFactory.addAdvice(new DmzIntroductionAdvice());

		// 设置目标类
		proxyFactory.setTarget(new DmzService());

		// 由于要测试代理对象本身定义的方法,因此这里启用cglib代理
		proxyFactory.setProxyTargetClass(true);

		// 建立代理对象
		Object proxy = proxyFactory.getProxy();

		// 调用代理类的toString方法,经过控制台查看代理逻辑的执行状况
		proxy.toString();

		if (proxy instanceof DmzService) {
			((DmzService) proxy).testAop();
		}

		// 判断引入是否成功,并执行引入的逻辑
		if (proxy instanceof Runnable) {
			((Runnable) proxy).run();
		}
	}
}

这里我就不将测试结果放出来了,你们能够先自行思考这段程序将输出什么。接下来咱们就来分析上面这段程序中所涉及到的API,经过这些API的学习相信你们能够完全理解上面这段代码。

API介绍

Pointcut(切点)

对应接口定义以下:

public interface Pointcut {
	
    // ClassFilter,在类级别进行过滤
	ClassFilter getClassFilter();
    
	// MethodMatcher,在方法级别进行过滤
	MethodMatcher getMethodMatcher();
	
    // 一个单例对象,默认匹配全部
	Pointcut TRUE = TruePointcut.INSTANCE;

}

切点的主要做用是定义通知所要应用到的类跟方法,上面的接口定义也很明显的体现了这一点,咱们能够将其拆分红为两个部分

  • ClassFilter,接口定义以下:
public interface ClassFilter {

	boolean matches(Class<?> clazz);

	ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

ClassFilter的主要做用是在类级别上对通知的应用进行一次过滤,若是它的match方法对任意的类都返回true的话,说明在类级别上咱们不须要过滤,这种状况下,通知的应用,就彻底依赖MethodMatcher的匹配结果。

  • MethodMatcher,接口定义以下:
public interface MethodMatcher {

	boolean matches(Method method, @Nullable Class<?> targetClass);

	boolean isRuntime();

	boolean matches(Method method, @Nullable Class<?> targetClass, Object... args);

	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

MethodMatcher中一共有三个核心方法

  • matches(Method method, @Nullable Class<?> targetClass),这个方法用来判断当前定义的切点跟目标类中的指定方法是否匹配,它能够在建立代理的时候就被调用,从而决定是否须要进行代理,这样就能够避免每次方法执行的时候再去作判断
  • isRuntime(),若是这个方法返回true的话,意味着每次执行方法时还须要作一次匹配
  • matches(Method method, @Nullable Class<?> targetClass, Object... args),当以前的isRuntime方法返回true时,会调用这个方法再次进行一次判断,返回false的话,意味这个不对这个方法应用通知

Advice(通知)

环绕通知(Interception Around Advice)

接口定义以下:

public interface MethodInterceptor extends Interceptor {

    Object invoke(MethodInvocation invocation) throws Throwable;
}

在上面接口定义的invoke方法中,MethodInvocation就是当前执行的方法,当咱们调用invocation.proceed就是在执行当前的这个方法,基于此,咱们能够在方法的执行先后去插入咱们自定义的逻辑,好比下面这样

// 执行前的逻辑
doSomeThingBefore();
Object var = invocation.proceed;
doSomeThingAfter();
// 执行后的逻辑
retrun var;

前置通知(Before Advice)

public interface MethodBeforeAdvice extends BeforeAdvice {

    void before(Method m, Object[] args, Object target) throws Throwable;
}

跟环绕通知不一样的是,这个接口中定义的方法的返回值是void,因此前置通知是没法修改方法的返回值的。

若是在前置通知中发生了异常,那么会直接终止目标方法的执行以及打断整个拦截器链的执行

后置通知(After Returning Advice)

public interface AfterReturningAdvice extends Advice {

    void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable;
}

后置通知相比较于前置通知,主要有如下几点不一样

  • 后置通知能够访问目标方法的返回值,可是不能修改
  • 后置通知是在方法执行完成后执行

异常通知(Throws Advice)

public interface ThrowsAdvice extends AfterAdvice {

}

异常通知中没有定义任何方法,它更像一个标记接口。咱们在定义异常通知时须要实现这个接口,同时方法的签名也有要求

  1. 方法名称必须是afterThrowing
  2. 方法的参数个数必须是1个或者4个,以下:
public class OneParamThrowsAdvice implements ThrowsAdvice {

    // 若是只有一个参数,那么这个参数必须是要进行处理的异常
    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with remote exception
    }
}

public class FourParamThrowsAdvice implements ThrowsAdvice {
	
    // 若是定义了四个参数,那么这四个参数分别是
    // 1.m:目标方法
    // 2.args:执行目标方法所须要的参数
    // 3.target:目标对象
    // 4.ex:具体要处理的异常
    // 而且参数类型必须按照这个顺序定义
    public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
        // Do something with all arguments
    }
}

咱们能够在一个异常通知中定义多个方法,在后续的源码分析中咱们会发现,这些方法最终会被注册成对应的异常的handler,像下面这样

public static class CombinedThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with remote exception
    }

    public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
        // Do something with all arguments
    }
}

引入通知(Introduction Advice)

引入通知的主要做用是可让生成的代理类实现额外的接口。例如在上面的例子中,咱们为DmzService建立一个代理对象,同时为其定义了一个引入通知

public class DmzIntroductionAdvice extends DelegatingIntroductionInterceptor implements Runnable {
	@Override
	public void run() {
		System.out.println("running!!!!");
	}
}

在这个引入通知中,咱们为其引入了一个新的须要实现的接口Runnable,同时通知自己做为这个接口的实现类。

经过这个引入通知,咱们能够将生成的代理类强转成Runnable类型而后执行其run方法,同时,run方法也会被前面定义的前置通知,后置通知等拦截。

为了更好的了解引入通知,咱们来须要了解下DelegatingIntroductionInterceptor这个类。见名知意,这个类就是一个委托引入拦截器,由于咱们要为代理类引入新的接口,由于着咱们要提供具体的实现的逻辑,而具体的实现的逻辑就能够被委托给这个DelegatingIntroductionInterceptor

咱们能够看看它的源码

public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport
		implements IntroductionInterceptor {
	
    // 实际实现了引入逻辑的类
	@Nullable
	private Object delegate;
	
    // 对外提供了一个带参的构造函数,经过这个构造函数咱们能够传入一个
    // 具体的实现类
	public DelegatingIntroductionInterceptor(Object delegate) {
		init(delegate);
	}
    // 对子类暴露了一个空参的构造函数,默认将自身做为实现了引入逻辑的委托类
    // 咱们上面的例子中就是使用的这种方法
	protected DelegatingIntroductionInterceptor() {
		init(this);
	}
	
    // 对这个类进行初始化,要经过实际的实现类来找到具体要实现的接口
	private void init(Object delegate) {
		Assert.notNull(delegate, "Delegate must not be null");
		this.delegate = delegate;
        
        // 找到delegate全部实现的接口
		implementInterfacesOnObject(delegate);
		
        // 由于咱们可能会将DelegatingIntroductionInterceptor自己做为委托者
        // Spring的设计就是不对外暴露这两个接口
        // 若是将其暴露,意味着咱们能够将代理类强转成这种类型
		suppressInterface(IntroductionInterceptor.class);
		suppressInterface(DynamicIntroductionAdvice.class);
	}

	// 引入通知自己也是基于拦截器实现的,当执行一个方法时须要判断这个方法
    // 是否是被引入的接口中定义的方法,若是是的话,那么不能调用目标类的方法
    // 而要调用委托类的方法
	public Object invoke(MethodInvocation mi) throws Throwable {
		if (isMethodOnIntroducedInterface(mi)) {
			Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());
			// 这里是处理一种特殊状况,方法的返回值是this的时候
            // 这里应该返回代理类
			if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
				Object proxy = ((ProxyMethodInvocation) mi).getProxy();
				if (mi.getMethod().getReturnType().isInstance(proxy)) {
					retVal = proxy;
				}
			}
            // 其他状况下直接将委托类的执行结果返回
			return retVal;
		}
        // 执行到这里说明不是引入的方法,这是Spring提供了一个扩展逻辑
        // 正常来讲这个类只会处理引入的逻辑,经过这个方法能够对目标类中的方法作拦截
        // 不经常使用
		return doProceed(mi);
	}

	protected Object doProceed(MethodInvocation mi) throws Throwable {
		return mi.proceed();
	}

}

经过查看这个类的源码咱们能够发现,所谓的引入其实就是在方法执行的时候加了一层拦截,当判断这个方法是被引入的接口提供的方法的时候,那么就执行委托类中的逻辑而不是目标类中的方法

关于通知的总结

经过上文的分析咱们能够发现,通知总共能够分为这么几类

  1. 普通的通知(前置,后置,异常等,没有实现MethodInterceptor接口)
  2. 环绕通知(实现了MethodInterceptor接口)
  3. 引入通知(须要提供额外的引入的信息,实现了MethodInterceptor接口)

上面的分类并不标准,只是为了方便你们记忆跟理解,虽然咱们普通的通知没有直接实现MethodInterceptor接口,但其实它的底层也是依赖于拦截器来完成的,你们能够看看下面这个类

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}
	
    // 根据传入的一个前置通知,建立一个对应的拦截器
	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}

}

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

	private final MethodBeforeAdvice advice;

	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

	// 实际上仍是利用拦截器,在方法执行前调用了通知的before方法完成了前置通知
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}

}

Advisor (绑定通知跟切点)

一个Advisor实际上就是一个绑定在指定切点上的通知。在前面的例子咱们能够发现,有两种添加通知的方式

// 一个Advisor表明的是一个已经跟指定切点绑定了的通知
// 在这个例子中意味着环绕通知不会做用到toString方法上
Advisor advisor = new DefaultPointcutAdvisor(new DmzPointcut(), new DmzAroundAdvice());

// 添加一个绑定了指定切点的环绕通知
proxyFactory.addAdvisor(advisor);

// 添加一个返回后的通知
proxyFactory.addAdvice(new DmzAfterReturnAdvice());

一种是直接添加了一个Advisor,还有一种是添加一个Advice,后者也会被转换成一个Advisor而后再进行添加,没有指定切点的通知是没有任何意义的

public void addAdvice(Advice advice) throws AopConfigException {
    int pos = this.advisors.size();
    // 默认添加到集合的最后一个位置
    addAdvice(pos, advice);
}

// 这个方法添加通知
public void addAdvice(int pos, Advice advice) throws AopConfigException {
    Assert.notNull(advice, "Advice must not be null");
    
    // 若是是一个引入通知,那么构建一个DefaultIntroductionAdvisor
    // DefaultIntroductionAdvisor会匹配全部类
    if (advice instanceof IntroductionInfo) {
        addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
    }
    // 不能直接添加一个不是IntroductionInfo的DynamicIntroductionAdvice(动态引入通知)
    else if (advice instanceof DynamicIntroductionAdvice) {
        throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
    }
    else {
        // 若是是普通的通知,那么会建立一个DefaultPointcutAdvisor
        // DefaultPointcutAdvisor所定义的切点会匹配全部类以及全部方法
        addAdvisor(pos, new DefaultPointcutAdvisor(advice));
    }
}

ProxyCreatorSupport

这个类的主要做用是为建立一个AOP代理对象提供一些功能支持,经过它的getAopProxyFactory能获取一个建立代理对象的工厂。

// 这里我只保留了这个类中的关键代码
public class ProxyCreatorSupport extends AdvisedSupport { 

    private AopProxyFactory aopProxyFactory;

    // 空参构造,默认会建立一个DefaultAopProxyFactory
    // 经过这个ProxyFactory能够建立一个cglib代理或者jdk代理
    public ProxyCreatorSupport() {
        this.aopProxyFactory = new DefaultAopProxyFactory();
    }

    // 经过这个方法能够建立一个具体的代理对象
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        // 实际就是使用DefaultAopProxyFactory来建立一个代理对象
        // 能够看到在调用createAopProxy方法时,传入的参数是this
        // 这是由于ProxyCreatorSupport自己就保存了建立整个代理对象所须要的配置信息
        return getAopProxyFactory().createAopProxy(this);
    }
}

image-20200701231849588

另外经过上面的UML类图还能看到,ProxyCreatorSupport继承了AdvisedSupportAdvisedSupport继承了ProxyConfig

ProxyConfig

其中ProxyConfig是全部的AOP代理工厂的父类,它包含了建立一个AOP代理所须要的基础的通用的一些配置信息

// 这里省略了一些getter跟setter方法
public class ProxyConfig implements Serializable {
	
    // 是否开启cglib代理,默认不开启使用jdk动态代理
	private boolean proxyTargetClass = false;

    // 是否启用优化,默认为false,按照官网对这个参数的解释
    // 这个优化是针对cglib,若是设计为true的话,会作一些侵入性的优化
    // 是否开启在jdk代理的状况下没有影响
    // 官网中特意说明了,除非对cglib的优化很是了解,不然不要开启这个参数
	private boolean optimize = false;
	
    // 生成的代理类是否须要实现Advised接口,这个接口能够向外提供操做通知的方法
    // 若是为false会实现
    // 为true的话,不会实现
	boolean opaque = false;
	
    // 是否将当前的配置类暴露到一个线程上下文中,若是设置为true的话
    // 能够经过AopContext.currentProxy()来获取到当前的代理对象
	boolean exposeProxy = false;
    
    // 标志着是否冻结整个配置,若是冻结了,那么配置信息将不容许修改
	private boolean frozen = false;
}

AdvisedSupport

当咱们为某个对象建立代理时,除了须要上面的ProxyConfig提供的一些基础配置外,起码还须要知道

  1. 须要执行的通知是哪些?
  2. 目标对象是谁?
  3. 建立出来的代理须要实现哪些接口?

而这些配置信息是由AdvisedSupport提供的,AdvisedSupport自己实现了Advised接口,Advised接口定义了管理通知的方法。


在了解了上面的API后咱们来看看Spring提供了几种建立AOP代理的方式

  1. ProxyFactoryBean
  2. ProxyFactory
  3. Auto-proxy

ProxyFactoryBean的方式建立AOP代理

使用示例

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

	<bean class="com.dmz.spring.initalize.service.DmzService" name="dmzService"/>

	<bean id="aroundAdvice" class="com.dmz.spring.initalize.aop.advice.DmzAroundAdvice"/>

	<bean id="dmzProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!---->
		<property name="proxyInterfaces" value="java.lang.Runnable"/>
		<property name="proxyTargetClass" value="true"/>
 		<property name="target" ref="dmzService"/>
		<property name="interceptorNames">
			<list>
				<value>aroundAdvice</value>
			</list>
		</property>
	</bean>

</beans>
// 目标类
public class DmzService {
	@Override
	public String toString() {
		System.out.println("dmzService toString invoke");
		return "dmzService";
	}

	public void testAop(){
		System.out.println("testAop invoke");
	}
}

// 通知
public class DmzAroundAdvice implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("aroundAdvice invoked");
		return invocation.proceed();
	}
}

public class SourceMain {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext cc =
				new ClassPathXmlApplicationContext("application-init.xml");
		DmzService dmzProxy = ((DmzService) cc.getBean("dmzProxy"));
		dmzProxy.testAop();
	}
}

ProxyFactoryBean介绍

跟普通的FactoryBean同样,这个类的主要做用就是经过getObject方法可以获取一个Bean,不一样的是这个类获取到的是代理后的Bean。

咱们查看这个类的继承关系能够发现

image-20200701181049929

这个类除了实现了FactoryBean接口以及一些Aware接口外,额外还继承了ProxyCreatorSupport类。它是一个factoryBean,因此咱们重点就关注它的getObject方法便可。

public Object getObject() throws BeansException {
    // 初始化通知链
    // 这里主要就是将在XML中配置的通知添加到
    // AdvisedSupport管理的配置中去
    initializeAdvisorChain();
    if (isSingleton()) {
        // 若是是单例的,那么获取一个单例的代理对象
        return getSingletonInstance();
    }
    else {
        if (this.targetName == null) {
            logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
        }
        // 若是是原型的,获取一个原型的代理对象
        return newPrototypeInstance();
    }
}

关于这段代码就不作过多分析了,它其实就两步(不论是哪一种方式建立代理,都分为这两步)

  1. 完善建立代理须要的配置信息
  2. 建立代理

其中配置信息分为两部分,其一是AppConfig管理的通用的配置信息,其二是AdvisedSupport管理的通知信息。通用的配置信息咱们能够直接在XML中配置,例如在上面的例子中咱们就配置了proxyTargetClass属性,而通知信息即便咱们在XML中配置了也还须要作一层转换,在前面咱们也提到过了,全部的Advice都会被转换成Advisor添加到配置信息中。

ProxyFactory的方式建立AOP代理

使用示例(略,见开头)

ProxyFactory介绍

image-20200702084419039

从上面咱们能够看出,ProxyFactory也继承自ProxyCreatorSupport,从以前的例子咱们也能感觉到,使用它的API来建立一个代理对象也是要先去设置相关的配置信息,最后再调用建立代理的方法

咱们以后要分析的自动代理内部就是经过建立了一个ProxyFactory来获取代理对象的。

咱们能够对比下ProxyFactoryBeanProxyFactory在建立代理对象时的代码

  • ProxyFactory
public Object getProxy() {
    // 调用了ProxyCreatorSupport的createAopProxy()方法建立一个AopProxy对象
    // 而后调用AopProxy对象的getProxy方法
    return createAopProxy().getProxy();
}
  • ProxyFactoryBean
private synchronized Object getSingletonInstance() {
    if (this.singletonInstance == null) {
        this.targetSource = freshTargetSource();
        if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
            Class<?> targetClass = getTargetClass();
            if (targetClass == null) {
                throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
            }
            setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
        }
        super.setFrozen(this.freezeProxy);
        // 重点就看这里
        // 这里调用了ProxyCreatorSupport的createAopProxy()方法建立一个AopProxy对象
        // 而getProxy方法就是调用建立的AopProxy的getProxy方法
        this.singletonInstance = getProxy(createAopProxy());
    }
    return this.singletonInstance;
}

protected Object getProxy(AopProxy aopProxy) {
    return aopProxy.getProxy(this.proxyClassLoader);
}

综上,咱们能够得出结论,不论是经过哪一种方式建立AOP代理,核心代码就一句

createAopProxy().getProxy()

这句代码也是咱们接下来源码分析的重点

Auto-proxy(实现自动AOP代理)

自动代理机制的实现其实很简单,就是经过Bean的后置处理器,在建立Bean的最后一步对Bean进行代理,并将代理对象放入到容器中。

实现自动代理的核心类就是AbstractAutoProxyCreator。咱们来看看它的继承关系

image-20200702103750263

为了更好的体会自动代理的做用,咱们对它的三个具体的实现类来进行分析,分别是

  1. BeanNameAutoProxyCreator
  2. DefaultAdvisorAutoProxyCreator
  3. AnnotationAwareAspectJAutoProxyCreator

BeanNameAutoProxyCreator

使用示例

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

	<bean class="com.dmz.spring.initalize.service.DmzService" name="dmzService"/>

	<bean id="aroundAdvice" class="com.dmz.spring.initalize.aop.advice.DmzAroundAdvice"/>

	<bean id="beforeAdvice" class="com.dmz.spring.initalize.aop.advice.DmzBeforeAdvice"/>

    <!--使用很简单,只要配置一个BeanNameAutoProxyCreator便可-->
	<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" name="autoProxyCreator">
        <!--使用cglib代理-->
		<property name="proxyTargetClass" value="true"/>
         <!--对全部以dmz开头的bean进行自动代理-->
		<property name="beanNames" value="dmz*"/>
        <!--添加两个通知-->
		<property name="interceptorNames">
			<list>
				<value>beforeAdvice</value>
				<value>aroundAdvice</value>
			</list>
		</property>
	</bean>

</beans>
public class SourceMain {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext cc =
				new ClassPathXmlApplicationContext("application-init.xml");
		DmzService dmzProxy = ((DmzService) cc.getBean("dmzService"));
		dmzProxy.testAop();
	}
}
// 程序打印:
// before invoke method [testAop],aop before logic invoked
// aroundAdvice invoked
// testAop invoke

DefaultAdvisorAutoProxyCreator

使用示例

在上面例子的基础上咱们要修改配置文件,以下:

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

	<bean class="com.dmz.spring.initalize.service.DmzService" name="dmzService"/>

	<bean id="aroundAdvice" class="com.dmz.spring.initalize.aop.advice.DmzAroundAdvice"/>

	<bean id="beforeAdvice" class="com.dmz.spring.initalize.aop.advice.DmzBeforeAdvice"/>
	
	<bean class="org.springframework.aop.support.DefaultPointcutAdvisor" id="dmzBeforeAdvisor">
		<property name="advice" ref="beforeAdvice"/>
	</bean>

	<bean class="org.springframework.aop.support.DefaultPointcutAdvisor" id="dmzAroundAdvisor">
		<property name="advice" ref="aroundAdvice"/>
	</bean>

	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" id="advisorAutoProxyCreator">
        <!--这两个参数标明了咱们要使用全部以dmz开头的Advisor类型的通知 这里必须配置是Advisor,不能是Advice或者interceptor, 能够看到DefaultAdvisorAutoProxyCreator跟BeanNameAutoProxyCreator的区别在于 BeanNameAutoProxyCreator须要指定要被代理的bean的名称, 而DefaultAdvisorAutoProxyCreator不须要,它会根据咱们传入的Advisor 获取到须要被代理的切点 -->
		<property name="usePrefix" value="true"/>
		<property name="advisorBeanNamePrefix" value="dmz"/>
		
        <property name="proxyTargetClass" value="true"/>
	</bean>
</beans>

测试代码就不放了,你们能够自行测试,确定是没问题的

AnnotationAwareAspectJAutoProxyCreator

咱们正常在使用AOP的时候都会在配置类上添加一个@EnableAspectJAutoProxy注解,这个注解干了什么事呢?

实际就是向容器中注册了一个AnnotationAwareAspectJAutoProxyCreator

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 这里导入了一个类
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
	
	boolean proxyTargetClass() default false;

	boolean exposeProxy() default false;

}

经过@EnableAspectJAutoProxy导入了一个AspectJAutoProxyRegistrar,这个类会向容器中注册一个AnnotationAwareAspectJAutoProxyCreator,对应源码以下:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		
        // 在这里完成的注册
		// 最终会调用到AopUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法
        // 完成AnnotationAwareAspectJAutoProxyCreator这个bd的注册
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
		
        // 解析注解的属性
        // proxyTargetClass:为true的话开启cglib代理,默认为jdk代理
        // exposeProxy:是否将代理对象暴露到线程上下文中
		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
          
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

前面已经说过了,自动代理机制实际上就是Spring在内部new了一个ProxyFactory,经过它建立了一个代理对象。对应的代码就在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 proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
	// 经过proxyFactory来建立一个代理对象
    return proxyFactory.getProxy(getProxyClassLoader());
}

关于这个类的执行流程在下篇文章中我再详细介绍,接下来咱们要分析的就是具体建立AOP代理的源码了。对应的核心源码就是咱们以前所提到的

createAopProxy().getProxy();

这行代码分为两步,咱们逐步分析

  1. 调用AopProxyFactorycreateAopProxy()方法获取一个AopProxy对象
  2. 调用AopProxy对象的getProxy()方法

核心源码分析

createAopProxy方法分析

AopProxyFactory在Spring中只有一个默认的实现类,就是DefaultAopProxyFactory,它的对应的createAopProxy的是实现代码以下:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
	
    // 就是经过AOP相关的配置信息来决定究竟是使用cglib代理仍是jdk代理
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 若是开启了优化,或者ProxyTargetClass设置为true
        // 或者没有提供代理类须要实现的接口,那么使用cglib代理
        // 在前面分析参数的时候已经说过了
        // 默认状况下Optimize都为false,也不建议设置为true,由于会进行一些侵入性的优化
        // 除非你对cglib的优化很是了解,不然不建议开启
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                                             "Either an interface or a target is required for proxy creation.");
            }
            // 须要注意的是,若是须要代理的类自己就是一个接口
            // 或者须要被代理的类自己就是一个经过jdk动态代理生成的类
            // 那么无论如何设置都会使用jdk动态代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        // 不然都是jdk代理
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

	// 判断是否提供代理类须要实现的接口
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    }

}

getProxy方法分析

从对createAopProxy方法的分析能够看到,咱们要么执行的是ObjenesisCglibAopProxy中的getProxy方法,要么就是JdkDynamicAopProxygetProxy方法,两者的区别在于一个是经过cglib的方式生成代理对象,然后者则是经过jdk的方式生成动态代理。

这里我只分析一个JdkDynamicAopProxy,首先咱们来看看这个类的继承关系

但愿你以前已经阅读过

原创 动态代理学习(一)本身动手模拟JDK动态代理

原创 动态代理学习(二)JDK动态代理源码分析

image-20200702154037428

能够看到这个类自己就是一个InvocationHandler,这意味着当调用代理对象中的方法时,最终会调用到JdkDynamicAopProxyinvoke方法。

因此对于这个类咱们起码应该关注两个方法

  1. getProxy方法
  2. invoke方法

getProxy方法源码以下:

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    // 这里获取到代理类须要实现的全部的接口
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    // 须要明确是否在接口定义了hashCode以及equals方法
    // 若是接口中没有定义,那么在调用代理对象的equals方法的时候
    // 若是两个对象相等,那么意味着它们的目标对象,通知以及实现的接口都相同
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

咱们再来看看究竟是怎么获取到须要实现的接口的

static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
    // 第一步:获取在配置中指定的须要实现的接口
    Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
    
    // 第二步:若是没有指定须要实现的接口,可是须要代理的目标类自己就是一个接口
    // 那么将其添加到代理类须要实现的接口的集合中
    // 若是目标类自己不是一个接口,可是是通过jdk代理后的一个类
    // 那么获取这个代理后的类全部实现的接口,并添加到须要实现的接口集合中
    if (specifiedInterfaces.length == 0) {
        Class<?> targetClass = advised.getTargetClass();
        if (targetClass != null) {
            if (targetClass.isInterface()) {
                advised.setInterfaces(targetClass);
            }
            else if (Proxy.isProxyClass(targetClass)) {
                advised.setInterfaces(targetClass.getInterfaces());
            }
            specifiedInterfaces = advised.getProxiedInterfaces();
        }
    }
    
    // 第三步:为代理类添加三个默认须要实现的接口,分别是
    // 1.SpringProxy,一个标记接口,表明这个类是经过Spring的AOP代理生成的
    // 2.Advised,提供了管理通知的方法
    // 3.DecoratingProxy,用户获取到真实的目标对象
    // 这个真实对象指的是在嵌套代理的状况下会获取到最终的目标对象
    // 而不是指返回这个ProxyFactory的target
    boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
    boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
    boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
    int nonUserIfcCount = 0;
    if (addSpringProxy) {
        nonUserIfcCount++;
    }
    if (addAdvised) {
        nonUserIfcCount++;
    }
    if (addDecoratingProxy) {
        nonUserIfcCount++;
    }
    Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
    System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
    int index = specifiedInterfaces.length;
    if (addSpringProxy) {
        proxiedInterfaces[index] = SpringProxy.class;
        index++;
    }
    if (addAdvised) {
        proxiedInterfaces[index] = Advised.class;
        index++;
    }
    if (addDecoratingProxy) {
        proxiedInterfaces[index] = DecoratingProxy.class;
    }
    return proxiedInterfaces;
}

invoke方法分析

在确认了须要实现的接口后,直接调用了jdk的动态代理方法,这个咱们就不作分析了,接下来咱们来看看Spring是如何将通知应用到代理对象上的,对应的要分析的代码就是JdkDynamicAopProxyinvoke方法,源码以下:

// 这个方法的代码稍微有点长,代码也比较难,但愿你们能耐心看完
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 {
        // 首先处理的是hashCode跟equals方法
        // 若是接口中没有定义这两个方法,那么会调用本类中定义的equals方法
        // 前面咱们也说过了,只有当两个类的目标对象,通知以及实现的接口都相等的状况下
        // equals才会返回true
        // 若是接口中定义了这两个方法,那么最终会调用目标对象中的方法
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            return hashCode();
        }
        
        // 也就是说咱们调用的是DecoratingProxy这个接口中的方法
        // 这个接口中只定义了一个getDecoratedClass方法,用于获取到
        // 最终的目标对象,在方法实现中会经过一个while循环来不断接近
        // 最终的目标对象,直到获得的目标对象不是一个被代理的对象才会返回
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        
        // 说明调用的是Advised接口中的方法,这里只是单纯的进行反射调用
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                 method.getDeclaringClass().isAssignableFrom(Advised.class)) {

            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
		
        // 说明须要将代理类暴露到线程上下文中
        // 调用AopContext.setCurrentProxy方法将其放入到一个threadLocal中
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
		
        // 接下来就是真正的执行代理逻辑了
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
		
        // 先获取整个拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        
        // 若是没有进行拦截,直接反射调用方法
        if (chain.isEmpty()) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        
        // 不然开始执行整个链条
        else {
            MethodInvocation invocation =
                new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            retVal = invocation.proceed();
        }
	
        // 这里是处理一种特殊状况,就是当执行的方法返回值为this的状况
        // 这种状况下,须要返回当前的代理对象而不是目标对象
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            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()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

在上面整个流程中,咱们抓住核心的两步

  1. 获取整个拦截器链
  2. 开始在拦截器链上执行方法

咱们先看第一步,对应源码以下:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        // 调用了advisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
            this, method, targetClass);
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    Advised config, Method method, @Nullable Class<?> targetClass) {

    List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);

    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());

    // 是否有引入通知
    boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);

    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

    // 获取到全部的通知
    for (Advisor advisor : config.getAdvisors()) {
        // 除了引入通知外,能够认为全部的通知都是一个PointcutAdvisor
        if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // config.isPreFiltered:表明的是配置已通过滤好了,是能够直接应用的
            // 这句代码的含义就是配置是预过滤的或者在类级别上是匹配的
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                // 接下来要判断在方法级别上是否匹配
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                    // 将通知转换成对应的拦截器
                    // 有些通知自己就是拦截器,例如环绕通知
                    // 有些通知须要经过一个AdvisorAdapter来适配成对应的拦截器
                    // 例如前置通知,后置通知,异常通知等
                    // 其中MethodBeforeAdvice会被适配成MethodBeforeAdviceInterceptor
                    // AfterReturningAdvice会被适配成AfterReturningAdviceInterceptor
                    // ThrowAdvice会被适配成ThrowsAdviceInterceptor
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

                    // 若是是动态的拦截,会建立一个InterceptorAndDynamicMethodMatcher
                    // 动态的拦截意味着须要根据具体的参数来决定是否进行拦截
                    if (mm.isRuntime()) {
                        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;
}

在构建好拦截器链后,接下来就是真正执行方法了,对应代码就是

// 先建立一个MethodInvocation
MethodInvocation invocation =
    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 开始在拦截器链上执行这个方法
retVal = invocation.proceed();

最后的关键代码就落在了ReflectiveMethodInvocationproceed方法

public Object proceed() throws Throwable {
	
    // 知足这个条件,说明执行到了最后一个拦截器,那么直接反射调用目标方法
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }
	
    // 获取到下一个要执行的拦截器
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // 前面构建拦截器链的时候咱们能够看到,动态的拦截的话会建立一个InterceptorAndDynamicMethodMatcher
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
        // 若是匹配失败了,执行拦截器链中的下一个拦截逻辑
         return proceed();
      }
   }
   else {
	  // 调用拦截器中的invoke方法,能够看到这里将this做为参数传入了
      // 因此咱们在拦截器中调用 MethodInvocation的proceed时又会进行入当前这个方法
      // 而后去执行链条中的下一个拦截器 
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

总结

本文主要是为下篇文章作准备,下篇文章将会结束整个IOC流程的分析,IOC的最后一步即是为Bean建立代理。本文已经分析了代理的具体建立逻辑,在下篇文章中咱们主要结合Spring的启动流程来看一看Spring是如何将通知添加到建立代理的配置信息中去的。

关于整个IOC跟AOP的模块还会有两篇文章,一篇用于结束整个IOC流程,另一篇专门探讨Spring中循环依赖的解决。完成这两篇文章中,接下来打算用5到7篇文章对Spring的事务管理进行分析!

若是个人文章能帮到你,记得点个赞哈~!