Spring AOP
AOP
为Aspect Oriented Programming
的缩写
面向切面编程
通过预编译方式和运行期间动态代理实现程序功能编程功能的同一维护
OOP
(面向对象) 的一种延续
可以对业务逻辑的各个部分进行隔离
从而使业务逻辑各部分之间的耦合度降低
提高程序的可重复利用性
提高代码效率
导入AOP
的包文件
pom
文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
在spring
管理容器里面定义表示启动AOP
@Configuration
@ComponentScan("com.jt")
@EnableAspectJAutoProxy// 启动切面编程 让AOP生效
public class SpringConfig {
}
声明一个类为 切面类
@Component //必须让 Spring容器进行管理
@Aspect // 表示该类 是一个切面
public class SpringAOP{}
声明切面 里面必须要有方法
1.切入点表达式
2.通知方法
@Pointcut("bean(userServiceImpl)") //切入点表达式
//判断如果当前执行的目标对象是 输入的id 那么spring会自动创建 代理对象
public void pointcut(){}
如果对象满足切入点表达式
则先执行通知方法 如:前置通知
// 前置通知: 在目标方法之前执行
//传入 目标方法
@Before("pointcut()")//检测到前置通知时 先执行前置通知 再执行目标方法
public void before(){
System.out.println("我是前置通知");
}
后置通知
@AfterReturning("pointcut()")// 后置通知
//目标方法执行完成之后 运行
public void after(){}
异常通知
@AfterThrowing("pointcut()")//异常通知
//目标方法出现错误时 执行
public void afterThrowing(){
System.out.println("我是异常通知");
}
最终通知
@After("pointcut()")//最终通知
// 最后都要执行的方法
public void after(){
System.out.println("我是最终通知");
}
环绕通知
在目标方法前后都要执行
目的是为了控制 目标方法
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("目标方法执行前~~~");
//执行 目标方法
//底层调用动态代理点的 invoke 方法 执行 目标方法
//执行过程中 可能会报错 编译异常 向上抛出 或自己解决try..catch...
Object result = joinPoint.proceed();
System.out.println("目标方法执行后");
return result;
}
默认通过bean
标签去寻找执行的代理对象是哪个
如果写想 代理多个 对象则 逗号拼接
@Pointcut("bean(userServiceImpl ,adminService ,BService)")
//通过 bean 标签去寻找 对应的 id
public void pointcut(){}
within
标签
如果想代理的对象有多个可以使用within
标签
可以使用通配符
@Pointcut("within(com.jt.service.*)")
//代理 com.jt.service 下面的类
//并不是所有的类
public void pointcut(){}
只扫描com.jt.service
包 下面的类
多集子目录写法com.jt.service..*
代理所有的类包括子类里面的子类…
@Pointcut("within(com.jt.service..*)")
//代理 com.jt.service 下面的 所有包下面的所有类
public void pointcut(){}
如果只想管理类里面的某一个方法
使用 execution(返回值类型 包名类名方法名(参数列表))
@Pointcut("execution(* com.jt.service.UserService.addUser())")
// 任意的返回值类型 com.jt.service.UserService 里面的方法addUser
//如果 有一个参数 int a 如下写法
@Pointcut("execution(* com.jt.service.UserService.addUser(int))")
如: com.jt.service
下面的所有类里的所有方法的任意参数
@Pointcut("execution(* com.jt.service..*.*(..))")
通过自定义注解进行拦截
// 注解书写 包路径
@Pointcut("@annotation(com.jt.anno.Demo)")//里面写 注解的类型
通知的用法
可以记录程序执行的各个过程
为日志提供记录
@Before @AfterReturning @AfterThrowing @After
方法里面有一个参数
连接点 获取方法中的数据
public void before(JoinPoint joinPoint) { // 连接点: 获取方法中的数据
Class targetClass = joinPoint.getTarget().getClass();//获取类型
//获取方法,名称
String methodName = joinPoint.getSignature().getName();// 获取方法名
String className = joinPoint.getSignature().getDeclaringTypeName();// 获取类名称
Object[] objs = joinPoint.getArgs();//获取方法携带的参数数组
System.out.println("我是前置通知--");
System.out.println(targetClass);// 类型
System.out.println(methodName);// 方法的名称
System.out.println(className);//类的名称
System.out.println(Arrays.toString(objs));// 方法携带的参数数组
}
获取方法的返回值 利用后置通知
@AfterReturning
@AfterReturning(value = "pointcut()",returning = "result")// 后置通知
public void afterReturning(Object result) {
// 通过 returning 属性 获取目标方法的返回值 当做参数传递给 result
System.out.println(result);// 目标方法的返回值
System.out.println("我是后置通知");
}
获取异常信息
@AfterThrowing
@AfterThrowing(value="pointcut()",throwing = "exception") //获取异常信息
public void afterThrowing(Exception exception) {
System.out.println(exception);
System.out.println("我是异常通知");
}
多个AOP
问题
有多个代理文件会出现执行顺序问题
默认是 目录结构 进行执行
可以通过注解进行排序
@Order(参数)
参数里面填写数字表示第几个执行
@Component
@Aspect
@Order(1)
public class SpringAOP{
}
Spring
中动态代理默认采用的是JDK
代理
如果更改为CGLib
代理 在Spring管理容器配置里添加
@EnableAspectJAutoProxy(proxyTargetClass = true)