Spring AOP AspectJ使用及配置过程解析
作者:yaominghui 发布时间:2023-09-29 00:17:45
这篇文章主要介绍了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*(..))
前置通知
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(..))
*/
2|4切点命名
切点命名
假设有多个通知应用在同一个切点上时,我们需要重复编写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: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;
来源:https://www.cnblogs.com/yangyuanhu/p/12180732.html


猜你喜欢
- 本文实例为大家分享了Java代码对HDFS进行增删改查操作的具体代码,供大家参考,具体内容如下import java.io.File;imp
- Android 双击返回键退出程序的方法总结下面先说说LZ思路,具体如下: 1. 第一种就是根据用户点击俩次的时间间隔去判断是否退出程序;
- 在java中用到的最多的时间类莫过于 java.util.Date了, 由于Date类中将getYear(),getMonth()等获取年、
- 使用范围: 只能作用在方法和构造函数之上@SneakyThrows注解的作用得从java的异常设计体系说起。java中常见的异常有两种:Ex
- 最近有个需求,我需要获取所有同一类型的定时任务的对象,并自动执行。我想的方案是:直接获取某个接口下面所有的实现类的对象集合,方便以后只需要
- 我们在开发中经常用到倒计时的功能,比如发送验证码后,倒计时60s再进行验证码的获取,为了方便以后使用,这里做个记录,讲讲倒计时器的实现。&n
- 意图:想将项目用到的两个dll库文件(CryptEnDe.dll和ICSharpCode.SharpZipLib.dll)一同编译进exe中
- 一、效果实现二、实现思路:1. 正上方的提示区域,用一个类(LockIndicator.java)来实现,自定义view来绘制9个提示图标;
- 前言在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,从而实现定时任务的动态配置,下面就通过一个简单的示例,来实现这个功
- 1 Struts2框架内部执行过程Structs请求过程源码分析参考链接https://www.jb51.net/article/22058
- 这是一个介绍基本异常处理的小例子,包括抛出,捕获,断言,日志。Java异常处理通过5个关键字try、catch、throw、throws、f
- ActionBar的引入方式:有几种,从 Android 3.0(API lever 11) 开始,所有使用 Theme.Holo 主题(或
- 在这篇文章中,我精选了几个比较适合 Java 编码的 IDEA 主题供小伙伴们选择。另外,我自己用的是 One Dark theme 这款。
- C#中,Image为源自 Bitmap 和 Metafile 的类提供功能的抽象基类,也就是说更通用,当我们用Image.FromFile(
- Feign多参数传递及注意的问题这边沿用前面的Eureka,Feign,Service在服务提供者cloud-shop-userservic
- 一. 方法重写在面向对象中,实现多态的必备条件是继承、重写和向上转型,现在我们已经学习了什么是继承。接下来我们再来学习什么是方法重写,这是我
- 前言制作无边框窗口时,系统自带阴影会消失,这时就需要我自己给窗口添加阴影以防止窗口融入背景。添加阴影的方法很简单,直接用effect就可以了
- Java常用类包装类由于Java语言中的基本类型不是面向对象,并不具备对象的性质,实际使用存在很多不便。Java在java.lang包中提供
- 在项目推进中,如果说第一件事是搭Spring框架的话,那么第二件事情就是在Sring基础上搭建日志框架,我想很多人都知道日志对于一个项目的重
- 前言之所以要写这篇关于C#反射的随笔,起因有两个:第一个是自己开发的网站需要用到其次就是没看到这方面比较好的文章。所以下定决心自己写一篇,废