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/aop/spring-aop.xsd"> <!-- 启用aspectj --> <aop:aspectj-autoproxy/> <!-- 目标--> <bean/> <!-- 切面--> <bean/> </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("环绕后置.."); }环绕通知与其他通知最大的区别在于环绕通知可以控制是否调用原始方法