zoukankan      html  css  js  c++  java
  • spring boot项目08:切面(AOP)-基础使用

    JAVA 8

    Spring Boot 2.5.3

    ---

    百度百科:

    在软件业,AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

    涉及注解:

    @Aspect、@Pointcut、

    @Around、@Before、@AfterReturning、@AfterThrowing、@AfterThrowing、@After

    涉及接口:

    JoinPoint

    ProceedingJoinPoint

    ---

    jar包:aspectjweaver-x.y.z.jar

    AOP 属于 Spring Framework的内容,在官文 Spring Framework Documentation->Core 可以找到 介绍。

    在 本文的 Spring Boot 版本使用时,不需要使用 @EnableAspectJAutoProxy 注解

    找原因:

    spring-boot-autoconfigure 包 的 META-INFO/spring.factories 中有下面的定义:AopAutoConfiguration

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,

    官文中 spring.aop.auto 默认为 true:

    示例代码:

    package org.lib.mysqlhello.common;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    @Aspect
    @Component // 必须
    public class WebLogAspect {
    
    	@Autowired
    	private HttpServletRequest req;
    	
    //	@Autowired
    //	private HttpServletResponse resp;
    
    	// org.lib.mysqlhello.user.controller包下 所有public方法
        // 注意通配符的使用!
        @Pointcut(value="execution(public * org.lib.mysqlhello.user.controller..*.*(..))")
    	private void ptUserController() {
    	}
    	
    	/**
    	 * Before
    	 * @author ben
    	 * @date 2021-08-22 11:32:28 CST
    	 * @param joinPoint
    	 */
        // 等效
    //	@Before(value="execution(public * org.lib.mysqlhello.user.controller..*.*(..))")
    	@Before(value="ptUserController()")
    	public void logBeforeUserController(JoinPoint joinPoint) {
    		log.info("请求before:{}", req.getRequestURI());
    	}
    	
    	/**
    	 * Around
    	 * @author ben
    	 * @date 2021-08-22 11:32:21 CST
    	 * @param pjp
    	 * @return
    	 * @throws Throwable
    	 */
        // 等效
    //	@Around(value="execution(public * org.lib.mysqlhello.user.controller..*.*(..))")
    	@Around(value="ptUserController()")
    	public Object logAroundUserController(ProceedingJoinPoint pjp) throws Throwable {
    		try {
    			// 在 @Before 之后执行
    			log.info("请求around: {}", req.getRequestURI());
    			return pjp.proceed();
    		} catch (Exception e) {
    			log.error("请求around发生异常:{}, e={}", req.getRequestURI(), e.getMessage());
    			throw e;
    			
    			// return后,全局异常处理无效了!
    //			return ResultVO.getFailed(500, "发生异常-@Aspect-Around", e.getMessage());
    		} finally {
    			// 在 @After 之后执行
    			log.info("请求around-finally: {}", req.getRequestURI());
    		}
    	}
    	
    	/**
    	 * AfterReturning
    	 * @author ben
    	 * @date 2021-08-22 11:32:15 CST
    	 */
        // 等效
    //	@AfterReturning(value="execution(public * org.lib.mysqlhello.user.controller..*.*(..))")
    //	@AfterReturning(value="ptUserController()")
    	@AfterReturning(pointcut="ptUserController()")
    	public void logAfterReturningUserController() {
    		log.info("请求afterReturning:{}", req.getRequestURI());
    	}
    
    	/**
    	 * AfterThrowing 1-不抛出异常,没有throwing属性
    	 * @author ben
    	 * @date 2021-08-22 11:30:33 CST
    	 */
        // 等效
    //	@AfterThrowing(value="execution(public * org.lib.mysqlhello.user.controller..*.*(..))")
    //	@AfterThrowing(value="ptUserController()")
    	@AfterThrowing(pointcut = "ptUserController()")
    	public void logAfterThrowingUserController() {
    		log.info("请求afterThrowing:{}, nothing...", req.getRequestURI());
    	}
    	
    	/**
    	 * AfterThrowing 2-抛出异常,必须有throwing属性
    	 * @author ben
    	 * @date 2021-08-22 11:31:02 CST
    	 * @param ex
    	 */
        // 等效
    //	@AfterThrowing(value="execution(public * org.lib.mysqlhello.user.controller..*.*(..))", throwing = "ex")
    //	@AfterThrowing(value="ptUserController()", throwing = "ex")
    	@AfterThrowing(pointcut = "ptUserController()", throwing = "ex")
    	public void logAfterThrowingUserController(Exception ex) {
    		log.info("请求afterThrowing:{}, ex.msg={}", req.getRequestURI(), ex.getMessage());
    	}
    	
    	/**
    	 * After
    	 * @author ben
    	 * @date 2021-08-22 11:31:59 CST
    	 */
        // 等效
    //	@After(value="execution(public * org.lib.mysqlhello.user.controller..*.*(..))")
    	@After(value="ptUserController()")
    	public void logAfterUserController() {
    		log.info("请求after:{}", req.getRequestURI());
    	}
    	
    }
    

    测试结果1:正常请求,,注意顺序

    测试结果2:异常请求,,注意顺序

    注,上图最后异常 是 全局异常处理 的日志——AOP 的 @Around 捕获异常后,没有return,直接throw,这才执行到 全局异常处理。

    各个Advice注解的属性列举:

    注解 @Around、@Before、@After 的属性——只有2个

    String value、

    String argNames;

    注解 @AfterReturning 、@AfterThrowing 的属性——各4个

    String value、

    String pointcut

    String returning —— 仅@AfterReturning 

    String throwing —— 仅@AfterThrowing 

    String argNames

    AOP的实际使用中,还会涉及到 入参、响应 的一些处理,可以通过 各个Advice 的属性 或者 函数里面的参数 JoinPoint、ProceedingJoinPoint对象 获取。

    参考文档

    1、拦截机制中Aspect、ControllerAdvice、Interceptor、Fliter之间的区别详解

    理解 这个顺序很重要。

    2、在SpringBoot中用SpringAOP实现日志记录功能

    讲了 Aspect内部各个Advice的顺序。

    但其中介绍的 RequestContextHolder 在 本文的 S.B.版本中没有。

    3、

  • 相关阅读:
    在redhat上搭建redmine
    工具第二天 cocoaPods 私有库的创建
    回归 从注释开始 appledoc
    Chrome浏览器插件开发-关于案例
    Chrome浏览器插件开发-淘宝自动登录
    IOS开发-本地持久化存储sqlite应用
    IOS开发-表单控件的应用
    如何安全可靠的处理后台任务
    Cache应用/任务Mutex,用于高并发任务处理经过多个项目使用
    报表的缓存基本存储和读写
  • 原文地址:https://www.cnblogs.com/luo630/p/15172308.html
Copyright © 2011-2022 走看看