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相关概念
通知(Advice)
通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。它应该应用在某个方法被调用之前?之后?之前和之后都调用?还是只在方法抛出异常时调用?
Spring 切面可以应用5种类型的通知:
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
- 返回通知(After-returning):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
对应注解:
| 注解 | 通知 |
|---|---|
| @After | 通知方法会在目标方法返回或抛出异常后调用 |
| @AfterReturning | 通知方法会在目标方法返回后调用 |
| @AfterThrowing | 通知方法会在目标方法抛出异常后调用 |
| @Around | 通知方法会将目标方法封装起来 |
| @Before | 通知方法会在目标方法调用之前执行 |
连接点(Join point)
连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
切点(Pointcut)
如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处” 。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。有些AOP框架允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知。
切面(Aspect)
通知+切点=切面
引入(Introduction)
引入允许我们向现有的类添加新方法或属性
织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:
* 编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
* 类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。
* 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。
Spring 对 AOP 的支持:
- 基于代理的经典Spring AOP;
- 纯POJO切面(4.x版本需要XML配置);
- @AspectJ注解驱动的切面;
- 注入式AspectJ切面(适用于Spring各版本)
前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。也就是说,AspectJ才是王道。 另外在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。如下图所示,代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。直到应用需要被代理的bean时,Spring才创建代理对象。 如果使用的是ApplicationContext的话,在ApplicationContext从BeanFactory中加载所有bean的时候,Spring才会创建被代理的对象。因为Spring运行时才创建代理对象,所以我们不需要特殊的编译器来织入Spring AOP的切面。

