面向切面
1. 过程图示:
2. AOP有误异常时的执行顺序(@Around@Before@After@AfterReturning@AfterThrowing)
正常情况
- @Around start
- @Before…
- method invoke
- @Around end
- @After…
- @AfterReturning…
有异常的情况
- @Around start
- @Before…
- method invoke
- @After…
- @AfterThrowing…
整个执行过程
try{
try{
//@Around start
//@Before
method.invoke(..);
//@Around end
}finally{
//@After
}
//@AfterReturning
}catch(){
//@AfterThrowing
}
3. 使用介绍
1.@Pointcut(“execution(* com.test.server.impl…*(…))”),路径生效的方式
//界面生效的解释
(1)execution(public * *(..))——表示匹配所有public方法
(2)execution(* set*(..))——表示所有以“set”开头的方法
(3)execution(* com.xyz.service.AccountService.*(..))——表示匹配所有AccountService接口的方法
(4)execution(* com.xyz.service.*.*(..))——表示匹配service包下所有的方法
(5)execution(* com.xyz.service..*.*(..))——表示匹配service包和它的子包下的方法
(1) 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(2)相关通知的代码
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAop {
//需要切入的类的所有的方法,具体的业务
@Pointcut("execution(* com.test.server.impl..*(..))")
public void pointcut() {
}
//前置通知
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("-----------before 方法执行了-----------");
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("前置方法中,获取参数: " + args[i]);
}
String name = joinPoint.getTarget().getClass().getName();
System.out.println("前置方法中,获取目标类: " + name);
}
//后置通知
@After("pointcut()")
public void after() {
System.out.println("-----------after 方法执行了-----------");
}
//返回通知
@AfterReturning(pointcut = "pointcut()", returning = "rs")
public void afterReturn(Object rs) {
System.out.println("-----------afterReturning 方法执行了-----------");
System.out.println("返回值为: " + rs);
}
//错误拦截通知
@AfterThrowing(pointcut = "pointcut()", throwing = "err")
public void afterTrowing(Throwable err) {
System.out.println("-----------afterTrowing 统一错误处理执行了-----------");
System.out.println("错误信息: " + err.getMessage());
}
//环绕通知
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) {
try {
//around开始
System.out.println("---around方法开始执行-----");
//环绕通知,手动执行真正的业务方法
Object proceed = joinPoint.proceed();
//around结束
System.out.println("----around方法执行完成----");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
2.注解生效的方式
(1).定义注解 @LogAnnotation
@Target(ElementType.METHOD)//标注在方法上
@Retention(RetentionPolicy.RUNTIME)//运行时生效
public @interface LogAnnotation{
}
(2)在需要增强的方法上添加定义的注解 @LogAnnotation
@Override
@LogAnnotation
public User findById(Integer id) {
System.out.println("userService业务方法执行了");
User user = userMapper.findById(id);
return user;
}
(3)在通知的代码中,通过注解生效
@Pointcut("@annotation(com.test.config.LogAnnotation)")
public void pointcut() {
}
3.注解中传入参数
@Target(ElementType.METHOD)//标注在方法上
@Retention(RetentionPolicy.RUNTIME)//运行时生效
public @interface AopAnnotaiton {
String value() default "";
String decription() default "";
boolean flag() default true;
}
(2)在业务代码中添加定义的注解,并且给定参数
@Override
@LogAnnotation(value = "test", description = "注解测试", flag = false)
public User findById(Integer id) {
System.out.println("userService业务方法执行了");
User user = userMapper.findById(id);
return user;
}
(3)在通知的代码中,通过注解生效,并且获取注解
//前置通知
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("-----------before 方法执行了-----------");
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("前置方法中,获取参数: " + args[i]);
}
String name = joinPoint.getTarget().getClass().getName();
System.out.println("前置方法中,获取目标类: " + name);
//获取注解的参数
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
LogAnnotation logAnnotation = signature.getMethod().getDeclaredAnnotation(LogAnnotation.class);
//获取注解中参数
String value = logAnnotation.value();
String description = logAnnotation.description();
boolean flag = logAnnotation.flag();
//输出
System.out.println(value);
System.out.println(description);
System.out.println(flag);
}