我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring AOP实现原理的示例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring AOP实现原理的示例分析

这篇文章将为大家详细讲解有关Spring AOP实现原理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

什么是AOP

AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。 

而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。 

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。” 

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

AOP使用场景

AOP用来封装横切关注点,具体可以在下面的场景中使用: 

Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

AOP相关概念

方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。 

连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。 

通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice 

切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上 

引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口 

目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO 

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 

织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

Spring AOP组件

下面这种类图列出了Spring中主要的AOP组件

Spring AOP实现原理的示例分析

如何使用Spring AOP

可以通过配置文件或者编程的方式来使用Spring AOP。 

配置可以通过xml文件来进行,大概有四种方式:
1. 配置ProxyFactoryBean,显式地设置advisors, advice, target等
2. 配置AutoProxyCreator,这种方式下,还是如以前一样使用定义的bean,但是从容器中获得的其实已经是代理对象
3. 通过<aop:config>来配置
4. 通过<aop: aspectj-autoproxy>来配置,使用AspectJ的注解来标识通知及切入点 

也可以直接使用ProxyFactory来以编程的方式使用Spring AOP,通过ProxyFactory提供的方法可以设置target对象, advisor等相关配置,最终通过 getProxy()方法来获取代理对象 

具体使用的示例可以google. 这里略去

Spring AOP代理对象的生成

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。下面我们来研究一下Spring如何使用JDK来生成代理对象,具体的生成代码放在JdkDynamicAopProxy这个类中,直接上相关代码:

   public Object getProxy(ClassLoader classLoader) {     if (logger.isDebugEnabled()) {       logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());     }     Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);     findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);     return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }

那这个其实很明了,注释上我也已经写清楚了,不再赘述。 

下面的问题是,代理对象生成了,那切面是如何织入的?
我们知道InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。而通过JdkDynamicAopProxy的签名我们可以看到这个类其实也实现了InvocationHandler,下面我们就通过分析这个类中实现的invoke()方法来具体看下Spring AOP是如何织入切面的。

publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {     MethodInvocation invocation = null;     Object oldProxy = null;     boolean setProxyContext = false;       TargetSource targetSource = this.advised.targetSource;     Class targetClass = null;     Object target = null;       try {       //eqauls()方法,具目标对象未实现此方法       if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){         return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);       }         //hashCode()方法,具目标对象未实现此方法       if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){         return newInteger(hashCode());       }         //Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知       if (!this.advised.opaque &&method.getDeclaringClass().isInterface()           &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {         // Service invocations onProxyConfig with the proxy config...         return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);       }         Object retVal = null;         if (this.advised.exposeProxy) {         // Make invocation available ifnecessary.         oldProxy = AopContext.setCurrentProxy(proxy);         setProxyContext = true;       }         //获得目标对象的类       target = targetSource.getTarget();       if (target != null) {         targetClass = target.getClass();       }         //获取可以应用到此方法上的Interceptor列表       List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);         //如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)       if (chain.isEmpty()) {         retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);       } else {         //创建MethodInvocation         invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);         retVal = invocation.proceed();       }         // Massage return value if necessary.       if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)           &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {         // Special case: it returned"this" and the return type of the method         // is type-compatible. Notethat we can't help if the target sets         // a reference to itself inanother returned object.         retVal = proxy;       }       return retVal;     } finally {       if (target != null && !targetSource.isStatic()) {         // Must have come fromTargetSource.         targetSource.releaseTarget(target);       }       if (setProxyContext) {         // Restore old proxy.         AopContext.setCurrentProxy(oldProxy);       }     }   }

主流程可以简述为:获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下。 

首先,从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现:

public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {           MethodCacheKeycacheKey = new MethodCacheKey(method);           List<Object>cached = this.methodCache.get(cacheKey);           if(cached == null) {               cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(                         this,method, targetClass);               this.methodCache.put(cacheKey,cached);           }           returncached;      }

可以看到实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存。

下面来分析下这个方法的实现:

   publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) {     // This is somewhat tricky... we have to process introductions first,     // but we need to preserve order in the ultimate list.     List interceptorList = new ArrayList(config.getAdvisors().length);       //查看是否包含IntroductionAdvisor     boolean hasIntroductions = hasMatchingIntroductions(config,targetClass);       //这里实际上注册一系列AdvisorAdapter,用于将Advisor转化成MethodInterceptor     AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();       Advisor[] advisors = config.getAdvisors();     for (int i = 0; i <advisors.length; i++) {       Advisor advisor = advisors[i];       if (advisor instanceof PointcutAdvisor) {         // Add it conditionally.         PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;         if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {           //TODO: 这个地方这两个方法的位置可以互换下           //将Advisor转化成Interceptor           MethodInterceptor[]interceptors = registry.getInterceptors(advisor);             //检查当前advisor的pointcut是否可以匹配当前方法           MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();             if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {             if(mm.isRuntime()) {               // Creating a newobject instance in the getInterceptors() method               // isn't a problemas we normally cache created chains.               for (intj = 0; j < interceptors.length; j++) {                 interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));               }             } else {               interceptorList.addAll(Arrays.asList(interceptors));             }           }         }       } else if (advisor instanceof IntroductionAdvisor){         IntroductionAdvisor ia =(IntroductionAdvisor) advisor;         if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {           Interceptor[] interceptors= registry.getInterceptors(advisor);           interceptorList.addAll(Arrays.asList(interceptors));         }       } else {         Interceptor[] interceptors =registry.getInterceptors(advisor);         interceptorList.addAll(Arrays.asList(interceptors));       }     }     return interceptorList; }

 

这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor. 

接下来我们再看下得到的拦截器链是怎么起作用的。

 if (chain.isEmpty()) {         retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);       } else {         //创建MethodInvocation         invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);         retVal = invocation.proceed();       }

从这段代码可以看出,如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行,来看下具体代码

public Object proceed() throws Throwable {     // We start with an index of -1and increment early.     if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {       //如果Interceptor执行完了,则执行joinPoint       return invokeJoinpoint();     }       Object interceptorOrInterceptionAdvice =       this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);          //如果要动态匹配joinPoint     if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){       // Evaluate dynamic method matcher here: static part will already have       // been evaluated and found to match.       InterceptorAndDynamicMethodMatcher dm =         (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;       //动态匹配:运行时参数是否满足匹配条件       if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) {         //执行当前Intercetpor         returndm.interceptor.invoke(this);       }       else {         //动态匹配失败时,略过当前Intercetpor,调用下一个Interceptor         return proceed();       }     }     else {       // It's an interceptor, so we just invoke it: The pointcutwill have       // been evaluated statically before this object was constructed.       //执行当前Intercetpor       return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);     } }

代码也比较简单,这里不再赘述。

关于“Spring AOP实现原理的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring AOP实现原理的示例分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Spring AOP实现原理的示例分析

这篇文章将为大家详细讲解有关Spring AOP实现原理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是AOPAOP(Aspect-OrientedProgramming,面向方面编程),可
2023-05-30

Spring AOP的示例分析

这篇文章主要为大家展示了“Spring AOP的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Spring AOP的示例分析”这篇文章吧。Spring中对AOP的支持Spring中AOP
2023-05-30

Spring AOP底层原理及代理模式实例分析

这篇文章主要介绍“Spring AOP底层原理及代理模式实例分析”,在日常操作中,相信很多人在Spring AOP底层原理及代理模式实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring AOP底
2023-06-30

Spring中IOC和AOP的示例分析

小编给大家分享一下Spring中IOC和AOP的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Spring是一个开源框架,主要实现两件事,IOC(控制反转
2023-05-30

spring aop实现用户权限管理的示例

AOP 在实际项目中运用的场景主要有 权限管理(Authority Management)、事务管理(Transaction Management)、安全管理(Security)、日志管理(Logging)和调试管理(Debugging)
2023-05-30

Spring事务开启原理的示例分析

这篇文章给大家分享的是有关Spring事务开启原理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。在事务配置类上声明@EnableTransactionManagement注解开启事务在事务配置类上定义数
2023-06-14

php数组实现原理的示例分析

这篇文章主要介绍了php数组实现原理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。php有什么特点1、执行速度快。2、具有很好的开放性和可扩展性。3、PHP支持多种
2023-06-14

如何进行Spring源码剖析AOP实现原理

今天就跟大家聊聊有关如何进行Spring源码剖析AOP实现原理,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。前言前面写了六篇文章详细地分析了Spring Bean加载流程,这部分完了
2023-06-02

Spring Boot底层原理实例分析

这篇“Spring Boot底层原理实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring Boot底层原理实例
2023-06-29

链表原理及java实现的示例分析

这篇文章主要介绍了链表原理及java实现的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一:单向链表基本介绍链表是一种数据结构,和数组同级。比如,Java中我们使用的
2023-05-30

Spring数据库连接池实现原理实例分析

这篇“Spring数据库连接池实现原理实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring数据库连接池实现原理
2023-07-04

Java注解机制之Spring自动装配实现原理的示例分析

小编给大家分享一下Java注解机制之Spring自动装配实现原理的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧! Java中使用注解的情况主要在SpringMVC(Spring Boot等),注解实际上相当于一种标
2023-05-31

spring aop底层原理及实现方法

这篇文章主要介绍spring aop底层原理及实现方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!使用要分析spring aop的底层原理,首先要会使用,先创建一个普通maven webapp项目,引入spring
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录