目录
为什么Spring中存在AOP
什么是面向切面
什么是动态代理
AOP源码解读
总结
凡是学一样东西,要知其先后。对于Spring来说,其中2大核心IoC和AoP。对于IoC来说控制反转,对于项目中的类(bean)你直接交给Spring就行了,你想使用直接从我这里拿。当项目不断的更新迭代,需要在原有的基础上不断的增加逻辑。可能没有AoP之前,对于程序员来说可能会使用代理模式来对其解耦,此时又有啥静态代理,动态代理的。那么,对于Spring的开发人员来说,他们觉得你们项目中的类(bean)已经交给我处理了,那么他们又考虑到项目不断的更新迭代,所以干脆再造一个AoP这玩意,底层帮你们把动态代理给封装好,只需要开发者指定一下切面、切点、通知的一些映射就行,通过几个注解就能完成项目迭代的增强逻辑。
那么什么是面向切面呢?在没接触AoP之前,大家都知道Java是一个面向对象的语言,而Spring又是Java的一个框架。那么面向对象和面向切面他们又有什么瓜果呢?从官方的话来说,面向切面是对面向对象的一个延续,也是一种思想。顾名思义,切面,可以理解为在原有的代码下,我们选择一个切入点,将我们的通知(增强逻辑)给切进原有代码中。在不改变原有代码的情况下,就完成了一些功能的加强。完全的解耦,所以就流行一句面向切面编程。
切面、切点、通知的介绍。这里都是笔者自己的理解
切点:从哪里地方动刀切进去,就是说需要代理的某个方法
通知:代理增强的逻辑
切面:点成线,线程面。所以也就是由1个或者N个切点+通知组成一个面
而面向切面的实现就是基于动态代理来实现。 那么,动态代理又是啥?动...态,就可以理解为某天你的leader叫你把一段日志插入到一段代码中,你使用到代理的思想来实现,此时,就区分静态代理和动态代理,静态代理就需要你手动创建原有类的实现类然后完成增强逻辑,也可以实现,然后你的leader又叫你把其他N个类都做一下增强,那么你用静态代理来实现就需要把N个类都实现然后做增强。此时动态代理说你这样太愚蠢啦,看我的,我直接根据某种技术,直接可以生成一个他的代理类,你只需要把增强的逻辑写好,我通过某种技术生成代理类的时候,帮你把增强逻辑给放入到代理类中,不管你有多少个类需要代理,你只需要把增强逻辑写好就行,其他的交给我。
目前,动态代理的思想,在Java中就有JDK动态代理和Cglib代理。而AoP也是使用到两者做到的面向切面编程,所以想学习AoP是肯定需要有这两技术的支撑。笔者也是有考虑到,事先就把这两者的源码解读早已写好,可以通过下面链接去学习。
从源码角度解读JDK动态代理https://blog.csdn.net/qq_43799161/article/details/123536547?spm=1001.2014.3001.5501从源码角度解读Cglib动态代理https://blog.csdn.net/qq_43799161/article/details/123604338?spm=1001.2014.3001.5501
源码解读之前,做一些必备的推理和猜想,大家可以自行推理和猜想,笔者暂时能想到的猜想如下:
笔者的推理和猜想暂时只有这么多,笔者非常建议大家发散思维,去猜测推理,最后用源码来证实自己!
看到AnnotationAwareAspectJAutoProxyCreator类,从关系图中可以清楚的看到继承了BeanPostProcessor接口,所以能明白,Spring又是靠这个扩展点在创建bean对象的时候来判断是否需要做代理。所以我们直接看到回调的时机和内容吧!
注:这里我很多bean创建的过程跳过了,因为那都是IoC的源码,可以这么说所有Spring全家桶源码的支撑都是容器上下文refresh,建议大家一定要把这方面弄懂!
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 查看当前类是否实现了FactoryBean,
// 实现了就获取到FactoryBean的类型
// 没实现就返回原有bean的名字
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 因为Spring存在三级缓存,并且三级缓存是来处理循环依赖
// 为什么不是二级是三级,因为Aop的出现,所以这里就是检查缓存中是否存在已经生成好的代理对象
// 如果已经生成了代理对象就直接删除,并且返回代理对象,如果不是就去创建
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
大概总结一下上面流程(详细的可以看注释):
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;
}
// 创建代理前的判断
// 并且shouldSkip这个方法中会把所有的切面中的所有切点给加入到缓存中
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 根据当前bean去找到当前bean所对应的所有切点+通知,也就是Pointcut+Advisor
// 最后会通过MethodMatcher的matches去判断当前类中是否存在一个及以上的方法需要被切面做增强
// 并且最后将切点(Pointcut)+通知(Advisor)打包成InstantiationModelAwarePointcutAdvisorImpl
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果没找到就是当前bean没有需要被切面做增强
if (specificInterceptors != DO_NOT_PROXY) {
// 假如缓存判断位
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理
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;
}
这里就开始准备代理了,先判断是否需要被代理,再获取到当前类所对应的所有的切点+通知
shouldSkip()这个方法会对所有的切面做解析,大致流程如下:
getAdvicesAndAdvisorsForBean()方法解析出当前bean的所有切点+通知,大致流程如下:
所以获取到当前bean的切点+通知以后,我们接下来的重心就是如何创建代理了。
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);
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);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
创建出一个ProxyFactory,顾名思义就是代理工厂,用他来创建代理类。工厂创建出物品,是肯定需要其他物品来做原材料,所以这里还把切点,被代理的bean,还有一个工厂创建的属性。不同的属性是肯定影响到工厂产出不同的物品。
所以我们接下来看AoP他是如何选择JDK代理还是Cglib代理的。
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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 {
return new JdkDynamicAopProxy(config);
}
}
这里先判断Optimize,也就是是否优化代理,这个默认为false。所以往下看,判断proxyTargetClass是否为true,这个基本为true,因为他的条件来自于,如果当前bean是否是与目标类一起做代理,这是肯定的。第三个条件是当前bean是否已经指定了是一个接口。
所以也就是说,如果是接口就一定是JDK动态代理,如果是类就一定用Cglib动态代理....(这确实跟没讲一样)
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
try {
Class> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class> proxySuperClass = rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass = rootClass.getSuperclass();
Class>[] additionalInterfaces = rootClass.getInterfaces();
for (Class> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class>[] types = new Class>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
上面代码是Cglib创建代理的过程,因为创建代理无非就是底层通过技术创建字节码文件,并且产生代理类的Class对象,并且通过反射创建代理实例,但是Cglib存在一个FastClass通过索引值直接执行方法(这里不过多讲,在笔者的Cglib文章中有介绍)。所以对产生代理类的过程就不过多说,想学习的可以看笔者对应的文章。
下面就用Cglib的代理来做以说明,因为JDK的动态代理比较容易。并且我们并不是关心Cglib和JDK动态代理如何操作字节码生成代理类。我们关心的是如何做的方法拦截(如何实现的增强),而Cglib和JDK动态代理中的增强方法中都用到了MethodInvocation的proceed()方法。
Callback[] mainCallbacks = new Callback[] {
aopInterceptor, // for normal advice
targetInterceptor, // invoke target without considering advice, if optimized
new SerializableNoOp(), // no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};
这里就是Cglib的全部的方法拦截,也就是增强的回调逻辑,这些Callback方法拦截会通过底层字节码技术通过反射+ThreadLocal维护在代理类中。
我们再通过反编译字节码来反推代理方法是使用的哪一个Callback方法拦截。
所以我们看到0号MethodInterceptor(这个是Callback的子类)对应的DynamicAdvisedInterceptor类中的intercept()方法。
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class> targetClass = (target != null ? target.getClass() : null);
List
直接看到最重要的地方。
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
这里就是对切点对应的方法做增强的逻辑实现,而具体实现,大家可以自行debug,这里大概提一嘴,内部实现使用到递归,根据解析好的通知,因为通知有环绕通知,前置通知,后置通知,异常通知,返回值通知,不同的分支肯定实现是有区别的,也就是递归的先后顺序有变换。
在Aop中,不管是Cglib动态代理还是JDK动态代理都是使用MethodInvocation的proceed()方法来对其做的拦截具体逻辑的实现。
笔者已经完善后续的处理逻辑https://blog.csdn.net/qq_43799161/article/details/124160435
笔者基本上把创建代理前后操作都讲的挺清楚,但是最后的方法拦截逻辑,也就是MethodInvocation的proceed()方法的执行没有去讲,因为内部使用到递归,不是三言二语能讲明白。但是笔者已经介绍完如何走到MethodInvocation的proceed()方法,所以读者可以自行debug了解底层实现。
最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!