简介

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用于标注其属于通知类,见下面案例:

  1. @Before 前置通知 在原始方法执行前执行

  2. @AfterReturning 后置通知 在原始方法执行后执行

  3. @Around 环绕通知 彻底拦截原始方法的执行,执行前后都可以增加逻辑,也可以不执行原始方法

  4. @AfterThrowing抛出通知,执行原始方法出现异常时执行

  5. @After 最终final通知,不管是否异常,原始方法调用后都会执行

  6. @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的更多相关文章

  1. Spring AOP基于配置文件的面向方法的切面

    Spring AOP基于配置文件的面向方法的切面 Spring AOP根据执行的时间点可以分为around.before和after几种方式. around为方法前后均执行 before为方法前执行 ...

  2. Comparing Spring AOP and AspectJ

    AOP 概念 在我们开始之前 , 让我们做一个快速.高级别审查的核心术语和概念 : 方面 — —标准 / 特征代码被分散在多个场所中的应用 , 通常不同于实际的业务逻辑 (例如 , 交易管理) .各方 ...

  3. 比较 Spring AOP 与 AspectJ

    本文翻译自博客Comparing Spring AOP and AspectJ(转载:https://juejin.im/post/5a695b3cf265da3e47449471) 介绍 如今有多个 ...

  4. Spring AOP 和 AspectJ

    现如今有许多个可用的 AOP 库,使用这些库需要能够回答以下问题: 是否与现有的或新的应用程序兼容? 在哪里可以使用 AOP ? 如何迅速与应用程序集成? 性能开销是多少? 在本文中,我们将回答这些问 ...

  5. 比较分析 Spring AOP 和 AspectJ 之间的差别

    面向方面的编程(AOP) 是一种编程范式,旨在通过允许横切关注点的分离,提高模块化.AOP提供方面来将跨越对象关注点模块化.虽然现在可以获得许多AOP框架,但在这里我们要区分的只有两个流行的框架:Sp ...

  6. Spring aop与AspectJ的区别?

    根据我看spring官方文档的理解(不出意外是最正确的答案): ①选择spring的AOP还是AspectJ? spring确实有自己的AOP.功能已经基本够用了,除非你的要在接口上动态代理或者方法拦 ...

  7. spring aop与aspectj

    AOP:面向切面编程 简介 AOP解决的问题:将核心业务代码与外围业务(日志记录.权限校验.异常处理.事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一. AOP应用场景: 日志记录.权 ...

  8. 曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  9. Spring AOP and AspectJ AOP 有什么区别?

    Spring AOP 基于动态代理方式实现:AspectJ 基于静态代理方式实现.Spring AOP 仅支持方法级别的 PointCut:提供了完全的 AOP 支持,它还支持属性级别的 PointC ...

随机推荐

  1. windows上安装redis并安装php5.6的redis扩展

    http://www.884358.com/php-redis/ 1.安装redis Redis 没有官方的Windows版本,但是微软开源技术团队(Microsoft Open Tech group ...

  2. H3C HDLC配置

  3. java 多线程之synchronized wait/notify解决买票问题

    一.Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的st ...

  4. 【9001】Internet消息发布

    Time Limit: 1 second Memory Limit: 256 MB 问题描述 设Internet上有N个站点,通常从一个站点发送消息给其他N-1个站点,需依次发送N-1次.这样从一个站 ...

  5. P1041 查找元素

    题目描述 现在告诉你一个长度为 \(n\) 的有序数组 \(a_1, a_2, ..., a_n\) ,以及 \(q\) 次询问,每次询问会给你一个数 \(x\) ,对于每次询问,你需要确定在数组中是 ...

  6. H3C 主机名与IP地址映射需求

  7. css页面去除滚动条

    注:滚动条是导致页面切换标签页闪动的原因 html{ /*隐藏滚动条,当IE下溢出,仍然可以滚动*/ -ms-overflow-style:none; /*火狐下隐藏滚动条*/ overflow:-m ...

  8. javascript基础之循环

    //while循环 <script type="text/javascript"> i = 1; while (i <= 6) { document.write( ...

  9. Linux 内核USB 驱动

    通用串行总线(USB)是一个在主机和许多外设之间的连接. 最初它被创建来替代许多慢速和不同的总线- 并口, 串口, 和键盘连接--有一个单个的所有设备都可以连接的总线类型.[45] USB 已经成长超 ...

  10. vue-learning:22 - js - directives

    directives 在讲解视图层指令时,我们讲到ref特性,使用它我们可以获取当前DOM元素对象,以便执行相关操作. <div id="app"> <input ...