Spring AOP 基于AspectJ
简介
AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切点表达式支持。因为Spring1.0的时候Aspectj还未出现;
AspectJ1.5中新增了对注解的支持,允许直接在Bean类中定义切面。新版本的Spring框架建
议我们都使用AspectJ方式来开发AOP,并提供了非常灵活且强大的切点表达式 ;
当然无论使用Spring自己的AOP还是AspectJ相关的概念都是相同的;
注解配置
依赖导入:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
通知类型
首先需要创建一个普通类作为通知类,@AspectJ用于标注其属于通知类,见下面案例:
@Before 前置通知 在原始方法执行前执行
@AfterReturning 后置通知 在原始方法执行后执行
@Around 环绕通知 彻底拦截原始方法的执行,执行前后都可以增加逻辑,也可以不执行原始方法
@AfterThrowing抛出通知,执行原始方法出现异常时执行
@After 最终final通知,不管是否异常,原始方法调用后都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (了解即可)
定义切点
通过execution函数来定义切点
语法:execution(访问修饰符 返回类型 方法名 参数 异常)
表达式示例:
匹配所有类public方法:execution(public * *(..))
第一个*表示返回值 ..表示任意个任意类型的参数
匹配指定包下所有类所有方法: execution(* cn.xxx.dao.*.*(..))
第一个想*表示忽略权限和返回值类型
匹配指定包下所有类所有方法:execution(* cn.xxx.dao..*(..))
包含子包
匹配指定类所有方法: execution(* cn.xxx.service.UserService.*(..))
匹配实现特定接口所有类方法 : execution(* cn.xxx.dao.GenericDAO+.*(..))
匹配所有save开头的方法: execution(* save*(..))
匹配某个指定的方法: execution(* com.yh.dao.StudentService.save(..))
前置通知
pom依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
xml需要添加aop名称空间及xsd:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 启用aspectj -->
<aop:aspectj-autoproxy/>
<!-- 目标-->
<bean id="personDao" class="com.yh.demo1.PersonDao"/>
<!-- 切面-->
<bean class="com.yh.demo1.MyAspect"/>
</beans>
test:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test1 {
@Autowired
PersonDao personDao;
@Test
public void test(){
personDao.delete();
personDao.update();
}
}
切面类:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
//表示PersonDao下所有方法都作为切点
@Before(value = "execution(* com.yh.demo1.PersonDao.*(..))")
public void beforeAdvice(){
System.out.println("before code run.....");
}
}
当我们需要获取切点信息(被增强的代码)时,可以在通知添加参数,像下面这样
@Aspect
public class MyAspect {
@Before(value = "execution(* com.yh.demo1.PersonDao.*(..))")
public void beforeAdvice2(JoinPoint point){
System.out.println("before code run2....." + point);
}
}
后置通知:
//当需要获取原始方法的返回值时可以在注解中添加returning参数来指定参数名 Aspectj会自动将返回值放到参数中
@AfterReturning(value = "execution(* com.yh.demo1.PersonDao.delete(..))",returning = "result")
public void afterAdvice(Object result){
System.out.println("删除方法执行后 ..... 返回值为:"+ result);
}
后置通知可以获取目标方法的返回值
环绕通知:
@Around(value = "execution(* com.yh.demo1.PersonDao.insert(..))")
public void aroundAdvice(ProceedingJoinPoint point) throws Throwable {
//code............
System.out.println("环绕前置..");
//执行原始方法 __当需要获取返回值时可以声明变量接收
Object result = point.proceed();
System.out.println("原始方法返回值: "+result);
//code............
System.out.println("环绕后置..");
}
环绕通知与其他通知最大的区别在于环绕通知可以控制是否调用原始方法
注意:参数类型必须为ProceedingJoinPoint,否则 无法执行原始方法,
异常通知
@AfterThrowing(value = "execution(* com.yh.demo1.PersonDao.save(..))",throwing = "e")
public void exceptionHandler(JoinPoint point,Exception e){
System.out.println(point + " 方法出现"+e.getMessage()+"异常");
}
当方法中出现时才会执行该通知,若需要获取异常信息,可在注解中添加throwing指定参数名称
我们可以使用环绕+异常通知来处理数据库事务,在环绕中开启事务以及提交事务,异常通知中回滚事务,当然Spring已经对事务进行了封装不需要自己写
最终通知
@After(value = "execution(* *delete(..))")
public void afterRun(){
System.out.println("最终");
}
最终通知叫做after 即调用原始方法之后执行无论原始方法中是否出现异常
而后置叫做afterReturning表示在成功返回后才会执行执行
带有逻辑符的表达式:
在表达式中可以使用户逻辑操运算符,与&&
或||
非!
示例:
/*
execution(* cn.xxx.service.UserDao.insert(..))||execution(* cn.xxx.service.UserDao.delete(..))
execution(* cn.xxx.service.UserDao.*nsert(..))&&execution(* cn.xxx.service.UserDao.inser*(..))
!execution(* cn.xxx.service.UserDao.insert(..))
*/
切点命名
假设有多个通知应用在同一个切点上时,我们需要重复编写execution表达式,且后续要修改切点时则多个通知都需要修改,维护起来非常麻烦,我们可以通过给切点指定名称从而完成对切点的重复使用和统一操作,以提高开发维护效率;
//定义命名切点 方法名称即切点名称
@Pointcut(value = "execution(* com.yh.demo1.PersonDao.save(..))")
private void savePointcut(){}
@Pointcut(value = "execution(* com.yh.demo1.PersonDao.delete(..))")
private void deletePointcut(){}
多个通知应用到同一个切点:
//使用命名切点
@Before(value = "savePointcut()")
public void beforeAdvice(){
System.out.println("before code run.....");
}
//使用命名切点
@Around(value = "savePointcut()")
public void beforeAdvice2(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕前");
point.proceed();
System.out.println("环绕后");
一个通知应用到多个切点
//同一个通知对应多个切点
@After(value = "savePointcut()||deletePointcut()")
public void afterAdvice(){
System.out.println("after code run.....");
}
XML配置
XML配置所需的jar 以及各个对象之间的依赖关以及表达式的写法都是一样的,仅仅是换种方式来写而已;
xml:
<!--目标-->
<bean id="studentDao" class="com.yh.demo2.StudentDao"/>
<!--通知-->
<bean id="advices" class="com.yh.demo2.XMLAdvice"/>
<!--织入信息-->
<aop:config>
<!--切点定义-->
<aop:pointcut id="select" expression="execution(* com.yh.demo2.StudentDao.select(..))"/>
<!--切面定义-->
<aop:aspect ref="advices">
<aop:before method="before" pointcut-ref="select"/>
<aop:after-returning method="afterReturning" pointcut-ref="select" returning="result"/>
<aop:after method="after" pointcut-ref="select" />
<aop:after-throwing method="exception" pointcut-ref="select" throwing="e"/>
<aop:around method="around" pointcut-ref="select"/>
</aop:aspect>
<!--入侵式通知 即通知需要实现指定接口 与aop:aspect选其一 -->
<aop:advisor advice-ref="advice2" pointcut-ref="select"/>
</aop:config>
<!--入侵式通知Bean-->
<bean id="advice2" class="com.yh.demo2.XMLAdvice2"/>
通知类:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.JoinPoint;
public class XMLAdvice {
public void before(JoinPoint pointcut){ System.out.println("前置通知 切点:"+pointcut); }
public void afterReturning(JoinPoint point,Object result){
System.out.println("后置通知 切点:"+point);
}
public void after(JoinPoint point){ System.out.println("最终通知 切点:"+point); }
public void exception(JoinPoint point,Throwable e){
System.out.println("异常通知: " + e+"切点:"+point);
}
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕前");
point.proceed();
System.out.println("环绕后");
}
}
你会发现 ,无论是XML还是注解都不需要手动指定代理,以及目标对象,Aspectj会从切点中获取目标对象信息并自动创建代理;
AspectJ是目前更流行的方式,具体采用XML还是注解需要根据项目具体情况,小组协作开发推荐xml;
Spring AOP 基于AspectJ的更多相关文章
- Spring AOP基于配置文件的面向方法的切面
Spring AOP基于配置文件的面向方法的切面 Spring AOP根据执行的时间点可以分为around.before和after几种方式. around为方法前后均执行 before为方法前执行 ...
- Comparing Spring AOP and AspectJ
AOP 概念 在我们开始之前 , 让我们做一个快速.高级别审查的核心术语和概念 : 方面 — —标准 / 特征代码被分散在多个场所中的应用 , 通常不同于实际的业务逻辑 (例如 , 交易管理) .各方 ...
- 比较 Spring AOP 与 AspectJ
本文翻译自博客Comparing Spring AOP and AspectJ(转载:https://juejin.im/post/5a695b3cf265da3e47449471) 介绍 如今有多个 ...
- Spring AOP 和 AspectJ
现如今有许多个可用的 AOP 库,使用这些库需要能够回答以下问题: 是否与现有的或新的应用程序兼容? 在哪里可以使用 AOP ? 如何迅速与应用程序集成? 性能开销是多少? 在本文中,我们将回答这些问 ...
- 比较分析 Spring AOP 和 AspectJ 之间的差别
面向方面的编程(AOP) 是一种编程范式,旨在通过允许横切关注点的分离,提高模块化.AOP提供方面来将跨越对象关注点模块化.虽然现在可以获得许多AOP框架,但在这里我们要区分的只有两个流行的框架:Sp ...
- Spring aop与AspectJ的区别?
根据我看spring官方文档的理解(不出意外是最正确的答案): ①选择spring的AOP还是AspectJ? spring确实有自己的AOP.功能已经基本够用了,除非你的要在接口上动态代理或者方法拦 ...
- spring aop与aspectj
AOP:面向切面编程 简介 AOP解决的问题:将核心业务代码与外围业务(日志记录.权限校验.异常处理.事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一. AOP应用场景: 日志记录.权 ...
- 曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- Spring AOP and AspectJ AOP 有什么区别?
Spring AOP 基于动态代理方式实现:AspectJ 基于静态代理方式实现.Spring AOP 仅支持方法级别的 PointCut:提供了完全的 AOP 支持,它还支持属性级别的 PointC ...
随机推荐
- Laravel5.5 支付宝手机网站支付的教程
https://segmentfault.com/a/1190000015559571 这篇文章主要介绍了Laravel5.5 支付宝手机网站支付的教程,小编觉得挺不错的,现在分享给大家,也给大家做个 ...
- jq杂项方法/工具方法----trim() html() val() text() attr()
https://www.cnblogs.com/sandraryan/ $.trim() 函数用于去除字符串两端的空白字符.在中间的时候不会去掉. var str = ' 去除字符串左右两端的空格,换 ...
- Tensorflow安装问题: Could not find a version that satisfies the requirement tensorflow pip命令
引言: Tensorflow大名鼎鼎,这里不再赘述其为何物.这里讲描述在安装python包的时候碰到的“No matching distribution found for tensorflow”,其 ...
- sdk uncaught third Error Cannot assign to read only property 'constructor' of object '#<V>' (小程序)
sdk uncaught third Error Cannot assign to read only property 'constructor' of object '#<V>' 在a ...
- Python--day43--增删改查补充和limit以及order by
增删改查补充: 增: 删和改: 查: 其他: limit:(具有分页的功能) 分页:
- codeforce 382 div2 E —— 树状dp
题意:给一棵n个结点的无根树染色,求使每个结点距离为k的范围内至少有一个被染色的结点的总染色方法数目 分析:首先我们定义: 对于结点v, 如果存在一个黑色结点u距离v不超过k,则结点v被“控制” 首先 ...
- gitLab操作规范和项目流程
刚做完一个项目并且艰难得上线,对整个项目流程和gitLab规范 有了一些心得,给新来的同学普及一下. 最先产品会写一篇需求文档,咱们要先看需求文档对项目有一个大致了解,然后产品喊后端.ui.前端 一 ...
- poj1080 - Human Gene Functions (dp)
题面 It is well known that a human gene can be considered as a sequence, consisting of four nucleotide ...
- poj/OpenJ_Bailian - 2528 离散化+线段树
传送门:http://bailian.openjudge.cn/practice/2528?lang=en_US //http://poj.org/problem?id=2528 题意: 给你n长海报 ...
- 开篇 | 揭秘 Flink 1.9 新架构,Blink Planner 你会用了吗?
本文为 Apache Flink 新版本重大功能特性解读之 Flink SQL 系列文章的开篇,Flink SQL 系列文章由其核心贡献者们分享,涵盖基础知识.实践.调优.内部实现等各个方面,带你由浅 ...