首页 > 编程语言 > 详细

spring Aop的学习记录

时间:2021-05-02 22:16:31      阅读:28      评论:0      收藏:0      [点我收藏+]

学习springAop的源码,在看源码之前,思考了几个问题

首先明确几个概念:

springAop,切面编程,对于方法做增强,主要有下面几个概念

切面(Aspect)

由切入点和通知组成,是指对象被横切出来的地方,可做功能增强,是一个比较抽象的概念

通知(Advice)

就是只织入的代码逻辑,一个切面有多个通知,例如前置,后置,环绕,返回后,异常

切入点(Pointcut)

就是切入表达式匹配的位置,切面中所有通知关注的连接点,都是有切入点表达式确定

连接点(Joinpoint)

就是切入点表达式匹配的方法

目标对象:

被一个或多个切面通知的对象,不过aop中一般不会直接操作这个对象,而是操作代理对象

代理对象:

aop中真正面向的对象,是目标对象的代理对象

前置通知,

后置通知,

返回后通知,

环绕通知,

异常通知

1,springAop从什么时候开始

基于之前spring IOC和DI的学习,可以确定,aop一定是在依赖注入之前执行,满足aop的对象一定是织入增强逻辑后放入容器的

2,代码中每个方法与通知直接如何绑定关系

这个首先想到的是用一个map来维护,一个Method实例对应一个map,map内维护着各种通知的关系

3,切面方法如何织入目标方法

这一点可以肯定,一定使用的动态代理来实现的,不论是jdk还是CGlib,一定值操作代理对象,来达到目标对象的功能增强。spring的思想也是如此,不会去操作原生对象,即便是非此处场景,也会将原生对象封装成一个BeanWapper做IOC

4,调用

spring的方法调用,不出意外,就是java反射来实现的

 

源码入口:

spring中有如下接口,提供两个功能,如注释

public interface BeanPostProcessor {
//为在Bean的初始化前提供回调入口
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//为在Bean的初始化之后提供回调入口
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

}

springAop一定是在bean初始化完成后才去做增强,所以看postProcessAfterInitialization方法的实现

AbstractBeanFactory bean工厂中有对BeanPostProcessor的引用,思考应该是从这里,最bean初始化之后的回调入口

技术分享图片

 

找到AbstractBeanFactory的子类AbstractAutowireCapableBeanFactory,ioc中创建实例的方法如下

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
方法内调用了
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)方法

技术分享图片

 

 

 

实现内调用了postProcessAfterInitialization方法

技术分享图片

 

 

 

 至此,源码入口已经可以确认了

这一点和猜测基本相符合

 

第二点,就是具体方法与切面的绑定关系的,这里要从aop创建代理对象开始看,因为绑定关系一定在创建代理对象时被使用了

所以找到了类 AbstractAutoProxyCreator

进入方法org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary

创建aop代理类的代码,此处应该算是aop最核心的内容了

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
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;
}

看到了getAdvicesAndAdvisorsForBean方法,得到的是一个数组,这与一开始的map结构已经不符合了,内部使用的是list,使用list如何是的通知与方法匹配,一定要遍历,不过spring一向处理优雅,
所以思考了下哪种设计模式会适合这个场景,想到了责任链是变成,验证一下

技术分享图片

 

 

 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy方法内部

技术分享图片

 

 

 

proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
上面两个方法,是给AdvisedSupport实例赋值,分别是通知数组和需要增强的对象

 存在方法:

public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

最终调用的是DefaultAopProxyFactory中的方法,判断了是使用CGlib还是JDK动态代理,返回了一个代理类

技术分享图片

 

 

 

看jdk动态代理,存在方法
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}

技术分享图片

 

 

 此处将所有增强赋值成了一个对象AdvisedSupport,其内存在属性

技术分享图片

 

 

jdk的动态代理的核心是实现了InvocationHandler接口,所有的方法调用都是走的invoke方法

看到了chain,果然是责任链模式

技术分享图片

 

org.springframework.aop.framework.AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice,获得当前方法上的通知链

方法内部主要是做遍历判断,检查当前advisor的pointcut是否可以匹配当前方法,匹配的放入列表内,最后返回,所以此次拿到是匹配当前方法的通知列表

org.springframework.aop.framework.ReflectiveMethodInvocation#proceed方法内部使用了递归,存在匹配的,则调用invoke方法

技术分享图片

invoke方法的实现如下,也并非一开始认为的在代理方法前后获取执行,而是执行将前置,后置,环绕等做了不同的实现。

技术分享图片

通过mi.proceed()和invokeAdviceMethod(getJoinPointMatch(), null, ex)的调用顺序来达到前置,后置等实现效果

技术分享图片

最后通过反射,完成目标方法的调用。

技术分享图片 

 

最后总结:org.springframework.aop.framework.AopProxyFactory#createAopProxy创建代理创建类,

技术分享图片

 

最后总结:通过org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy方法拿到org.springframework.aop.framework.AopProxy类的实例, 通过构造方法将实例内的org.springframework.aop.framework.JdkDynamicAopProxy#advised方法赋值完成。

依赖注入的实例通过org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)方法获取,是一个jdk的动态代理实例,所以调用方法是会调用JdkDynamicAopProxy实例内的invoke方法,方法内部,对advised做遍历匹配处理,实现了前后置等逻辑织入的效果。

 

spring容器中 JdkDynamicAopProxy 是多例的,每次代理对象的获取,都会根据当前实例获取到对应的所有通知,每次JdkDynamicAopProxy构造的入参也就不同,invoke内部再做方法级别的匹配。

 

看下来发现切面匹配是使用了责任链的思想,但是不是标准的责任链模式,存在节点匹配关系,但是节点之间并没有关联关系,这点不重要

 

通顺了

 

 






 

spring Aop的学习记录

原文:https://www.cnblogs.com/xianyi/p/14725861.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!