Spring AOP从原理到源码(三)
2020-04-20 本文已影响0人
李不言被占用了
接着上一节Spring AOP从原理到源码(二),本节关注spring aop创建代理对象的过程。
Spring AOP的ProxyFactory
本文希望传递的是掌握spring aop的方法,而不是抠源码,因为源码本身并没有什么难度,掌握的方法以后,基本上都能看懂。
学习源码的方法一般是从两种:
- 自上而下
- 自下而上
其实这跟我们设计软件的思路是一样的。自上而下可以让你避免抠细节,可以从大局上知道功能的整体情况,整体流程;自下而上则是先了解每个组件的细节以后,再将所有组件整合成完成的功能。两种方法其实各有利弊。
刚开始看spring aop源码的时候也是采用自上而下的,从EnableAspectJAutoProxy
注解开始,了解了aop的整体流程。但是总感觉不够深刻,因为发现里面有很多的细节(概念):Advice
、Advisor
、Pointcut
、Advised
、ProxyConfg
、AspectJ*
(AspectJAfterAfterAdvice
、AspectJAfterReturningAfterAdvice
……)……被搞得有点凌乱
于是决定从ProxyFactory
和ProxyFactoryBean
开始。
先从两段测试代码开始:
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
/**
* <br/>
*
* @author
* @version V1.0
* @email
* @date 2020-04-17 16:41
*/
public class ProxyFactoryTest {
@Test
public void test() {
IMyService target = new MyServiceImpl();
ProxyFactory factory = new ProxyFactory(target);
// 注意这里添加的是Advice
factory.addAdvice(new MyMethodBeforeAdvice());
IMyService proxy = (IMyService) factory.getProxy();
System.out.println(proxy.doService("aop"));
}
@Test
public void test1() {
IMyService target = new MyServiceImpl();
ProxyFactory factory = new ProxyFactory(target);
// 注意这里添加的是Advisor
factory.addAdvisor(new DoServiceMethodPointcutAdvisor());
IMyService proxy = (IMyService) factory.getProxy();
System.out.println(proxy.doService("aop"));
System.out.println("-------------------我是无情的分割线------------------");
System.out.println(proxy.anotherService("你觉得我会被拦截吗?"));
}
}
IMyService.java
public interface IMyService {
String doService(String msg);
String anotherService(String msg);
}
MyMethodBeforeAdvice.java
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(String.format("%s#before正在拦截%s方法", getClass().getSimpleName(), method));
}
}
MyServiceImpl.java
public class MyServiceImpl implements IMyService {
@Override
public String doService(String msg) {
System.out.println("MyServiceImpl#doService方法正在被调用");
return "Hello, " + msg;
}
@Override
public String anotherService(String msg) {
System.out.println("这是MyServiceImpl#anotherService方法正在被调用");
return "Hello, " + msg;
}
}
public class DoServiceMethodPointcutAdvisor implements PointcutAdvisor {
@Override
public Pointcut getPointcut() {
return new Pointcut() {
@Override
public ClassFilter getClassFilter() {
return new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return clazz.equals(MyServiceImpl.class);
}
};
}
@Override
public MethodMatcher getMethodMatcher() {
return new MethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {// 只拦截doService方法
return method.getName().equalsIgnoreCase("doService");
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return false;
}
};
}
};
}
@Override
public Advice getAdvice() {
return new MyMethodBeforeAdvice();
}
@Override
public boolean isPerInstance() {
return false;
}
}
代码详解
接着一行代码带着大家看:
前面说过AOP的核心过程,所以,看源码其实分两步看即可:(划重点、划重点、划重点)
- 代理对象的创建
- 调用方法的拦截。
1. 代理对象的创建
new ProxyFactory(target);
/**
* Create a new ProxyFactory.
* <p>Will proxy all interfaces that the given target implements.
* @param target the target object to be proxied
*/
public ProxyFactory(Object target) {
// 把被代理对象封装到SingletonTargetSource里,没什么特别的逻辑
setTarget(target);
// 拿到被代理对象的所有接口
setInterfaces(ClassUtils.getAllInterfaces(target));
// 以上两段都没什么特别的逻辑,就是保存配置,为什么说是配置呢?
// 因为ProxyFactory是ProxyConfig的子类,它肯定要保存代理相关的配置,才能创建出代理对象
}
这一段的核心就是把被代理的对象保存起来。
factory.addAdvice(new MyMethodBeforeAdvice());
@Override
public void addAdvice(Advice advice) throws AopConfigException {
// 插入一个通知,Advice就是特定连接点上的执行东西
// 此处不必过于抠术语,简单理解就是插入了一个拦截器,拦截器在你执行方法的时候会进行拦截,然后增强
// pos为advice待插入的位置,其实就是末尾!
int pos = this.advisors.size();
addAdvice(pos, advice);
}
/**
* Cannot add introductions this way unless the advice implements IntroductionInfo.
*/
@Override
public void addAdvice(int pos, Advice advice) throws AopConfigException {
Assert.notNull(advice, "Advice must not be null");
/**
* 一点补充知识:
* IntroductionAdvisor类级别的拦截(类中所有方法都会被拦截)。
* PointcutAdvisor可以实现方法级别的拦截。
* DynamicMethodMatcher方便实现根据方法参数拦截
*/
// 这里先不纠结,直接看else
if (advice instanceof IntroductionInfo) {
// We don't need an IntroductionAdvisor for this kind of introduction:
// It's fully self-describing.
addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
}
else if (advice instanceof DynamicIntroductionAdvice) {
// We need an IntroductionAdvisor for this kind of introduction.
throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
}
else {
// 把advice封装成DefaultPointcutAdvisor
/**
* 再次插入一点背景知识:
* Advisor(或者说PointcutAdvisor)是Pointcut+Advice的结合体
* Advice定义在连接点做什么。
* Pointcut是用来匹配切入点的:它能触发类匹配(ClassFilter)+方法匹配(MethodMather)
*/
addAdvisor(pos, new DefaultPointcutAdvisor(advice));
}
}
@Override
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
// 先不关注
if (advisor instanceof IntroductionAdvisor) {
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
}
// 加入到list里
addAdvisorInternal(pos, advisor);
}
private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
Assert.notNull(advisor, "Advisor must not be null");
if (isFrozen()) {
throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
}
if (pos > this.advisors.size()) {
throw new IllegalArgumentException(
"Illegal position " + pos + " in advisor list with size " + this.advisors.size());
}
// 添加到末尾,没啥好说的。
// 不知道出于什么考虑advisors用的是LinkedList
// 不过把advisor添加到末尾倒是效率非常高的,因为LinkedList内部保存了last指针(引用)
this.advisors.add(pos, advisor);
updateAdvisorArray();
// 拦截器发生改变的时候需要把缓存清一下,不然方法被拦截就不正确了
adviceChanged();
}
整一段的核心逻辑就是把Advice
封装成DefaultPointcutAdvisor
加入到List
里。
factory.getProxy();
/**
* Create a new proxy according to the settings in this factory.
* <p>Can be called repeatedly. Effect will vary if we've added
* or removed interfaces. Can add and remove interceptors.
* <p>Uses a default class loader: Usually, the thread context class loader
* (if necessary for proxy creation).
* @return the proxy object
*/
public Object getProxy() {
// 由具体的AopProxy来创建代理对象
return createAopProxy().getProxy();
}
分两段看:
- createAopProxy():
/**
* Subclasses should call this to get a new AOP proxy. They should <b>not</b>
* create an AOP proxy with {@code this} as an argument.
*/
protected final synchronized AopProxy createAopProxy() {
// 当第一个代理被创建的时候active被设置成true,并且通知listener(s)
// 观察者模式
if (!this.active) {
activate();
}
// 真正创建的工作是交给DefaultAopProxyFactory#createAopProxy的,传入的参数是this,代表AdvisedSupport(ProxyConfig)
return getAopProxyFactory().createAopProxy(this);
}
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 做了这些配置,或者被代理对象(target)没有实现接口,就要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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
// 有实现的接口,就用JDK来创建代理
// 需要关注的就是JdkDynamicAopProxy持有config对象和它本身实现了InvocationHandler,
// 所以拦截方法的时候会被调用invoke方法,不明白的先恶补JDK的动态代理原理。
return new JdkDynamicAopProxy(config);
}
}
- getProxy()
这里会有多个实现,此处关注的是JdkDynamicAopProxy#getProxy
。
@Override
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);
// 对equals和hashcode方法进行标记,不是重点
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 创建出代理对象,InvocationHandler指向自己(this),所以拦截方法的时候会触发invoke调用
// 至此,代理对象被创建出来,创建过程就算完成了。
// 剩下的就是关注调用过程的方法拦截了。
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
至此,代理对象已经创建出来了。
小结
简单总结一下:
- 封装被代理对象;
- 添加
Advice
,封装成Advisor
- 创建代理对象。