回顾
在上一篇搞懂AOP之一,拦截器链中,我们介绍了MethodInterceptor增强目标类的方式,了解到了AOP的拦截器是如何使用,以及多个拦截器组成的拦截器链是如何工作的,并且看到了如何使用ProxyFactory去创建代理类。本篇接上篇,接着介绍另外一种增强方式--引入增强。
了解引入增强
定义
spring中的引入增强对应IntroductionInterceptor类。看下spring对其的定义:
/**
* Subinterface of AOP Alliance MethodInterceptor that allows additional interfaces
* to be implemented by the interceptor, and available via a proxy using that
* interceptor. This is a fundamental AOP concept called <b>introduction</b>.
*
* <p>Introductions are often <b>mixins</b>, enabling the building of composite
* objects that can achieve many of the goals of multiple inheritance in Java.
*
* @author Rod Johnson
* @see DynamicIntroductionAdvice
*/
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
}
MethodInterceptor的子接口,允许添加由拦截器实现接口,并可以通过代理使用拦截器。这是AOP的基本概念。 从字面意思理解比较懵,下文将通过实例去理解。
我只是一个翻译搬运官,对于源码的英文注释,我会保留并贴出来。因为我觉得编程这玩意,有时候翻译过来味道就变了,作者原本的思想可能就失真了,所以还是建议多读注释,spring的注释字字珍贵,没什么废话。
AOP是一种思想,而非实现。AOP是基于OOP,而又远远高于OOP,主要是将主要核心业务和交叉业务分离,交叉业务就是切面。introduction是AOP中的一个基本概念。不要把AOP和spring绑定起来,也不要把introduction和spring绑定起来。
相关类
在spring AOP中,与引入增强有关的类主要有:IntroductionInterceptor、DynamicIntroductionAdvice、DelegatingIntroductionInterceptor、IntroductionInfoSupport和IntroductionInfo。先总览下他们之间的关系,后文将一一介绍。
IntroductionInterceptor和MethodInterceptor
IntroductionInterceptor和MethodInterceptor相比有什么区别呢? 我的理解是,MethodInterceptor是对目标类方法的增强,它是基于目标类方法,在目标类方法基础上添加额外的逻辑。而IntroductionInterceptor是在不修改目标类的情况下,可以让没有实现A接口的目标类,具备A接口的功能,是凭空赋予目标类新的能力。这就好比,MethodInterceptor可以让一个篮球水平如周琦的人变得像姚明一样(非黑),而IntroductionInterceptor可以赋予一个不会打篮球的人运球、投篮的能力。这就是“0到1(从无到有)”和“1到2(从有到富)”的区别。
引用增强的原理
使用
学会一个东西之前,要先知道怎么用它。
还是以搞懂AOP之一,拦截器链中的例子,我们新建一个目标类Cat,实现了Animal接口。
public class Cat implements Animal {
@Override
public void bark() {
System.out.println("miao miao miao...");
}
}
###################
public interface Animal {
void bark();
}
接下来我们新建一个接口Flyable,定义了“飞行”的行为。
public interface Flyable {
void fly();
}
同时,定义引入飞行的增强行为,FlyableIntroductionInterceptor:
public class FlyableIntroductionInterceptor extends DelegatingIntroductionInterceptor implements Flyable {
@Override
public void fly() {
System.out.println("fly fly fly...");
}
}
FlyableIntroductionInterceptor继承了DelegatingIntroductionInterceptor,实现了Flyable接口,定义了fly的具体实现,这里仅仅打印一些东西。 接下来我们就在不修改Cat的前提下,让Cat也具有Flyable的行为。
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvice(new LogInterceptor());
proxyFactory.addAdvice(new TimeInterceptor());
proxyFactory.addAdvice(new AOPAfterReturningAdvice());
proxyFactory.addAdvice(new AOPMethodBeforeAdvice());
proxyFactory.addAdvice(new FlyableIntroductionInterceptor());
proxyFactory.setTarget(new Cat());
for (Class<?> ifc : Cat.class.getInterfaces()) {
proxyFactory.addInterface(ifc);
}
proxyFactory.setProxyTargetClass(false);
Object proxy = proxyFactory.getProxy();
if (proxy instanceof Animal) {
((Animal) proxy).bark();
}
System.out.println("============");
if (proxy instanceof Flyable) {
((Flyable) proxy).fly();
}
}
输出结果
开始执行
计时开始
Before...
miao miao miao...
AfterReturning ....
计时结束,耗时:0
执行完毕
============
开始执行
计时开始
Before...
fly fly fly...
AfterReturning ....
计时结束,耗时:0
执行完毕
可以看到,通过proxyFactory.addAdvice(new FlyableIntroductionInterceptor())
,我们让Cat拥有了Flyable行为,一个猫具备了飞行的能力!并且fly方法与bark方法一样,被LogInterceptor、TimeInterceptor、AOPAfterReturningAdvice和AOPMethodBeforeAdvice进行了增强。看起来就跟Cat自己实现了fly接口一样。这就是引入增强,在不修改目标类(Cat)的情况下,可以让没有实现A接口(Flyable)的目标类,具备A接口的功能。
剖析
按照惯例,我们还是看一下代理增强后的Cat对应的拦截器链:
JdkDynamicAopProxy#invoke
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
可以看到,FlyableIntroductionInterceptor作为一个MethodInterceptor加入到了拦截器链中,按照搞懂AOP之一,拦截器链的分析,真相就在FlyableIntroductionInterceptor的invoke方法中! 最终进入到其父类DelegatingIntroductionInterceptor内。
public Object invoke(MethodInvocation mi) throws Throwable {
if (isMethodOnIntroducedInterface(mi)) {
// Using the following method rather than direct reflection, we
// get correct handling of InvocationTargetException
// if the introduced method throws an exception.
// 如果当前被调用的方法是被引入增强的,则直接执行
Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());
// Massage return value if possible: if the delegate returned itself,
// we really want to return the proxy.
// 如果返回值是被代理对象,要确保返回的是代理后的对象而非代理前的对象
if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
Object proxy = ((ProxyMethodInvocation) mi).getProxy();
if (mi.getMethod().getReturnType().isInstance(proxy)) {
retVal = proxy;
}
}
return retVal;
}
// 如果当前被调用的方法不是被引入增强的,则继续拦截器链执行
return doProceed(mi);
}
逻辑比较简单,先判断当前执行的方法是不是属于引入的接口的方法。
如果是,则直接调用目标方法,AopUtils.invokeJoinpointUsingReflection
。也就是直接执行fly方法。
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
try {
// 反射调用目标方法
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
如果不是,则doProceed
,继续执行拦截器链后续部分。
protected Object doProceed(MethodInvocation mi) throws Throwable {
// If we get here, just pass the invocation on.
return mi.proceed();
}
DelegatingIntroductionInterceptor本身就是一个MethodInterceptor,所以跟之前拦截器链执行的逻辑是一样,只不过是多了一层判断,即当前被调用方法是不是被引入增强的方法。 画了个逻辑图帮助全局把控:
小细节
addAdvice的顺序问题
不知道大家注意到了没有,当DelegatingIntroductionInterceptor#invoke执行时,如果当前被执行方法是被引入增强的,则会直接调用目标方法,然后返回!返回会有什么问题呢?返回就意味着拦截器链执行到头了,如果后续还有MethodInterceptor,那么是执行不到的,拦截器链就断了。 现在我把之前addAdvice的顺序调换一下,变成
proxyFactory.addAdvice(new LogInterceptor());
// 提升FlyableIntroductionInterceptor的add顺序
proxyFactory.addAdvice(new FlyableIntroductionInterceptor());
proxyFactory.addAdvice(new TimeInterceptor());
proxyFactory.addAdvice(new AOPAfterReturningAdvice());
proxyFactory.addAdvice(new AOPMethodBeforeAdvice());
再来看一下输出
开始执行
计时开始
Before...
miao miao miao...
AfterReturning ....
计时结束,耗时:0
执行完毕
============
开始执行
fly fly fly...
执行完毕
可以看到,对于fly方法的执行,TimeInterceptor、AOPAfterReturningAdvice和AOPMethodBeforeAdvice都不会被执行到,拦截器链戛然而止。 spring是如何解决这种情况的呢? 这里先大概说一下,后续解读Advisor时会再详说。 首先获取到的拦截器链是List结构,有序。然后就是spring会保证IntroductionInterceptor会处在拦截器的最后(通过设置最低优先级来实现),而不是我们测试时候自己按照随意顺序去add的。这样就可以保证我们调用引入增强方法之前,所有的MethodInterceptor都已执行完毕。
ProxyFactory的addInterface和setInterfaces
不知道大家主要本篇测试类的写法没有:
for (Class<?> ifc : Cat.class.getInterfaces()) {
proxyFactory.addInterface(ifc);
}
在搞懂AOP之一,拦截器链中的测试类我们是这样写的:
proxyFactory.setInterfaces(Dog.class.getInterfaces());
这两种写法有什么区别呢?
AdvisedSupport(ProxyFactory的父类,后续会详细分析)
public void setInterfaces(Class<?>... interfaces) {
Assert.notNull(interfaces, "Interfaces must not be null");
// 先清空当前的interfaces
this.interfaces.clear();
for (Class<?> ifc : interfaces) {
addInterface(ifc);
}
}
###################
public void addInterface(Class<?> intf) {
Assert.notNull(intf, "Interface must not be null");
if (!intf.isInterface()) {
throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface");
}
if (!this.interfaces.contains(intf)) {
this.interfaces.add(intf);
adviceChanged();
}
}
总结来说,ProxyFactory里维护了一个List,即List<Class<?>> interfaces = new ArrayList<>();
,这里面存放了代理类需要实现的接口,供创建代理类时使用。setInterfaces
方法会以入参值为最终interfaces,因为有一步clear操作。addInterface
方法会将入参值add进当前interfaces中。
如果我们先执行了addInterface
操作,再执行setInterfaces
操作,那么之前addInterface
就被冲掉了。
再回到我们的测试类中,主要关注这一步:
proxyFactory.addAdvice(new FlyableIntroductionInterceptor());
这里最终会调用到
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
if (advisor instanceof IntroductionAdvisor) {
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
}
addAdvisorInternal(pos, advisor);
}
对于FlyableIntroductionInterceptor,会进入到if内部,也就是执行validateIntroductionAdvisor
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
advisor.validateInterfaces();
// If the advisor passed validation, we can make the change.
Class<?>[] ifcs = advisor.getInterfaces();
for (Class<?> ifc : ifcs) {
addInterface(ifc);
}
}
可以看到,这里会把引入增强的接口fly添加到ProxyFactory所维护的代理需要实现的接口中,也就是List<Class<?>> interfaces = new ArrayList<>();
。这也是为什么不采用proxyFactory.setInterfaces(Cat.class.getInterfaces());
写法的原因,因为会把fly冲掉嘛。这样生成的代理类就不会实现Flyable接口了。
我们这里先不管Advisor是个啥东西,可以先简单理解成就是切面的一种实现,也可以当MethodInterceptor去看,后续会专门探讨下Advisor、Advice和MethodInterceptor。
后续详细解析源码的过程中,我们将会看看spring是怎么处理这种情况的。
总结
本篇介绍了spring对AOP中的引入增强的实现,并举例说明如何使用IntroductionInterceptor。现在,我们应该已经知道了怎么定义增强,并把增强应用到代理对象。以及被增强过的代理对象在执行被切面切到的方法时,拦截器链是怎么工作的。 接下来,我们将继续分析: 1.proxyFactory.addAdvice做了什么事情?从而引出Advice和Advisor的牵连。 2.proxyFactory.getProxy做了什么事情?jdk代理对象和cglib代理对象是如何生成的。