SpringBoot AOP切面编程
引言
springboot是对原有项目中spring框架和springmvc的进一步封装,因此在springboot中同样支持spring框架中AOP切面编程,不过在springboot中为了快速开发仅仅提供了注解方式的切面编程.
使用
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
相关注解
切面注解
@Aspect 用来类上,代表这个类是一个切面
@Before 用在方法上代表这个方法是一个前置通知方法
@After 用在方法上代表这个方法是一个后置通知方法 @Around 用在方法上代表这个方法是一个环绕的方法
@Around 用在方法上代表这个方法是一个环绕的方法
前置切面
@Aspect
@Configuration
public class MyAspect {
//@Before 代表这是一个核心业务逻辑执行之前附加操作 value:用来书写切入点表达式 配置附加操作在哪里生效
@Before("execution(* com.wdj.springboot_aop.service1.*.*(..))")
public void before(JoinPoint joinPoint) {
System.out.println("=========前置附加操作=========");
System.out.println("当前执行目标类: " + joinPoint.getTarget());
System.out.println("当前执行目标类中方法: " + joinPoint.getSignature().getName());
System.out.println("当前执行目标方法所属类的简单类名: " + joinPoint.getSignature().getDeclaringType().getSimpleName());
System.out.println("当前执行目标方法所属类的类名: " + joinPoint.getSignature().getDeclaringTypeName());
System.out.println("当前执行目标方法声明类型: " + Modifier.toString(joinPoint.getSignature().getModifiers()));
}
}
后置切面
@Aspect
@Configuration
public class MyAspect {
@After("within(com.wdj.springboot_aop.service2.*)")
public void after(JoinPoint joinPoint){
System.out.println("========后置附加操作==========");
System.out.println("当前执行目标类: "+ joinPoint.getTarget());
System.out.println("当前执行目标类中方法: "+ joinPoint.getSignature().getName());
System.out.println("当前执行目标方法所属类的简单类名: " + joinPoint.getSignature().getDeclaringType().getSimpleName());
System.out.println("当前执行目标方法所属类的类名: " + joinPoint.getSignature().getDeclaringTypeName());
System.out.println("当前执行目标方法声明类型: " + Modifier.toString(joinPoint.getSignature().getModifiers()));
}
}
注意: 前置通知和后置通知都没有返回值,方法参数都为joinpoint
环绕切面
@Aspect
@Configuration
public class MyAspect {
//表示被@MyAdvice注释标注的类会执行此环绕方法
@Around("@annotation(com.wdj.springboot_aop.annotations.MyAdvice)")
// 返回值作用: 用来讲业务方法返回结果返回给调用者
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("==========进入环绕的前置操作===========");
//ProceedingJoinPoint继承了JoinPoint
System.out.println("当前执行类: "+proceedingJoinPoint.getTarget());
System.out.println("方法名: "+proceedingJoinPoint.getSignature().getName());
//放行目标方法执行
//还可以更改目标方法的参数
Object proceed = proceedingJoinPoint.proceed();//继续处理 业务逻辑方法执行
System.out.println("==========进入环绕的后置操作===========");
return proceed;
}
}
注意: 环绕通知存在返回值,参数为ProceedingJoinPoint,如果执行放行,不会执行目标方法,一旦放行必须将目标方法的返回值返回,否则调用者无法接受返回数据
自定义注解
//指定运行时生效
//用来控制注解的生命周期
@Retention(RetentionPolicy.RUNTIME)//@Retention作用是定义被它所注解的注解保留多久
//用于描述注解的适用范围 {被描述的注解可以用在什么地方}
@Target(ElementType.METHOD)
//用于描述其他类型的annotation应该被做为被标注的程序成员的公告Api(非必要)
@Documented
public @interface MyAdvice {
}
Spring AOP 切入点表达式
Spring AOP 支持以下用于切入点表达式的 AspectJ 切入点指示符 (PCD):
1、execution: 用于匹配方法执行连接点。这是使用 Spring AOP 时要使用的主要切入点指示符。
* :匹配任何数量字符
..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数
+ :匹配指定类型的子类型;仅能作为后缀放在类型模式后边
//任何公共方法:
execution(public * *(..))
// 任何名称以set开头的方法:
execution(* set*(..))
//AccountService接口定义的任何方法的执行:
execution(* com.xyz.service.AccountService.*(..))
//service包中定义的任何方法的执行:
execution(* com.xyz.service.*.*(..))
//service或其子包之一中定义的任何方法的执行:
execution(* com.xyz.service..*.*(..))
2、within:将匹配限制为某些类型内的连接点(使用 Spring AOP 时在匹配类型中声明的方法的执行)。
注意:within只能指定类,然后该类内的所有方法都将被匹配
//service内的任何连接点(方法只在 Spring AOP 中执行):
within(com.xyz.service.*)
//service或其子包之一中的任何连接点(仅在 Spring AOP 中执行方法):
within(com.xyz.service..*)
//目标对象的声明类型具有@Transactional注释的任何连接点(仅在 Spring AOP 中执行方法):
@within(org.springframework.transaction.annotation.Transactional)
3、this: 限制匹配连接点(使用 Spring AOP 时的方法执行),其中 bean 引用(Spring AOP 代理)是给定类型的实例。
//代理实现AccountService接口的任何连接点(仅在 Spring AOP 中执行方法) :
this(com.xyz.service.AccountService)
4、target: 限制匹配连接点(使用 Spring AOP 时的方法执行),其中目标对象(被代理的应用程序对象)是给定类型的实例。
功能:匹配type类型的目标对象的所有方法。即目标对象可以向上转型为type类型就算是匹配成功
//当前目标对象(非AOP代理对象)实现了 IPointcutService接口的任何方法
target(cn.javass.spring.chapter6.service.IPointcutService)
//当前目标对象(非AOP代理对象) 实现了IIntroductionService 接口的任何方法。不可能是引入接口
target(cn.javass.spring.chapter6.service.IIntroductionService)
5、 args: 限制匹配连接点(使用 Spring AOP 时的方法执行),其中参数是给定类型的实例。
性能消耗比较大,一般不用
//任何带有单个参数的连接点(仅在 Spring AOP 中执行方法),并且在运行时传递的参数是Serializable:
args(java.io.Serializable)
6、@target: 将匹配限制为连接点(使用 Spring AOP 时的方法执行),其中执行对象的类具有给定类型的注释。
//目标对象具有@Transactional注释的任何连接点(仅在 Spring AOP 中执行方法) :
@target(org.springframework.transaction.annotation.Transactional)
7、@args:限制匹配连接点(使用 Spring AOP 时的方法执行),其中传递的实际参数的运行时类型具有给定类型的注释。
//任何带有单个参数的连接点(仅在 Spring AOP 中执行方法),并且传递的参数的运行时类型具有@Classified注释:
@args(com.xyz.security.Classified)
8、@within: 限制匹配到具有给定注解的类型中的连接点(使用 Spring AOP 时,在具有给定注解的类型中声明的方法的执行)。
在这里插入代码片
9、@annotation:将匹配限制为连接点的主题(在 Spring AOP 中运行的方法)具有给定注释的连接点。
//任何连接点(方法仅在 Spring AOP 中执行),其中执行的方法有一个 @Transactional注解:
@annotation(org.springframework.transaction.annotation.Transactional)