深入SpringAOP实现流程(源码分析)
系列文章目录
SpringAOP实现
深入SpringAOP实现流程(源码分析)
使用递归算法+责任链模拟AOP底层通知调用
文章目录
- 系列文章目录
- 一、SpringAOP实现(源码追踪)
- 1.@EnableAspectJAutoProxy 开启AOP
- 2.底层引用类:@Import(AspectJAutoProxyRegistrar.class)
- 3.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)注册切面类
- 4.AnnotationAwareAspectJAutoProxyCreator注册
- 5.Bean初始化的时候在后置通知帮我们做一些处理
- 6.wrapIfNecessary()判断对象是否在我们切面的扫包范围之内
- 7.createProxy()方法去帮我们创建代理类
- 8.当我们调用目标方法的时候就会执行到我们代理类的invok()方法
- 9.用集合存放通知+责任链去循环调用
- 总结
一、SpringAOP实现(源码追踪)
1.@EnableAspectJAutoProxy 开启AOP
我们都知道创建一个切面类要想生效的话必须加上此注解
@EnableAspectJAutoProxy //开启AOP]
public class LogAOP {
2.底层引用类:@Import(AspectJAutoProxyRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {/*** Indicate whether subclass-based (CGLIB) proxies are to be created as opposed* to standard Java interface-based proxies. The default is {@code false}.*/boolean proxyTargetClass() default false;/*** Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.* Off by default, i.e. no guarantees that {@code AopContext} access will work.* @since 4.3.1*/boolean exposeProxy() default false;}
3.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)注册切面类
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {/*** Register, escalate, and configure the AspectJ auto proxy creator based on the value* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing* {@code @Configuration} class.*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if (enableAspectJAutoProxy != null) {if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}}
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);去注册我们的切面类 怎么注册的呢?我们往下看!
4.AnnotationAwareAspectJAutoProxyCreator注册
点进去
@Nullableprivate static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if (!cls.getName().equals(apcDefinition.getBeanClassName())) {int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);if (currentPriority < requiredPriority) {apcDefinition.setBeanClassName(cls.getName());}}return null;}RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}
我们看一下注册进去的bean:registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition):
beanDefinition是对我们的AnnotationAwareAspectJAutoProxyCreator封装,那么Name是什么我们看一下
public static final String AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator";
所以就以 "org.springframework.aop.config.internalAutoProxyCreator"为beanName然后把我们的切面类AnnotationAwareAspectJAutoProxyCreator注册到IOC中
5.Bean初始化的时候在后置通知帮我们做一些处理
@Overridepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}
6.wrapIfNecessary()判断对象是否在我们切面的扫包范围之内
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;}if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.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;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}
如果在的话Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);会拿到我们的五个通知
7.createProxy()方法去帮我们创建代理类
@Overridepublic 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);}}
可以看到会判断我们的被代理类是否有接口,如果有的话采用JDK动态代理的方式(JdkDynamicAopProxy(config))去帮我们创建代理类,如果没有就用CGLIB动态代理方式( ObjenesisCglibAopProxy(config))帮我们创建
8.当我们调用目标方法的时候就会执行到我们代理类的invok()方法
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 {if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {// The target does not implement the equals(Object) method itself.return equals(args[0]);}else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {// The target does not implement the hashCode() method itself.return hashCode();}else if (method.getDeclaringClass() == DecoratingProxy.class) {// There is only getDecoratedClass() declared -> dispatch to proxy config.return AopProxyUtils.ultimateTargetClass(this.advised);}else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {// Service invocations on ProxyConfig with the proxy config...return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}Object retVal;if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// Get as late as possible to minimize the time we "own" the target,// in case it comes from a pool.target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);// Get the interception chain for this method.List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// Check whether we have any advice. If we don't, we can fallback on direct// reflective invocation of the target, and avoid creating a MethodInvocation.if (chain.isEmpty()) {// We can skip creating a MethodInvocation: just invoke the target directly// Note that the final invoker must be an InvokerInterceptor so we know it does// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {// We need to create a method invocation...MethodInvocation invocation =new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// Proceed to the joinpoint through the interceptor chain.retVal = invocation.proceed();}// Massage return value if necessary.Class<?> returnType = method.getReturnType();if (retVal != null && retVal == target &&returnType != Object.class && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// Special case: it returned "this" and the return type of the method// is type-compatible. Note that we can't help if the target sets// a reference to itself in another returned object.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()) {// Must have come from TargetSource.targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}
9.用集合存放通知+责任链去循环调用
这块的具体我们在下一篇博客详细讲解
总结
以上就是我们追踪源码的SpringAOP的流程
深入SpringAOP实现流程(源码分析)相关推荐
- yaf php源码,PHP-Yaf执行流程-源码分析
介绍 Yaf框架是通过PHP扩展实现的一种PHP MVC开发框架,因为Yaf框架是PHP扩展实现的,所以性能非常高,而且通过阅读源码可以深入的了解框架内部实现细节和一些优秀的扩展开发技巧,但是学习Ya ...
- 跟随一笔交易来看以太坊c++客户端源码执行流程 / 源码分析
本文初步分析了一个交易在以太坊内部的处理流程,涉及到交易的接收,检查,执行,同步,区块的构建以及挖矿,结合前面一篇基于黄皮书的理解总结,对以太坊有了更多的认识.因为主要的工作在c++层面,所以这里以c ...
- springMvc的执行流程(源码分析)
1.在springMvc中负责处理请求的类为DispatcherServlet,这个类与我们传统的Servlet是一样的.我们来看看它的继承图 2. 我们发现DispatcherServlet也继承了 ...
- 【ElasticSearch】Es 启动流程 初始化流程 源码分析
文章目录 1.概述 1.1 核心类 2. 主要流程 2.1 主方法 2.1.1 关闭过程分析 2.2 execute 方法 2.3 Bootstrap.init 2.4 INSTANCE.setup方 ...
- 【ElasticSearch】Es 启动流程 源码分析
文章目录 1. 概述 2. start方法 2.1 启动生命周期相关的组件 2.2 启动IndicesService 2.3 IndicesClusterStateService启动 2.4 Snap ...
- DRF基本使用及执行流程分析 | APIView源码分析
DRF基本使用及执行流程分析 介绍: # 使用的都是CBV的方式 ,继承的类为drf提供的类(提供的类很多) # 这里目前继承使用APIView类 # 因为APIView是所有类的基类,其他类可能拓展 ...
- Nginx源码分析:master/worker工作流程概述
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的master与worker工作模式 在生成环境中的Nginx启动模式基本都是以m ...
- Nginx源码分析:启动流程
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> nginx简介 Nginx的作为服务端软件,表现的主要特点是更快.高扩展.高可靠性.低内存消 ...
- djangorestframework源码分析2:serializer序列化数据的执行流程
djangorestframework源码分析 本文环境python3.5.2,djangorestframework (3.5.1)系列 djangorestframework源码分析-serial ...
最新文章
- 自己写分布式配置中心(上篇)- 单机模式
- urllib使用cookies(下载,提取)
- TLD(Tracking-Learning-Detection)学习与源码理解之(四)
- eclipse调试报错,无法进入类
- Objective-C MacOS以管理员权限运行程序
- Web Bundles 学习笔记
- Linux下面makefile编写
- 孪生再世代表数字几_《孪生双鱼座》
- 继承(继承父类,super)
- 【Java】Java垃圾回收机制
- Nginx学习日记第五篇 -- upstream及fastcgi
- sqlplus 执行sql文件_详解sqlplus设定行大小、页大小、字符列格式、数字列格式、清屏...
- python导入第三方库dlib报错解决
- CentOS-6.3安装配置Tomcat-7
- java开发之权限管理详解
- qt unicode转gbk_Qt中文编码和QString类Unicode编码转换
- 社交类APP原型模板分享——Tinder
- 安装算量软件使用_鹏业安装算量软件常用按钮汇总(三)
- 多线程写法 与老虎机的制作
- PHP 小程序中微信支付
热门文章
- 汉文博士 0.5.6.2352 修订版发布
- 有趣的历史,据说都是真的
- 关于前后端分离 的腹黑意淫
- Android-微信支付-记录自己遇到的坑- -!
- 系统集成项目管理工程师2022年下半年下午案例分析题及答案
- Python真的是白学了
- android电量计电量跳变问题,Android电池电量跳变
- 麦当劳开启派Day活动庆祝国际圆周率日;阿科玛在张家港、常熟、上海共增资10亿元人民币 | 美通企业日报...
- (一)区块链钱包之生成助记词
- wiz文件打开方式(只要notepad++即可)