SpringMvc之AspectJ(一)

double

AspectJ是一个基于Java语言的AOP框架,它本身是一个独立的AOP框架,因 Spring 发现它非常好,就把它融入到 spring 体系了,在 Spring2.0 以后新增了对 AspectJ 表达式的支持。其中 @Aspectj 注解是 Aspectj 1.5新增的功能,通过 JDK5 注解技术,允许直接在 Bean 类中定义切面。

本页面相关示例是以 spring boot 2.2.5作为示例的。

引入pom依赖

1.ssm 应用

涉及架包:

  • spring-aop-4.2.4.RELEASE.jar
  • com.springsource.org.aopalliance-1.0.0.jar
  • spring-aspects-4.2.4.RELEASE.jar
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

xml配置:
在 resources 文件夹下添加 applicationContext.xml 文件,获取 beans 头

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 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></aop:aspectj-autoproxy> </beans>

2.spring boot应用

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>

spring boot 只是把以上4个架包进行了二层分装,本质上还是那些。

通知类型

具体有哪些参数可以通过点击该注解查看。

  • @Before: 前置通知,在执行目标方法之前进行增强,相当于 BeforeAdvice
  • @AfterReturning: 后置通知,在执行目标方法之后进行通知或增强,相当于 AfterReturningAdvice
  • @Around: 环绕通知,在执行目标方法之前/之后进行通知或增强,相当于 MethodInterceptor
  • @AfterThrowing: 异常抛出通知,相当于 ThrowAdvice
  • @After: 不管是否有异常,都会执行,类似 finally

一段伪代码读懂执行顺序流程

try { // @Before 执行前通知 // 执行目标方法 // @Around 执行环绕通知 成功走finall,失败走catch } finally { // @After 执行后置通知 // @AfterReturning 执行返回后通知 } catch(e) { // @AfterThrowing 抛出异常通知 }

定义切点表达式语法

通过 execution 函数,可以定义切点的方法切入。

语法:

execution(<访问修饰符> ? <返回类型> <方法名> (<参数>)<异常>)

如:

  • 匹配所有类 public 方法:execution(public * *(. .))
  • 匹配指定包下所有类方法:execution(* com.isyxf.dao.*(. .)) , 这里不不含子包
  • 匹配包、子孙包下所有类:execution(* com.isyxf.dao. . *(. .)),PS: 注意"dao. . *"
  • 匹配指定类所有方法: execution(* com.isyxf.service.UserService.*(. .))
  • 匹配实现特定接口所有类方法:execution(* com.isyxf.dao.GenericDao+.*(. .),PS: 注意 “+” 表示所有该接口实现类的方法
  • 匹配所有 save 开头的方法:execution(* save*(. .))

DEMO

@Component @Aspect public class LogAspectJ2 { /** * 前置通知,监听 MyTest 类下所有方法进行 */ @Before("execution(* com.isyxf.myaop.controller.v1.MyTest.*(..))") public void before() { System.out.println("before通知"); } /** * 后置通知,监听 MyTest 类下,以 po 开头的方法 */ @AfterReturning("execution(* com.isyxf.myaop.controller.v1.MyTest.po*(..))") public void afterRetuning() { System.out.println("afterReturning 通知"); } // 其他用法都按上面格式就可以了 }

只需给类中添加 @Component、@Aspect 然后在每个方法增加对应通知类型注解即可。

@Pointcut 为切点命名

如果同一个方法需要添加多种通知类型,你会发现在需要用 execution 写好多重复的语法,如果数量多的情况会造成工作量大且不易维护,对于这种重复的切点,就可以使用 @Pointcut 进行定义。当通知多个切点时,可以使用 || 进行连接,查看更多

切点方法:

private void 无参数方法,方法名为切点名

@Component @Aspect public class LogAspectJ2 { /** * 定义切点名 */ @Pointcut("execution(* com.isyxf.myaop.controller.v1.MyTest.*(..))") private void myPointcut(){} /** * 前置通知,监听 MyTest 类下所有方法 * 直接使用切点名 */ @Before("myPointcut()") public void before() { System.out.println("before通知"); } /** * 后置通知,监听 MyTest 类下,以 po 开头的方法 * 直接使用切点名 */ @AfterReturning("myPointcut()") public void afterRetuning() { System.out.println("afterReturning 通知"); } }

当基于 XML Sechma 实现 Advice 时,如果 Pointcut 需要被重要,可以使用 < aop:pointcut > </aop:pointcut >来声明 Pointcut,然后在使用这个 Pointcut 的地方,用 pointcut-ref 引用就行了,如:

<aop:config> <!-- 定义切入点: 哪些类的哪些方法需要增强 -->   <aop:pointcut id="log" expression="execution(* com.isyxf.myaop.bean.MessageLog.*(..))"/> <!-- 配置切面 -->   <aop:aspect id="logging" ref="logBeforeAdvice">     <aop:before pointcut-ref="log" method="before"/>     <aop:after-returning pointcut-ref="log" method="afterReturning"/>   </aop:aspect> </aop:config>

获取返回值

只需在注解加一个 returning 属性,然后在定义方法增加返回值作为参数,参数为 Object 类型是因为返回值类型的不确定。

@Component @Aspect public class LogAspectJ { /** * 后置通知,监听 MyTest 类下,以 po 开头的方法 */ @AfterReturning(value = "say()", returning = "result") public void afterRetuning(Object result) { System.out.println("afterReturning 通知" + result.toString()); } }

项目使用场景

  • 通过 @Around 通知注解和 @AfterThrowing 通知注解,可以用来数据库的事务回滚