栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

AOP--面向切面编程实战

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

AOP--面向切面编程实战

	Spring有AOP和IOC两大特性,本篇主要讲AOP的实战使用。主要从以下的例子来说明aop的使用。

AOP实现全局日志记录

@Aspect
@Configuration
public class ControllerLogAspect {

    private static final Log logger = LogFactory.getLog(ControllerLogAspect.class);

    //抽取公共的切入点表达式,此表达式标识其作用域作用于controller层下的代码
    @Pointcut("execution(public String com.zy.main.controller.*.*(..))")
    public void pointCut() {

    }

    ;

    
    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        //获取参数
        Object[] args = joinPoint.getArgs();
        //joinPoint.getSignature().getDeclaringTypeName()类名
        //joinPoint.getSignature().getName() 获取到请求方法名
        logger.info(joinPoint.getSignature().getDeclaringTypeName()+joinPoint.getSignature().getName() +"运行。。。@Before:参数列表是"+ Arrays.asList(args));
    }

    
    @After("com.zy.main.aspect.ControllerLogAspect.pointCut()")
    public void logEnd(JoinPoint joinPoint) {

        Object[] args = joinPoint.getArgs();
        //joinPoint.getSignature().getDeclaringTypeName()类名
        //joinPoint.getSignature().getName() 获取到请求方法名
        logger.info(joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName() +"结束。。。@After:参数列表是"+ Arrays.asList(args));

    }

    //JoinPoint一定要出现在参数表的第一位
    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {

        Object[] args = joinPoint.getArgs();
        //joinPoint.getSignature().getDeclaringTypeName()类名
        //joinPoint.getSignature().getName() 获取到请求方法名
        logger.info(joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName() +"结束。。。@AfterReturning:参数列表是"+ Arrays.asList(args)+"正常返回。。。@AfterReturning:运行结果:{" + result + "}");
    }

    
    @AfterThrowing(value = "pointCut()", throwing = "exception")
    public void logException(JoinPoint joinPoint, Exception exception) {
        Object[] args = joinPoint.getArgs();
        //joinPoint.getSignature().getDeclaringTypeName()类名
        //joinPoint.getSignature().getName() 获取到请求方法名
        logger.error(joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName() +"结束。。。@AfterThrowing:参数列表是"+ Arrays.asList(args),exception);

    }

}

  • @Before
    此注解作用于方法执行之前,我们可以通过JoinPoint.getArgs()方法来获取到方法执行前接收到的参数。
  • @After
    此注解作用于方法执行之后
  • @AfterReturning
    此注解作用于方法返回之后,通过returning = "result"可以获取到方法的返回值
  • @AfterThrowing
    此注解作用于,异常抛出后
  • @Around
    环绕增强,这个会在后面仔细介绍。
    下面用代码来展示各个注解的执行顺序,测试类
@RestController
public class TestController {

    private static final Log logger = LogFactory.getLog(TestController.class);

    @RequestMapping("getSxbm")
    public String getSxbm(HttpServletResponse response1, String jsonParam,String param) throws Exception {
        return "ok";
    }
}
20:22:17.330 [http-nio-8070-exec-2] INFO  com.zy.main.aspect.ControllerLogAspect - com.zy.main.controller.TestControllergetSxbm运行。。。@Before:参数列表是[com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper@78575c07, {"itemCode":"0103022000"}, null]
20:22:17.335 [http-nio-8070-exec-2] INFO  com.zy.main.aspect.ControllerLogAspect - com.zy.main.controller.TestController,getSxbm结束。。。@After:参数列表是[com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper@78575c07, {"itemCode":"0103022000"}, null]
20:22:17.335 [http-nio-8070-exec-2] INFO  com.zy.main.aspect.ControllerLogAspect - com.zy.main.controller.TestController,getSxbm结束。。。@AfterReturning:参数列表是[com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper@78575c07, {"itemCode":"0103022000"}, null]正常返回。。。@AfterReturning:运行结果:{ok}

执行顺序依次为Before,After,AfterReturning,可以看见这三个注解中均获取到了方法的参数,同时AfterReturning也获取到了方法的反参。已经达到了日志记录的功能。
下面测试这种写法的异常捕捉

20:50:38.573 [http-nio-8070-exec-2] INFO  org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
20:50:38.583 [http-nio-8070-exec-2] INFO  org.springframework.web.servlet.DispatcherServlet - Completed initialization in 10 ms
20:50:38.617 [http-nio-8070-exec-2] INFO  com.zy.main.aspect.ControllerLogAspect - com.zy.main.controller.TestControllergetSxbm运行。。。@Before:参数列表是[com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper@3649c92, {"itemCode":"0103022000"}, null]
20:50:38.622 [http-nio-8070-exec-2] INFO  com.zy.main.aspect.ControllerLogAspect - com.zy.main.controller.TestController,getSxbm结束。。。@After:参数列表是[com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper@3649c92, {"itemCode":"0103022000"}, null]
20:50:38.622 [http-nio-8070-exec-2] ERROR com.zy.main.aspect.ControllerLogAspect - com.zy.main.controller.TestController,getSxbm结束。。。@AfterThrowing:参数列表是[com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper@3649c92, {"itemCode":"0103022000"}, null]
java.lang.ArithmeticException: / by zero

可以看见@AfterThrowing 成功捕捉到了这次异常。下面来讲还没用到的around。
around 环绕增强,上面实现的所有日志记录功能都可以通过around来实现

around版日志记录实现
代码实现

@Aspect
@Configuration
public class ControllerLogAspect {

    private static final Log logger = LogFactory.getLog(ControllerLogAspect.class);

    
    @Pointcut("execution(public String com.zy.main.controller.*.*(..))")
    public void pointCut() {

    }

    @Around("com.zy.main.aspect.ControllerLogAspect.pointCut()")
    public Object logEnd(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        logger.info("入参为"+Arrays.asList(args));
        Object result = new Object();
        //  logger.info(joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName() +"参数列表是"+ Arrays.asList(args));
        try{
             result = joinPoint.proceed();
            logger.info("反参为"+result.toString());

        }catch (Exception e){
            logger.error("Exception",e);
        } catch (Throwable throwable) {
            logger.error("throwable",throwable);
        }
        return result.toString();
    }

}

照例还是通过代码测试返回结果

21:53:14.864 [http-nio-8070-exec-2] INFO  com.zy.main.aspect.ControllerLogAspect - 入参为[com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper@1e42dee5, {"itemCode":"0103022000"}, null]
21:53:14.868 [http-nio-8070-exec-2] INFO  com.zy.main.aspect.ControllerLogAspect - 反参为ok

可以看见,around同样也可以不修改业务方法但完成业务日志的记录。细心的同学可以注意到,After等注解使用的对象为JoinPoint,而我在around注解中使用的则是ProceedingJoinPoint。
从两者的源码来说前者是后者的父类

ProceedingJoinPoint类中,包含两个新方法
proceed()与proceed(object)方法,proceed这个方法的作用是通知目标方法执行,同时返回结果值。前者是不更改原始传参,后者是重写请求传入的参数,再传入方法中进行执行。
本次只是用作日志功能,所以不对原始参数进行重写。
从执行流程下around的执行流程,可以涵盖before,after,afterthrowing等注解的功能

从开发角度来说,AOP可以做到不修改原有的业务代码就可以增加通用的功能。而从AOP里面的注解来说,个人推荐使用around。但具体情况具体分析,大家根据自己的情况进行使用。下篇带大家了解自定义注解结合AOP的使用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/915145.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号