Spring-2.AOP

4 篇文章 0 订阅

用法示例

@Aspect
@Component
public class WebLogAspect {

    private Logger logger = Logger.getLogger(getClass());

    @Pointcut("execution(public * com.didispace.web..*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求,返回内容
        logger.info("RESPONSE : " + ret);
    }

}

AOP的用法主要时几个注解

  • @Aspect:标注类为AOP类
  • @Pointcut:指定切点,切点是指需要被切面的代码位置,该注解的主要作用是不要重复的写切点,其他注解只要在pointcut属性中指定@Pointcut标注的方法名即可。
  • @Before:标注切点代码执行前被触发的方法
  • @After:标注切点代码执行后被触发的方法
  • @Around:标注切点代码执行前后都会触发的方法
  • @AfterRetruening:标志切点代码返回后触发的方法
  • @AfterThrowing:标注切点代码异常后触发的方法
  • @Order,多个切面的执行顺序
try{  
     try{  
        doBefore();//对应@Before注解的方法切面逻辑  
        method.invoke();
     }finally{  
        doAfter();//对应@After注解的方法切面逻辑  
     }  
     doAfterReturning();//对应@AfterReturning注解的方法切面逻辑,如果发生异常,该代码不会被执行
 }catch(Exception e){  
      doAfterThrowing();//对应@AfterThrowing注解的方法切面逻辑  
 }

大概的执行逻辑如上。异常情况和正常情况的差别就在于正常情况下,最后执行的是@AfterReturning方法而不会执行@AfterThrowing,而异常情况下会执行@AfterThrowing方法而不会执行@AfterReturning。

如果一个方法有多个切面逻辑的执行顺序

如果有多个切面,情况则会复杂得多。执行顺序如下图。
在这里插入图片描述
并且Aspect1和Aspect2得执行顺序是不确定的,但是可以通过@Order注解标注顺序

@Order(5)
@Component
@Aspect
public class Aspect1 {
    // ...
}

@Order(6)
@Component
@Aspect
public class Aspect2 {
    // ...
}

实现机制

Spring AOP使用动态代理实现。实现机制有两种,一种是使用JDK提供的代理,一种是使用CGlib代理

  • JDK提供的代理
    动态代理直接使用Proxy类进行代理,可以看下我的另一篇文章,解释了动态代理:《java-基础知识:面向对象,反射,泛型,异常和错误,注解,序列化和反序列化,动态代理
    动态代理要求必须实现接口,原理是根据定义的接口创建一个新的代理类,可以在创建时把被代理的对象传入到代理类的实例中,调用原来的实例方法。也可以不用调用原实例的方法,比如MyBatis使用代理,并没有原实例。仅仅是把DAO接口创建成代理实例。不过动态代理要求被代理的类必须实现接口,而没有实现接口的类则无能为力了。

  • CGlib
    为了能够代理无实现接口的类,spring使用了CGlib进行代理,和JVM的动态代理不同,CGlib使用的是继承的方式进行代理。即创建一个被代理类的子类,重写被代理类的方法,调用被代理类的方法并注入切面逻辑。实际上调用的就是子类。CGlib的缺点是,不能代理final方法,因为无法重写。

  • AOP和IOC的关系
    在spring一般使用的都是IoC,那么,AOP是如何注入到bean实例中的呢?还记得上篇文章《Spring-1.IoC》提到的BeanPostProcessor吗?实现这个接口能够对生成的bean进行处理,没错,AOP就是利用BeanPostProcessor进行代码注入的,类AspectJAwareAdvisorAutoProxyCreator的父类实现了BeanPostProcessor的接口。在被放入到bean池之前,对bean进行了代理,并返回一个被代理过的实例。

常用实践

  • 异常处理
    异常处理是常规操作,但是异常处理一般都是和实际的逻辑并没有太大的关系,比如返回一个错误提示,比如记录下日志。如果把这些代码和业务代码放在一起,那么耦合度就和比较高。一个比较好的实践就是,使用AOP统一处理。我们可以自定义一个异常,然后发送错误时,只要把必要的信息传给自定义的异常,直接抛出去。切面逻辑可以监控所有的出口,比如mvc的control,捕获自定义异常,进行日志输出,统一返回提示。
//统一异常日志
@AfterThrowing(throwing="ex",pointcut="execution(* com.bean.*.*(..))")
public void doRecoveryActions(Throwable ex){
        System.out.println("目标方法中抛出的异常:"+ex);
        System.out.println("模拟抛出异常后的增强处理...");
}
//统一错误处理
@Around("...")
public Object requestAround(ProceedingJoinPoint joinPoint){
    Object result;
    try{
        result = joinPoint.proceed();
    }catch(Exception e){
        result = ...
    }

    return result;
}
  • 安全检查
    开发中最常用到的另外一个方面就是权限的检查,和错误处理一样,权限的检查也并不属于原始逻辑的一部分,通过AOP可以把权限的检查逻辑统一进行处理。

  • 缓存
    缓存也是非原始逻辑的一部分,可以通过AOP,把缓存的代码统一处理,通过AOP可以把各个功能的逻辑代码分离开来,这就是AOP最迷人的地方。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值