Spring系列(四) 面向切面的Spring
除了IOC外, AOP是Spring的另一个核心. Spring利用AOP解决应用横切关注点(cross-cutting concern)与业务逻辑的分离, 目的是解耦合. 横切关注点是指散布于代码多处的同一种功能, 比如日志, 事务, 安全, 缓存等.
AOP编程的基本概念
在OOP中, 如果要复用同一功能, 一般的做法是使用继承或委托. 继承容易导致脆弱的对象体系, 而委托实现起来比较麻烦, 需要对委托对象进行复杂调用. AOP提供了另外一种思路, 使用AOP我们仍然可以在一个地方定义功能, 并通过声明的方式告知何处以哪种方式使用这个功能. 这样我们既可以对功能做统一管理和维护, 同时也简化了业务逻辑模块, 使其更关注自身的业务逻辑. 此外, AOP还可以将新的功能行为添加到现有对象.
Spring中AOP的术语
- 切面(Aspect): 切面定义了横切关注点的功能以及使用该功能的声明. 它包含了另外两个术语, 通知(Advice, 功能逻辑代码)和切点(Pointcut,声明). 切面定义了它是什么(what), 以及在何时何处(when,where)完成其功能.
- 通知(Advice): 通知定义了切面的具体功能, 以及何时使用.
when,何时使用? 前置(Before), 后置(After), 返回(After-returning), 异常(After-throwing), 环绕(Around)
- 切点(Pointcut): 定义了切面定义的功能在哪里(Where)发生作用, 看起来就像从某个点把切面插入进去一样. 切点应该属于连接点中的一个或多个.
- 连接点(Join point): 定义了程序执行过程中可以应用切面的具体时机, 比如方法调用前, 调用后, 结果返回时, 异常抛出时等, 通常某个具体切面只会选择其中一个或几个连接点作为切点.
- 引入(Introduction): 为现有的类添加新的方法或属性叫引入.
- 织入(Weaving): 织入是把切面应用到目标对象并创建新代理对象的过程.
织入的方式有三种:
- 编译期: 需要特殊的编译器支持, 如AspectJ的织入编译器
- 类加载期: 需要特殊的类加载器ClassLoader
- 运行时: Spring AOP 使用该方式织入. AOP容器为对象动态创建一个代理对象.
Spring 对 AOP的支持
Spring对AOP的支持很多借鉴了AspectJ的方式.
Spring支持四种方式的织入:
- 基于代理的经典AOP; (方式太老旧, 不建议使用)
- 纯POJO切面;(需要XML配置)
- @AspectJ 注解驱动的切面; (没啥说的,很好用)
- 注入式AspectJ切面;
- 前三种都是基于动态代理实现, 因此Spring对AOP的支持局限于方法拦截. 如果前三种满足不了需求(比如拦截构造器方法或者字段修改), 可以使用第四种.
- 与AspectJ不同, Spring的切面就是Java类, Spring使用运行时动态代理, 而AspectJ需要学习特殊的语法以支持特殊的编译器织入.
通过切点来选择连接点
Spring 借鉴了AspectJ的切点表达式语言. 如前所述, Spring基于动态代理,只能在方法上拦截, 所以Spring只支持这个层面的表达式来定义.
spring支持的AspectJ指示器如下, 其中execution来执行匹配, 其他均为限制匹配的.

