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 ...
随机推荐
- python安装easyinstall/pip出错
在Windows中装了python3.6,自然还要装pip.按度娘的提供的方法先下载easyinstall,然后在CMD下输入: python ez_setup.py 结果报错 ........... ...
- 安卓gradle时报错"ERROR: Plugin with id 'com.android.application' not found."
在build.gradle中更改gradle插件版本号 buildscript { repositories { google() jcenter() } dependencies { //版本号请根 ...
- Spring Cloud Hystrix 学习(二)熔断与降级
今天来看下Hystrix的熔断与降级. 首先什么是降级?当请求超时.资源不足等情况发生时进行服务降级处理,不调用真实服务逻辑,而是使用快速失败(fallback)方式直接返回一个托底数据,保证服务链条 ...
- TP6 前一个项目可以,第二个项目配置多域名绑定应用一直失效
之前的域名类似: www.a.com test.a.com edu.a.com ... 文件config/app.php中 'domain_bind' => [ 'test' ...
- gin 源码阅读(1) - gin 与 net/http 的关系
gin 是目前 Go 里面使用最广泛的框架之一了,弄清楚 gin 框架的原理,有助于我们更好的使用 gin. 这个系列 gin 源码阅读会逐步讲明白 gin 的原理. gin 概览 想弄清楚 gin, ...
- ❤️【Android精进之路-03】创建第一个Android应用程序竟然如此简单❤️
您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 本文会重点介绍如何创建第一个Android应用,以及如何使用Android Studio进行调试 干货满满,建议收藏,需要用到时常看看.小伙伴们如 ...
- axios的简单的使用
Axios 是什么? Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中. 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中).在服务端 ...
- P6672-[清华集训2016]你的生命已如风中残烛【结论】
正题 题目链接:https://www.luogu.com.cn/problem/P6672 题目大意 长度为\(m\)的序列\(a\),有\(n\)个数字不是\(0\),其他\(m-n\)个是\(0 ...
- 踩坑系列《十一》完美解决阿里云vod视频点播无法播放音频和视频点播控制台里的媒资库里面的视频无法播放
刚开始项目部署的时候,音频还是正常播放,后面直接报了 获取m3u8文件失败(manifestLoadError) 的错误,原因是 我的域名 xxx.com 这个域名没有解析到点播提供的CNAME上,所 ...
- 细说JUC的线程池架构
前言 线程的创建是需要JVM和OS(操作系统)相互配合的,一次的创建要花费许多的资源. 1.首先,JVM要为该线程分配堆栈和初始化大量内存块,栈内存至少是1MB. 2.其次便是要进行系统的调用,在OS ...