Spring源码解读(二):Spring AOP
一、AOP介绍
面向方面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP)。OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面。方面实现了诸如跨越多种类型和对象的事务管理之类的关注点的模块化。(这种担忧通常被称为AOP文献中的横切关注点。)
Spring的一个关键组件是AOP框架。虽然Spring IoC容器不依赖于AOP,但是如果您不想使用AOP,则意味着您不需要使用AOP,AOP补充了Spring IoC以提供非常强大的中间件解决方案。
带有AspectJ切入点的Spring AOP,通过使用 基于模式的方法 或 @AspectJ注释样式,提供了编写自定义方面的简单而强大的方法 。这两种样式都提供完全类型的advice和使用AspectJ切入点语言,同时仍然使用Spring AOP进行编织。
AOP的相关术语:
1. Aspect 切面
跨越多个类别的关注点的模块化。事务管理是企业Java应用程序中横切关注点的一个很好的例子。在Spring AOP中,方面是通过使用常规类(基于模式的方法)或使用@Aspect 注释(@AspectJ 样式)注释的常规类来实现的。
2. JoinPoint 连接点
程序执行期间的一个点,例如执行方法或处理异常。在Spring AOP中,连接点始终表示方法执行。
3. PointCut 切入点
匹配连接点的谓词。建议与切入点表达式相关联,并在切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。由切入点表达式匹配的连接点的概念是AOP的核心,Spring默认使用AspectJ切入点表达式语言。
4. Advice 增强:特定连接点的某个方面采取的操作。不同类型的建议包括“周围”,“之前”和“之后”建议。许多AOP框架(包括Spring)将建议建模为拦截器并在连接点周围维护一系列拦截器。
① Before(前置增强);
② After(后置增强 ):AfterReturning、AfterThrowing、After(Finally,Spring AOP中没有);
③ Around(环绕增强):
环绕连接点的advice,例如方法调用。这是最有力的advice。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。
AroundAdvice是最普遍的Advice。由于Spring AOP(如AspectJ)提供了全方位的建议类型,因此建议使用可以实现所需行为的最不强大的advice类型。例如,如果您只需要使用方法的返回值更新缓存,那么最好实现AfterReturningAdvice而不是AroundAdvice,尽管AroundAdvice可以完成同样的事情。
④ Introduction(引入增强):
代表类型声明其他方法或字段。Spring AOP允许您向任何建议的对象引入新接口(以及相应的实现)。例如,您可以使用简介使bean实现IsModified接口,以简化缓存。(介绍被称为AspectJ社区中的类型间声明。);
⑤ ControlFlow(流程增强):
Spring控制流切入点在概念上类似于AspectJ cflow切入点,虽然功能较弱。(目前无法指定切入点在另一个切入点匹配的连接点下执行)
控制流切入点与当前调用堆栈匹配。 例如,如果com.mycompany.web包中的方法或SomeCaller类调用了连接点,则可能会触发它。 使用org.springframework.aop.support.ControlFlowPointcut类指定控制流切入点。
5. Target 目标类
由一个或多个方面建议的对象。 也称为“建议对象”。 由于Spring AOP是使用运行时代理实现的,因此该对象始终是代理对象。
6. Proxy 代理类
由AOP框架创建的对象,用于实现切面接口(advice方法executions等)。 在Spring Framework中,AOP代理是JDK动态代理或CGLIB代理。
7. Weaving 织入
将切面与其他应用程序类型或对象链接以创建advice对象。 这可以在编译时(例如,使用AspectJ编译器),加载时或在运行时完成。 与其他纯Java AOP框架一样,Spring AOP在运行时执行编织。
实现方案:
1. RTW(Run-Time Weaving)运行期织入
它是Spring AOP中使用的编织方法。生成代理以执行编织而不修改实际目标对象。在运行时,编织在Method调用完成。虽然有一个优点是不对源文件和类文件进行修改,但是存在一个缺点,即随着应用于切入点的advise数量的增加,性能会提高
2. LTW(Load-Time Weaving)类加载器织入
当使用特殊的ClassLoader将类加载到JVM中时,类如何编写字节序操作与CTW相比,编译时间相对较短,因为它不会像RTW那样操作源文件和类文件。但是,在运行时,时间比CTW慢,因为在对象在内存中时会发生编织。当对象加载到应用程序上下文中时,性能会降低,因为aspectj weaver和spring-instrument会进行对象处理。
3. CTW(Compile-Time Weaving)编译期间织入
AspectJ有一个名为AJC(AspectJ Compiler)的编译器,它是Java Compiler的扩展。通过AJC编译java文件,并在编译期间通过字节代码操作直接插入Advisor代码以执行编织。其中的优点是三种织机中性能最快(当在JVM运行时时,建议代码已经插入到方法中)。
Spring AOP功能和目标
Spring AOP是用纯Java实现的。不需要特殊的编译过程。Spring AOP不需要控制类加载器层次结构,因此适合在servlet容器或应用程序服务器中使用。
Spring AOP目前仅支持方法执行连接点(建议在Spring bean上执行方法)。虽然可以在不破坏核心Spring AOP API的情况下添加对字段拦截的支持,但未实现字段拦截。如果您需要建议字段访问和更新连接点,请考虑使用AspectJ等语言。
Spring AOP的AOP方法与大多数其他AOP框架的方法不同。目的不是提供最完整的AOP实现(尽管Spring AOP非常强大)。相反,目标是在AOP实现和Spring IoC之间提供紧密集成,以帮助解决企业应用程序中的常见问题。
例如,Spring Framework的AOP功能通常与Spring IoC容器一起使用。通过使用普通bean定义语法来配置方面(尽管这允许强大的“自动代理”功能)。这是与其他AOP实现的重要区别。使用Spring AOP无法轻松或高效地完成某些操作,例如建议非常细粒度的对象(通常是域对象)。在这种情况下,AspectJ是最佳选择。但是,我们的经验是Spring AOP为适合AOP的企业Java应用程序中的大多数问题提供了出色的解决方案。
AOP代理
AOP在Spring Framework中用于
提供声明性企业服务,尤其是作为EJB声明性服务的替代品。最重要的此类服务是声明式事务管理。
允许用户实现自定义方面,补充他们使用AOP的OOP。
Spring AOP也可以使用CGLIB代理。这是代理类而不是接口所必需的。默认情况下,如果业务对象未实现接口,则使用CGLIB。由于优化的做法是编程接口而不是类,业务类通常实现一个或多个业务接口。可以强制使用CGLIB,在那些需要建议未在接口上声明的方法或需要将代理对象作为具体类型传递给方法的情况下。
例子
Spring的配置文件 spring-config-filter-method.xml
1 <beans xmlns="http://www.springframework.org/schema/beans"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:context="http://www.springframework.org/schema/context"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans
5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
6 http://www.springframework.org/schema/context
7 http://www.springframework.org/schema/context/spring-context-3.0.xsd">
8
9 <context:component-scan base-package="com.springStyle"/>
10
11 <bean id="userServiceAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
12 <property name="advice" ref="beforeAdvice"/>
13 <property name="patterns">
14 <list>
15 <value>com.springStyle.target.*.save.*</value>
16 <value>com.springStyle.target.*.update.*</value>
17 </list>
18 </property>
19 </bean>
20
21 <bean id="userServiceProxyCglib" class="org.springframework.aop.framework.ProxyFactoryBean">
22 <property name="proxyTargetClass" value="true"/>
23 <property name="target" ref="userService"/>
24 <property name="interceptorNames">
25 <list>
26 <value>userServiceAdvisor</value>
27 </list>
28 </property>
29 </bean>
30
31 <bean id="userServiceProxyJDK" class="org.springframework.aop.framework.ProxyFactoryBean">
32 <property name="interfaces" value="com.springStyle.target.UserService"/>
33 <property name="target" ref="userService"/>
34 <property name="interceptorNames">
35 <list>
36 <value>userServiceAdvisor</value>
37 </list>
38 </property>
39 </bean>
40
41 </beans>
测试代码:
1 @Test
2 public void testFilterMethodJDK() {
3 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext
4 ("springStyle/declaration/manual/spring-config-filter-method.xml");
5 UserService proxy = (UserService) ctx.getBean("userServiceProxyJDK");
6 String ret = proxy.queryAllUser();
7
8 System.out.println("proxyClass:" +proxy.getClass());
9 System.out.println("执行结果:" + ret);
10 proxy.saveUser("zhangsan");
11 }
Spring AOP使用的JDK动态代理,当Sring的上下文环境配置好后,获取AOP的代理对象,JdkDynamicAopProxy其原理还是基于Jdk反射包中的Proxy
1 @Override
2 public Object getProxy(@Nullable ClassLoader classLoader) {
3 if (logger.isTraceEnabled()) {
4 logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
5 }
6 Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
7 findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
8 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
9 }
当使用Aop代理调用saveUser方法时,跳转到代理类JdkDynamicAopProxy的invoke方法
JdkDynamicAopProxy#invoke --> ReflectiveMethodInvocation#proceed --> MethodBeforeAdviceInterceptor#invoke(这里调用了自己的advice方法) --> ReflectiveMethodInvocation#invokeJoinpoint --> ReflectiveMethodInvocation#invokeJoinpoint --> AopUtils#invokeJoinpointUsingReflection --> Method#invoke --> DelegatingMethodAccessorImpl#invoke --> NativeMethodAccessorImpl#invoke --> NativeMethodAccessorImpl#invoke0 --> 自己的saveUserUser方法 --> 结束
Spring源码解读(二):Spring AOP的更多相关文章
- Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- spring源码分析(二)Aop
创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...
- spring源码学习之路---AOP初探(六)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 最近工作很忙,但当初打算学习 ...
- Spring源码解读之BeanFactoryPostProcessor的处理
前言 前段时间旁听了某课堂两节Spring源码解析课,刚好最近自己又在重新学习中,便在这里记录一下学习所得.我之前写过一篇博文,是介绍BeanFactoryPostProcessor跟BeanPost ...
- Spring源码解读--(一)源码下载
走在Java程序员这条路上,网上Java各种工具满天飞,写个简单的CRUD,相信是个开发都能写出来,于是在思考如何可以在同行业中更有竞争力(其实就是如何赚更多钱).那么,老大给我推荐了Spring源码 ...
- spring源码系列(二):IOC接口设计分析
这里主要对springIOC接口体系进行简单的概述和分析,具体每个接口详细分析在下面目录: 参考内容: <Spring技术内幕:深入解析 Spring架构与设计原理> 和 <Spri ...
- Spring源码系列(二)--bean组件的源码分析
简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...
- 剑指Spring源码(二)
这是春节后的第一篇博客,我在构思这篇博客的时候,一度想放弃,想想要不要换个东西写,因为毕竟个人水平有限,Spring源码实在博大精深,不是我这个菜的抠脚的菜鸡可以驾驭的,怕误人子弟,还有就是源码分析类 ...
- 【Spring源码解读】bean标签中的属性
说明 今天在阅读Spring源码的时候,发现在加载xml中的bean时,解析了很多标签,其中有常用的如:scope.autowire.lazy-init.init-method.destroy-met ...
- Spring源码解读:核心类DefaultListableBeanFactory的继承体系
1 简介 我们常用的ClassPathXmlApplicationContext是AbstractRefreshableApplicationContext的子类,而DefaultListableBe ...
随机推荐
- 管理后台界面 详细分析(内含代码 |【前端】)RuoYi
最近在做的一个后台管理 因为关于隐私原因 只方便展示个别页面代码 不会上传项目 注意是前端代码 我把项目代码地址放在最后了 如有需要可自取学习 我会为各位兄弟详细的介绍其中各个属性的含义和用法,记 ...
- 用java代码遍历excel文件并回显
今天需要完成282个指标,分析后发现好多都是可复用的字段和方法,生成的dao类也是很多重复的代码,所以写下了简单的自动化遍历excel的test方法, excel业务逻辑如下,用了 HSSFSheet ...
- Shell系列(7)- 通配符
通配符 通配符 作用 ? 匹配一个任意字符 * 匹配0个或任意多个任意字符,也就是可以匹配任何内容 [] 匹配中括号中任意一个字符.例如:[abc]代表一定匹配一个字符,或者是a,或者是b,或者是c. ...
- pip3 install beautifulsoup4 出现错误 There was a problem confirming the ssl certificate
chenhuimingdeMacBook-Pro:groceryList Mch$ sudo pip3 install beautifulsoup4 The directory '/Users/Mch ...
- 1.3redis小结--配置php reids拓展
1.执行php文件 输出phpinfo(); <?php phpinfo(); 2.根据PHPinfo的信息确定需要下载的 php_redis.dll , php_igbinary.dll 版 ...
- Redis之品鉴之旅(五)
Redis事务 原子性:就是最小的单位 一致性:好多命令,要么全部执行成功,要么全部执行失败 隔离性:一个会话和另一个会话之间是互相隔离的 持久性:执行了就执行了,数据保存在硬盘上 典型例子:银行转账 ...
- 解决联想R720双系统Ubuntu16.04的无线网卡开启问题及信号不稳定
问题一:1.问题描述笔记本型号:Lenovo r720笔记本(i5-7300hq,gtx1060 maxq 6g),默认装入Win10系统,然而当装入Ubuntu16.04双系统时,会出现无线网卡(型 ...
- Winform 实现图片轮播(解决Image.FromFile内存不足)
前言 最近项目中需要在winform中做一个类似于网页那种轮播的效果,这里做下记录. 实现 整体的实现思路如下: 读取图片文件夹. 建立一个集合存储Image对象. 定时器定时更换PictrueBox ...
- Vue插槽slot理解与初体验 ~
一.插槽的理解 1.官网介绍 Vue 实现了一套内容分发的 API,将 <slot> 元素作为承载分发内容的出口. 2.为什么使用插槽 Vue 中有一个重要的概念-组件,可以在开发中将子组 ...
- The Data Way Vol.2 | 做个『单纯』的程序员还真不简单
关于「The Data Way」 「The Data Way」是由 SphereEx 公司出品的一档播客节目.这里有开源.数据.技术的故事,同时我们关注开发者的工作日常,也讨论开发者的生活日常:我们聚 ...