Spring的AOP面向切面

AOP在Spring中原理

通过实现BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法,在初始化前后用代理对象加强原来的类

实现方式

  • 预编译:AspectJ
  • 运行期动态代理(JDK动态代理、CGLib动态代理):SpringAOP、JbossAOP

AOP的相关术语

  • jointpoint(连接点):是指那些被拦截到的点。在spring中是指方法,因为spring只支持方法类型的连接点。
  • pointcut(切入点):对哪些joinpoint进行拦截
  • advice(通知/增强):拦截到的jionpoint所做的事情。方法层面的。
    • 前置通知
    • 后置通知
    • 异常通知(抛出异常)
    • 最终通知(无论如何都执行)
    • 环绕通知(方法执行的前后都通知,甚至可以阻止目标方法执行)
  • introduction(引介):特殊的通知,类层面的。
  • target(目标对象):代理的目标对象
  • weaving(织入):把增强应用到目标对象来创建新的代理对象的过程
  • proxy(代理):被AOP织入增强后产生的结果代理类
  • aspect(切面):切入点+通知的结合
    • advisor:一般的切面,advice本身就是一个切面,对目标类所有方法进行拦截
    • PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法
    • IntroductionAdvisor:引介切面

底层实现

  • JDK动态代理:对接口做动态代理。代理类继承InvocationHandler接口使用Proxy.newProxyInstance(被代理的接口)
  • CGLIB的动态代理:对实现类做子类增强。使用Enhancer增强

总结

  • spring在运行期间,生成动态代理对象,不需要特殊的编译器
  • spring AOP的底层是通过JDK动态代理或CGLib动态代理技术为目标Bean执行横向织入
    • 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理
    • 若目标对象没有实现任何接口,spring使用CGLib库生成目标对象的子类
  • 程序中优先对接口创建代理,便于程序解耦维护
  • 标记为final的方法,不能被代理,因为无法进行覆盖
    • JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰
    • CGLib是针对目标类生产子类,因此类或方法不能使用final
  • spring只支持方法的连接点,不提供属性的连接点

AspectJ用法

通知类型

  • @Before 前置通知
  • @AfterReturning 后置通知
  • @Around 环绕通知
  • @AfterThrowing 异常抛出通知
  • @After 最终final通知

切入点表达式

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

  • 匹配所有类public方法 execution(public * *(..))
  • 匹配指定包下所有类方法 execution(* top.linxz.dao.*(..)) 不包含字包
    • 包含子包execution(* top.linxz.dao..(..)) ..表示包、子孙包下所有类
  • 匹配指定类所有方法 execution(* top.linxz.service.UserService.*(..))
  • 匹配实现特定接口所有类方法execution(* top.linxz.dao.GenericDAO+.*(..))
  • 匹配所有save开头的方法 execution(* save*(..))

通知的使用

  • 配置Aspect
@Aspect
public class MyAspectAnno{
    ...
}
  • 前置通知
@Before(value="execution(* top.linxz.java.UserDao(..))")
public void before(JoinPoint joinPoint){
    //joinPoint就是切到的方法
}
  • 后置通知
@AfterReturning(value="execution(* top.linxz.java.UserDao(..))", returning="returning")
public void afterReturing(Object returning){
    //这个returning就是拿到的返回值
}
  • 环绕通知
@Around(value="execution(* top.linxz.java.UserDao(..))", returning="returning")
public Object afterReturing(ProceedingJoinPoint joinPoint){
    System.out.println("环绕前通知")
    Object obj=joinPoint.proceed();//执行目标方法,如果不调用,目标方法就被拦截了。
    System.out.println("环绕后通知")
    return obj;
}
  • 异常通知
@AfterThrowing(value="execution(* top.linxz.java.UserDao(..))",throwing="e")
public void afterThrowing(Throwable e){
    
}
  • 最终通知,无论如何即使有异常也执行
@After(value="execution(* top.linxz.java.UserDao(..))")
public void afterThrowing(Throwable e){
    
}
  • @Pointcut为切点命名
    • 切点方法必须为private void 无参数方法,方法名为切点名
    • 当通知多个切点时,可以使用||进行连接

参考

  1. Spring AOP,https://class.imooc.com/course/587
  2. 基于AspectJ的AOP开发,https://class.imooc.com/course/588

关于我:

linxinzhe,全栈工程师,目前供职于某世界500强银行的金融科技部门(人工智能,区块链)。

GitHub:https://github.com/linxinzhe

欢迎留言讨论,也欢迎关注我~
我也会关注你的哦!

你可能感兴趣的:(Spring的AOP面向切面)