切点表达式更多使用可以参考官方文档
- spring新增了个bean()指示器
使用注解创建切面
一. 定义切面类, 并用 @Aspect注解, 该注释用来标记这个类是个切面
二. 定义切面的方法(what), 并使用注解标记方法(when), 可用的注解: @Before,@After,@AfterReturning,@AfterThrowing,@Around(功能最强大,后面将单独使用这种通知)
一,二步完成后的代码:
@Aspect
public class Audience{
@Before("execution(** com.xlx.Performance.perform(...))")
public void silencephone(){
System.out.println("silencephone");
}
@Before("execution(** com.xlx.Performance.perform(...))")
public void takeSeats(){
System.out.println("takeSeats");
}
@AfterReturning("execution(** com.xlx.Performance.perform(...))")
public void applause(){
System.out.println("applause");
}
@AfterThrowing("execution(** com.xlx.Performance.perform(...))")
public void refund(){
System.out.println("refund");
}
}
上面的代码中切面表达式被重复定义了四次, 无论如何这已经是重复代码了, 下一步优化一下.
三. 使用注解@Pointcut定义切点
@Aspect
public class Audience{
//定义切点并修改其他方法重用该切点
@Pointcut("execution(** com.xlx.Performance.perform(...))")
public void performance(){
}
@Before("performance()")
public void silencephone(){
System.out.println("silencephone");
}
@Before("performance()")
public void takeSeats(){
System.out.println("takeSeats");
}
@AfterReturning("performance()")
public void applause(){
System.out.println("applause");
}
@AfterThrowing("performance()")
public void refund(){
System.out.println("refund");
}
}
@Aspect注解的类依然是个普通java类, 它可以被装配为bean
@Bean
public Audience getAudience(){
return new Audience();
}
四. 使用@EnableAspectJAutoProxy注解启用自动代理功能, 如果是XML Config ,对应的节点是<aop:aspectj-autoproxy />
@Configuration
@ComponentScan // 包扫描
@EnableAspenctJAutoProxy // 启动自动代理
public class MyConfig{
// 如果Audience上加了@Component就不需要这个代码了
@Bean
public Audience getAudience(){
return new Audience();
}
}
五. 使用环绕通知@Around, 环绕通知同时兼具了@Before,@After ... 等注解的方法的功能, 下面代码演示了这种能力. 如可以使用它记录方法执行时长.
@Aspect
public class Audience{
//定义切点并修改其他方法重用该切点
@Pointcut("execution(** com.xlx.Performance.perform(...))")
public void performance(){
}
@Around("performance()")
public void silencephone(ProcdedingJoinPoint jp){
System.out.println("silencephone");
System.out.println("takeSeats");
try{
// 如果不是刻意为之, 一定要记得调用jp.proceed();否则实际的方法Performance.perform()将会阻塞
jp.proceed();
System.out.println("applause");
}catch(Exception e){
System.out.println("refund");
}
}
}
六. 参数传递 , 在切点表达式中使用args(paramName)结合切点方法可以为切面方法传递参数
@Aspect
public class Audience{
//定义切点并修改其他方法重用该切点
@Pointcut("execution(** com.xlx.Performance.perform(int) && args(actornum)))")
public void performance(int actornum){
}
@Before("performance(actornum)")
public void countActor(int actornum){
System.out.println("countActor"+actornum);
}
}
通过注解引用新功能
除了拦截对象已有的方法调用, 还可以使用AOP来为对象添加新的属性和行为(引入). 其实现就是通过动态代理生成代理类来实现.
一. 定义要添加的功能接口
public interface Encoreable{}
二. 定义切面(引入) @Aspect注解切面类. @DeclareParents注解功能接口静态变量
@Aspect
public class EncoreableIntroducer{
// 可以解释为: 为Performace的所有子类引入接口Encoreable, 并使用默认实现类DefaultEncoreableImpl
@DeclareParents(value="xlx.Performace+",defaultImpl=DefaultEncoreableImpl.class)
public static Encoreable encoreable;
}
基于XML配置的切面
如果没有办法为类添加注解, 比如没有源代码, 那就不得不使用xml来配置了.
- 示例1
<aop:config>
<aop:aspect ref="aspectBean">
<aop:pointcut id="pcId" expression="execution(** com.xlx.Performance.perform(int) and args(actornum)))" />
<aop:before pointcut-ref="pcId" method="count" />
</aop:aspect>
</aop:config>
- 示例2
<aop:config>
<aop:aspect>
<aop:declare-parents type-matching="xlx.Performace+" implement-interface="xlx.Encoreable" delegate-ref="defaultImpl" />
</aop:aspect>
</aop:config>
AspectJ 注入
使用AspectJ注入的方式可以解决使用动态代理无法解决的问题(应该比较少见,大多应用使用Spring AOP就可以实现了), 但需要使用AspectJ的特殊语法. 定义好的类需要用xml配置为bean, 使用factory-method="aspectOf"属性来制定bean的产生方式.
<bean factory-method="aspectOf" class="...ClassName">
<property name="other" ref="otherref"/>
</bean>
Spring系列(四) 面向切面的Spring的更多相关文章
- Spring学习(四)--面向切面的Spring
一.Spring--面向切面 在软件开发中,散布于应用中多处的功能被称为横切关注点(cross- cutting concern).通常来讲,这些横切关注点从概念上是与应用的业 务逻辑相分离的(但是往 ...
- Spring实战第四章学习笔记————面向切面的Spring
Spring实战第四章学习笔记----面向切面的Spring 什么是面向切面的编程 我们把影响应用多处的功能描述为横切关注点.比如安全就是一个横切关注点,应用中许多方法都会涉及安全规则.而切面可以帮我 ...
- Spring使用笔记(四) 面向切面的Spring
面向切面的Spring 一.面向切面的概念 在软件开发中,散布于应用多处的功能被称为横切关注点(cross-cutting concern). 通常来讲这些横切关注带点从概念上来讲是与应用逻辑相分离的 ...
- 第04章-面向切面的Spring
1. 什么是面向切面编程 AOP是什么 切面帮助我们模块化横切关注点. 横切关注点可被描述为影响应用[多处的]功能.如安全,应用许多方法会涉及安全规则. 继承与委托是最常见的实现重用 通用功能 的面向 ...
- 五、面向切面的spring(1)
spring的依赖注入看完了,接下来是spring中与DI一并重要的AOP了,开始吧,GO. 在软件开发中,散布于应用中多处的功能被称为横切发关注点,通常来讲,这些横切关注点从概念上市与应用的业务逻辑 ...
- 面向切面的Spring
在软件开发中,发布于应用中多处的功能被称为横切关注点.通常,这些横切关注点从概念上是与应用的业务逻辑相分离的(但往往直接嵌入到应用的业务逻辑之中).将横切关注点与业务逻辑相分离是AOP所要解决的. 一 ...
- Spring学习笔记(三):面向切面的Spring
Spring之面向切面编程 一.理解何为面向切面编程 对于这个的理解,我觉得Spring实战中的例子讲得很明白: 假设我现在是一个小区用户,每个月小区都要收电费,这时候就会来人查看电表,算出来这个月电 ...
- 2015年12月13日 spring初级知识讲解(四)面向切面的Spring
2015年12月13日 具体内容待补充...
- Spring AOP 面向切面的Spring
定义AOP术语 描述切面的常用术语有: 通知 (advice) 切点 (pointcut) 连接点 (joinpoint) 下图展示了这些概念是如何关联的 Spring 对AOP的支持 Spring提 ...
随机推荐
- 文本分类实战(八)—— Transformer模型
1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...
- SQL Server百万级大数据量删除
删除一个表中的部分数据,数据量百万级. 一般delete from 表 delete from 表名 where 条件: 此操作可能导致,删除操作执行的时间长:日志文件急速增长: 针对此情况处理 de ...
- hotspot目录结构
Hotspot的目录结构 ├─agent Serviceability Agent的客户端实现 ├─make 用来build出HotSpot的各种配置文件 ├─src HotSpot VM的源代码 │ ...
- IntelliJ IDEA 2018.3 重大升级,哪些功能打动了你?
前言 2018.11.28 IntelliJ IDEA 2018.3 正式版发布.对于一个忠实爱好者,迫不及待的我下载了最新版本来体验下.而且 IDEA 今年的第三次重大更新提供了不容错过的显著功能! ...
- django 之 stark组件
----------------------------------------------------------------烦恼没完没了,内心动荡不安,呜呼哀哉. 一.有个特殊的需求,需要用sta ...
- mybatis 使用缓存策略
mybatis中默认开启缓存 1.mybatis中,默认是开启缓存的,缓存的是一个statement对象. 不同情况下是否会使用缓存 同一个SqlSession对象,重复调用同一个id的<sel ...
- vue.js实战——splice使用
Vue在检测到数组变化时,并不是直接重新渲染整个列表,而是最大化地复用DOM元素.替换的数组中含有相同元素的项不会被重新渲染,因此可以大胆地用新数组来替换就数组,不用担心性能问题. 需要注意的是,以下 ...
- Linux--主从复制
一 . mysql+centos7 mariadb mariadb其实是跟mysql是一样的,只不过是在centos7上叫做mariadb, 主要是因为mysql被甲骨文公司收购后,可能会有闭源的风险 ...
- codeforces483B
Friends and Presents CodeForces - 483B You have two friends. You want to present each of them severa ...
- 【深入Java虚拟机】之三:内存溢出
为了更直接的了解各区域,下面我们来看几个示例. 1.Java 堆溢出 下面的程中我们限制Java 堆的大小为20MB,不可扩展(将堆的最小值-Xms 参 数与最大值-Xmx 参数设置为一样即可避免堆自 ...