实例
public interface Performance(){
public void perform();
}
现在来写一个切点表达式,这个表达式能够设置当 perform() 方法执行时触发通知的调用。
execution(* concert.Performance.perform(..))
//execution:在方法执行时触发
//*:返回任意类型
//concert.Performance:方法所属类
//perform:方法名
//(..):使用任意参数
不仅如此,还可以写的更复杂一点
execution(* concert.Performance.perform(..)&&within(concert.*))
//增加了一个与操作,当concert包下的任意类方法被调用时也会触发
在切点中选择bean
execution(*concert.Performance.perform()) and bean('woodstock')
//限定bean id为woodstock
来个完整的切面
@Aspect
public class Audience{
@Before("execution(**concert.Performance.perform(..))")
public void silenceCellPhones(){
System.out.println("Silencing cell phones");
}
@Before("execution{** concert.Performance.perform{..}}")
public void taskSeats(){
System.out.println("Talking seats");
}
@AfterReturning("execution{** concert.Performance.perform{..}}")
public void applause(){
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("execution{** concert.Performance.perform{..}}")
public void demanRefund(){
System.out.println("Demanding a refund");
}
}
可以简化一下
@Aspect
public class Audience{
//避免频繁使用切点表达式
@Pointcut("execution(** concert.Performance.perform(..))")
public void performance(){}
@Before("performance()")
public void silenceCellPhones(){
System.out.println("Silencing cell phones");
}
@Before("performance()")
public void taskSeats(){
System.out.println("Talking seats");
}
@AfterReturning("performance()")
public void applause(){
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("performance()")
public void demanRefund(){
System.out.println("Demanding a refund");
}
}
XML中声明切面
| AOP配置元素 | 用途 |
|---|---|
<aop:advisor> |
定义AOP通知器 |
<aop:after> |
定义AOP后置通知(不管被通知的方法是否执行成功) |
<aop:after-returning> |
定义AOP返回通知 |
<aop:after-throwing> |
定义AOP异常通知 |
<aop:around> |
定义AOP环绕通知 |
<aop:aspect> |
定义一个切面 |
<aop:aspectj-autoproxy> |
启用@AspectJ注解驱动的切面 |
<aop:before> |
定义一个AOP前置通知 |
<aop:config> |
顶层的AOP配置元素。大多数的aop:*元素必须包含在aop:config元素内 |
<aop:declare-parents> |
以透明的方式为被通知的对象引入额外的接口 |
<aop:pointcut> |
定义一个切点 |
来个例子
public class Audience{
public void silenceCellPhones(){
System.out.println("Silencing cell phones");
}
public void taskSeats(){
System.out.println("Talking seats");
}
public void applause(){
System.out.println("CLAP CLAP CLAP!!!");
}
public void demandRefund(){
System.out.println("Demanding a refund");
}
}
通过XML将无注解的Audience声明为切面
<aop:config>
<aop:aspect ref="audience">
<aop:before
pointcut ="execution(** concert.Performance.perform(..))"
method="sillenceCellPhones"/>
<aop:before
pointcut ="execution(** concert.Performance.perform(..))"
method="taskSeats"/>
<aop:after-returning
pointcut ="execution(** concert.Performance.perform(..))"
method="applause"/>
<aop:After-throwing
pointcut ="execution(** concert.Performance.perform(..))"
method="demanRefund"/>
</aop:aspect>
</aop:config>
AspectJ 关于Spring AOP 的AspectJ 切点,最重要的一点就是 Spring 仅支持AspectJ 切点指示器(pointcut designator)的一个子集。让我们回顾下,Spring是基于代理的,而某些切点表达式是与基于代理的AOP无关的。下表列出了 Spring AOP 所支持的 AspectJ 切点指示器。
Spring 借助 AspectJ 的切点表达式语言来定义 Spring 切面
| AspectJ指示器 | 描 述 |
|---|---|
| arg() | 限制连接点匹配参数为指定类型的执行方法 |
| @args() | 限制连接点匹配参数由指定注解标注的执行方法 |
| execution() | 用于匹配是连接点的执行方法 |
| this() | 限制连接点匹配AOP代理的bean引用为指定类型的类 |
| target | 限制连接点匹配目标对象为指定类型的类 |
| @target() | 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解 |
| within() | 限制连接点匹配指定的类型 |
| @within() | 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时,方法定义在由指定的注解所标注的类里) |
| @annotation | 限定匹配带有指定注解的连接点 |
Spring学习之旅(五)--AOP的更多相关文章
- Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探
由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...
- Spring学习之旅(十)--MockMvc
在之前的 Spring学习之旅(八)--SpringMVC请求参数 我们是通过在控制台输出来验证参数是否正确,但是这样做实在是太耗时间了,我们今天来学习下 MockMvc,它可以让我们不需要启动项目就 ...
- Spring学习总结(五)——Spring整合MyBatis(Maven+MySQL)一
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中. 使用这个类库中的类, Spring 将会加载必要的MyBatis工厂类和 session 类. 这个类库 ...
- Spring学习之第一个AOP程序
IOC和AOP是Spring的两大基石,AOP(面向方面编程),也可称为面向切面编程,是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP). 在进行 OOP 开发时,都是基于对 ...
- 我的Spring学习记录(五)
在我的Spring学习记录(四)中使用了注解的方式对前面三篇做了总结.而这次,使用了用户登录及注册来对于本人前面四篇做一个应用案例,希望通过这个来对于我们的Spring的使用有一定的了解. 1. 程序 ...
- Spring学习记录(十二)---AOP理解和基于注解配置
Spring核心之二:AOP(Aspect Oriented Programming) --- 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软 ...
- Spring学习笔记IOC与AOP实例
Spring框架核心由两部分组成: 第一部分是反向控制(IOC),也叫依赖注入(DI); 控制反转(依赖注入)的主要内容是指:只描述程序中对象的被创建方式但不显示的创建对象.在以XML语言描述的配置文 ...
- Spring学习(八)AOP详解
文章更新时间:2020/04/06 一.一个例子 在上面的例子中,包租婆的核心业务就是签合同,收房租,那么这就够了,灰色框起来的部分都是重复且边缘的事,交给中介商就好了,这就是 AOP 的一个思想:让 ...
- Spring学习之旅(五)极速创建Spring AOP java工程项目
编译工具:eclipse. 简单说一下,Spring AOP是干嘛的? 假设你创建了一群类:类A,类B,类C,类D.... 现在你想为每个类都增加一个新功能,那么该怎么办呢?是不是想到了为每个类增加 ...
随机推荐
- c语言进阶2-变量的作用域与无参函数
一. 什么是函数 函数是具有特定功能的模块.可以说一个完整的程序其实是由多个函数共同完成的.C语言的全部工作都是由程式各样的函数完成的,所以也把C语言称为函数式语言.使用模块化设计可能 使 ...
- SPC 数据分析工具
趁着公司在做QMS软件,自己实现一个简易版,类似minitab的工具. 环境:.net framework 4.0 目前提供功能: 数据存储,载入 计量型控制图:单值移动极差图.均值极差图.均值标准差 ...
- Mybatis generator生成工具简单介绍
Mybatis generator 其主要的功能就是方便,快捷的创建好Dao,entry,xml 加快了开发速度,使用方面根据其提供的规则配置好就OK 这里还有一个重要的开发场景,开发过程中,对数据 ...
- Redis(一)--- 概述
使用redis很长时间了,但只是限于使用,一直也没有进行系统的学习与底层研究,现系统的学习一下redis并记录笔记. Redis 1.Nosql 非关系型数据库,key-value键值对存贮. 2.内 ...
- 在工作中常用的Linux命令
前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 之前写过一篇 < 在公司做的项目和自己在学校做 ...
- vue.js-vue入门教程教你如何html中使用vue(30分钟快速入门)
前后端分离.微服务框架是当下比较流行的词汇,而vue就是前端框架的佼佼者.下面重点介绍一下vue的用法: vue起步:1.引包 2.启动new Vue({el:目的地,template:模板内容 ...
- JAVA 基于TCP协议的一对一,一对多文件传输实现
最近老师给我们上了多线程和TCP和UDP协议,其中一个要求就是我们用JAVA协议一个基于TCP和UDP这两种协议的一对一文件上传和一对多文件上传. 然后我就开始分析TCP和UDP这两个协议的特点,发现 ...
- 解决eclipse oxygen+java 10+Tomcat的Could not create the Java virtual machine问题
本文首发于cartoon的博客 转载请注明出处:https://cartoonyu.github.io/cartoon-blog 这个坑我遇到了两次了,所以就写下来以防自己再遇 ...
- rm -rf /*时遇到的趣事
今天在一个linux群里面闲逛的时候,突然看见一个愤青把自己的linux系统给 rm -rf /* 了 ,感觉很好玩就看了一下,突然我发现了有趣的事情! 我的朋友问我,这个为什么显示没有删除,我看了确 ...
- 【Algorithm】插入排序法
通常人们整理桥牌的方法是一张一张的来,将每一张插入到其他已经有序的牌中的适当位置. • 思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的序列的合适位置,直到全部插入排序完为止. Jav ...