SpringAOP中AspectJ怎么用
创始人
2024-12-24 19:22:03
0次

AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切点表达式支持。因为Spring1.0的时候Aspectj还未出现;

AspectJ1.5中新增了对注解的支持,允许直接在Bean类中定义切面。新版本的Spring框架建议我们都使用AspectJ方式来开发AOP,并提供了非常灵活且强大的切点表达式 ;

当然无论使用Spring自己的AOP还是AspectJ相关的概念都是相同的;

注解配置

依赖导入:

 org.springframework  spring-aspects  5.2.2.RELEASE

通知类型

@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依赖:

         org.springframework      spring-context      5.2.2.RELEASE              org.springframework      spring-aspects      5.2.2.RELEASE                  org.springframework      spring-test      5.2.2.RELEASE      test                  org.springframework      spring-test      5.2.2.RELEASE      test                  junit      junit      4.13      test      

xml需要添加aop名称空间及xsd:

     

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;@Aspectpublic class MyAspect {   //表示PersonDao下所有方法都作为切点  @Before(value = "execution(* com.yh.demo1.PersonDao.*(..))")  public void beforeAdvice(){    System.out.println("before code run.....");  }}

当我们需要获取切点信息(被增强的代码)时,可以在通知添加参数,想下面这样

@Aspectpublic 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:

                                                                                   

通知类:

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;

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...