Spring源码追踪3——AOP机制
研究代码:
spring配置文件
<cache:annotation-driven />
Java代码
@Cacheable(value = "test", key = "#city") public Map load(String city) {}
【cache:annotation-driven机制】
本来以为会有遍历package找类的代码(mybatis那个应该是这么干的),不过实际上只有这个。
org.springframework.cache.config.AnnotationDrivenCacheBeanDefinitionParser#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {String mode = element.getAttribute("mode");if ("aspectj".equals(mode)) {// mode="aspectj" registerCacheAspect(element, parserContext);}else {// mode="proxy" registerCacheAdvisor(element, parserContext);}return null;}
姑且不管aspectj,正常是走proxy模式,注册Advisor。
【拦截机制】
执行方法时通过代理执行。
org.springframework.aop.framework.JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInvocation invocation;Object oldProxy = null;boolean setProxyContext = false;TargetSource targetSource = this.advised.targetSource;Class<?> targetClass = null;Object target = null;try {if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {// The target does not implement the equals(Object) method itself.return equals(args[0]);}if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {// The target does not implement the hashCode() method itself.return hashCode();}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;}// May be null. Get as late as possible to minimize the time we "own" the target,// in case it comes from a pool.target = targetSource.getTarget();if (target != null) {targetClass = target.getClass();}// 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.retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);}else {// We need to create a method invocation...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.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);}}}
通过getInterceptorsAndDynamicInterceptionAdvice获取生效的Interceptor,中间走了个缓存,此处忽略。
org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass) {// This is somewhat tricky... We have to process introductions first,// but we need to preserve order in the ultimate list.List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();for (Advisor advisor : config.getAdvisors()) { // 遍历注册的Advisorif (advisor instanceof PointcutAdvisor) {// Add it conditionally.PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {MethodInterceptor[] interceptors = registry.getInterceptors(advisor);MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { // 判断方法是否适用Advisorif (mm.isRuntime()) {// Creating a new object instance in the getInterceptors() method// isn't a problem as we normally cache created chains.for (MethodInterceptor interceptor : interceptors) {interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));}}else {interceptorList.addAll(Arrays.asList(interceptors));}}}}else if (advisor instanceof IntroductionAdvisor) {IntroductionAdvisor ia = (IntroductionAdvisor) advisor;if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {Interceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}else {Interceptor[] interceptors = registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}return interceptorList;}
最终解析annotation。
spring cache:org.springframework.cache.annotation.AnnotationCacheOperationSource#determineCacheOperations
protected Collection<CacheOperation> determineCacheOperations(AnnotatedElement ae) {Collection<CacheOperation> ops = null;for (CacheAnnotationParser annotationParser : this.annotationParsers) {Collection<CacheOperation> annOps = annotationParser.parseCacheAnnotations(ae);if (annOps != null) {if (ops == null) {ops = new ArrayList<CacheOperation>();}ops.addAll(annOps);}}return ops;}
【advisor的注册】
在spring完成bean创建时经过AbstractAutoProxyCreator类:
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (beanName != null && 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;}
获取specificInterceptors:
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 获取候选advisorsList<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 过滤出可用的advisorsextendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {ProxyCreationContext.setCurrentProxiedBeanName(beanName); try {return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);}finally {ProxyCreationContext.setCurrentProxiedBeanName(null);}}
org.springframework.aop.framework.autoproxy.ProxyCreationContext#setCurrentProxiedBeanName
static void setCurrentProxiedBeanName(String beanName) {if (beanName != null) {currentProxiedBeanName.set(beanName); // ThreadLocal} else {currentProxiedBeanName.remove();}}
org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {if (advisor instanceof IntroductionAdvisor) {return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}}
org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");if (!pc.getClassFilter().matches(targetClass)) {return false;}MethodMatcher methodMatcher = pc.getMethodMatcher();IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}Set<Class<?>> classes = new HashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); // 获取目标类和所有父类classes.add(targetClass);for (Class<?> clazz : classes) {Method[] methods = clazz.getMethods();for (Method method : methods) { // 遍历方法如果有满足的则返回trueif ((introductionAwareMethodMatcher != null &&introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||methodMatcher.matches(method, targetClass)) {return true;}}}return false;}
常用methodMatcher:
1. org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut 事务
2. org.springframework.aop.aspectj.AspectJExpressionPointcut aspectj表达式
创建代理:
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {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);for (Advisor advisor : advisors) {proxyFactory.addAdvisor(advisor);}proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}return proxyFactory.getProxy(getProxyClassLoader());}
转载于:https://www.cnblogs.com/chanedi/p/4552555.html
Spring源码追踪3——AOP机制相关推荐
- Spring源码分析之AOP源码分析
文章目录 前言 一.AOP回顾 二.源码分析 EnableAspectJAutoProxy注解 AnnotationAwareAspectJAutoProxyCreator 前言 Spring框架的两 ...
- Spring 源码分析(三) —— AOP(五)创建代理
2019独角兽企业重金招聘Python工程师标准>>> 创建代理 代理的定义其实非常简单,就是改变原来目标对象方法调用的运行轨迹.这种改变,首先会对这些方法进行拦截,从而为这些方法提 ...
- Spring 源码分析(三) —— AOP(二)Spring AOP 整体架构
2019独角兽企业重金招聘Python工程师标准>>> Spring AOP 架构 先是生成代理对象,然后是拦截器的作用,最后是编织的具体实现.这是AOP实现的三个步 ...
- spring源码分析之Aop
今天读spring源码,读到aop相关内容,在此记录一下,以便以后复习和查阅. 一.spring如何实现Aop 这里简单的说下原理,spring实例化bean要经历一套完整的生命周期,在这个过程中会对 ...
- Spring源码浅析之AOP、Aspect、Advice
前言 理一理AOP与切面(Aspect).通知(Advice)的关系 概念 通知(Advice): AOP 框架中的增强处理.通知描述了切面何时执行以及如何执行增强处理. 连接点(join point ...
- Spring源码分析之Aop中拦截器,适配器,通知之间的关系
首先举一个例子: public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {priv ...
- Spring源码——动态AOP实现流程
前言 最近回顾了一下Spring源码,准备用思维导图的方式简单的将整个源码内容的流程展示出来,思维导图.图片等文件更新在https://github.com/MrSorrow/spring-frame ...
- Spring源码:AOP转文
Spring源码分析-深入浅出AOP(图文分析) https://blog.csdn.net/c_unclezhang/article/details/78769426 Spring中AOP的配置从1 ...
- spring源码阅读--aop实现原理分析
aop实现原理简介 首先我们都知道aop的基本原理就是动态代理思想,在设计模式之代理模式中有介绍过这两种动态代理的使用与基本原理,再次不再叙述. 这里分析的是,在spring中是如何基于动态代理的思想 ...
最新文章
- Spring Boot的exit code
- 文献学习(part39)--Cross-view semantic projection learning for person re-identification
- 【jQuery笔记】新浪微博案例笔记
- mysql查看innodb版本_mysql中查看innodb版本的方法
- asp activex 读取服务器上的文件,webshell中上传asp文件调用服务器ActiveX控件溢出获取shell...
- spring aop中使用@Pointcut注解抽取可重用的切入点表达式
- python 计算机程序设计-程序设计入门—Python
- Mac网易云音乐ncm格式转mp3
- android 免root 安装xposed,xposed框架免root安装|VAExposed(xposed框架免root版本)1.97最新版 - 维维软件园...
- 连接局域网及共享打印机提示操作无法完成(0x00000709)
- 如何教机器学会原研哉(小米新LOGO)的设计理念
- 为图片添加斜体水印并保存水印图片
- 安捷伦(Agilent)示波器使用简介
- 服务器能用usb pe安装win7系统,巧用U盘在win8PE下安装win7系统的教程
- 8 8点阵显示原理c语言,8X8 LED点阵显示原理与编程技术
- xml中处理大于号小于号的方法(Mybatis中大于、小于)
- fence_ipmilan 的一个缺陷
- CC Compare发布,再见了beyoned compare, 用qt自己写了个代码对比同步工具,大家给提提建议呢
- 揭开智能配置上网(微信Airkiss)的神秘面纱
- vue同意本站协议的制作
热门文章
- 数据分析、关键词和地下产业
- 关于bash的shellshock漏洞
- 国外一些知名ASP.Net开源CMS系统
- Team Foundation Server 的 Service Pack 1(中文)也发布了,
- python123第七周小测验_python+request+untitest的接口自动化测试
- iis10.0 php多版本,IIS7 IIS8 中多个版本php共存的方法
- 孪生网络Dataset
- 华为全面屏鸿蒙,华为5G概念新机:真全面屏+鸿蒙OS 这才是旗舰手机
- 用sum函数求三个数和C语言,C语言用函数写两数之和.doc
- android checkbox 选中事件_挖穿Android第四十九天