大家都知道spring的特点IOC和AOP,IOC是最常用的注入,就是被注入的类上加@Component注解,在需要用到时候,通过 @Autowired注入,不用每次都
new
出来。当然为了分清层级,@Component通常使用@Repository、@Service、@Controller代替。
本文只要记录AOP的用法,以springboot框架为例。
AOP通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,
在不修改主流程的情况下,可以进行权限校验,日志记录,性能监控等。
底层实现是JDC的动态代理和cglib动态代理。
动态代理
默认策略如果目标类是接口,则用 jDKProxy来实现,否则用cglib
JDKProxy:通过ava的内部反射机制实现
cgib:以继承的方式动态生成目标类的代理,借助ASM实现
两种实现方法:
路径切入和注解切入,区别在于切点,前者适合批量切入,后者比较灵活,加注解的类才会被切。
1、通过路径切入
2、通过注解切入
路径切入
1、新建切面类上面加俩注解 @Aspect @Component 缺一不可
2、@Pointcut写上要切入的包,也可以精确到类
3、@Before切入点之前要处理的业务
4、@After切入点之后要处理的业务
@Aspect
@Component
public class VisitAop {
@Pointcut("execution(public * com.forum.controller.*.*(..))")
public void log() {
}
@Before("log()")
public void doBefore(JoinPoint joinPoint) {
........
}
@After("log()")
public void doAfter() {
........
}
}
注解切入
1、自定义注解
2、切入类@Aspect @Component 缺一不可
3、@Pointcut写上要切入注解(意思是带此注解者,必切!)
4、@Before、@After同上。
1、自定义注解
1.1 @Target和@Retention定义自定义注解,无需其他,标识作用的注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VisitCount {
}
3、切入点
3.1和路径切入的区别在此
@Pointcut("@annotation(com.Annotation.VisitCount)")
JoinPoint
此外可以了解一些doBefore(),的参数JoinPoint,以便操作业务;
1、joinPoint.getSignature().getDeclaringType().getSimpleName(),切入的类名
2、joinPoint.getArgs(),切入方法的参数数组
3、joinPoint.getSignature().getName(),切入方法名
五种增强/通知
上面的@Before和@After只是aop五种增强也叫通知的其中两种。执行顺序各有不同。
前置通知(@Before)
后置通知(@Ater)
返回通知(@AfterReturning)
异常通知(@AfterThrowing)
环绕通知(@Around)
异常通知和返回通知只会有一个出现,目标方法异常后会进入异常通知方法,不会进入返回通知方法。
正常执行会进入返回通知方法,不会进入异常通知方法。
@Before("log()")
public void doBefore() {
System.out.println("前置通知-Before");
}
@After("log()")
public void doAfter() {
System.out.println("后置通知-After");
}
@AfterReturning("log()")
public void doAfterReturning() {
System.out.println("返回通知-AfterReturning");
}
@AfterThrowing("log()")
public void doAfterThrowing() {
System.out.println("异常通知-AfterThrowing");
}
目标方法正常执行的情况下打印:
前置通知-Before
后置通知-After
返回通知-AfterReturning
目标方法异常执行的情况下打印:
前置通知-Before
后置通知-After
异常通知-AfterThrowing
环绕通知比较特殊,ProceedingJoinPoint.proceed()方法表示继续执行目标方法。
环绕通知的返回值就是目标方法的返回值。如果没有返回值,目标方法也没有了。
@Around("log()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("环绕通知开始-Around");
Object o = null;
try {
o = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕通知结束-Around");
return o;
}
加入环绕通知后的执行顺序
环绕通知开始-Around
前置通知-Before
环绕通知结束-Around
后置通知-After
返回通知-AfterReturning
proceed()方法的有参方法甚至可以修改目标方法的参数,传入一个Object数组
Object[] var1 = new Object[]{"参数a"};
o = proceedingJoinPoint.proceed(var1);
由此可见,环绕通知可以用于对业务逻辑判断后可以修改目标方法入参的场景。