Spring AOP面向切面编程(附实现源码)

描述

AOP(Aspect Oriented Programming)称为面向切面编程, 在不改变原有的逻辑的基础上,增加一些额外的功能。那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

AOP的基本概念

(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

Advice通知类型

1、@Before:在目标方法被调用之前做增强处理,只需要指定切入点表达式即可

2、@AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值

3、@AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名来访问目标方法中所抛出的异常对象

4、@After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式

5、@Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint,需要调用ProceedingJoinPoint.proceed()

实现方式

1、写死拦截代码在方法里面,实际上不算AOP
2、基于代理的AOP,JDK动态代理,CGLIB代理
3、(主要)@Aspect注解驱动的切面, Spring框架完全支持 AspectJ
4、通过aop:fonfig标签配置,声明的方式

AOP使用场景

权限
ERROR错误处理
Debug调试
LOGGER记录跟踪

代码(@Aspect注解驱动的切面)

下面的代码主要功能是针对接口的拦截,LOGGER记录跟踪 ,Debug调试

@Aspect
@Component
public class CommonLogInterceptor {
    @Autowired
    ItmpOperatorLogMapper itmpOperatorLogMapper;

    private Logger logger = LoggerFactory.getLogger(CommonLogInterceptor.class);
    private static final int OVERFLOW = 5000;

    /**
     * controller日志Aop
     */
    @Pointcut("execution(* cn.gzsendi.itmp.itmpmanageserver.controller.*.*(..))")
    public void enterController() {
    }

    /**    
     * ps:@Around兼具了@before和afterReturn的功能。
     *
     * @param jt ProceedingJoinPoint
     * @return 返回方法的值
     * @throws Throwable jt.proceed()异常
     */
    @Around("enterController()")
    public Object aroundController(ProceedingJoinPoint jt) throws Throwable {
        //获取请求
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (servletRequestAttributes == null) {
            return null;
        }

        //执行方法之前
        long startTime = System.currentTimeMillis();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        UserInfo userInfo = CurrentUserInfo.get();
        MethodSignature signature = (MethodSignature) jt.getSignature();
        logger.info("【Controller】请求的url: {}", request.getRequestURI());
        logger.info("【Controller】请求方式: {}", request.getMethod());
        logger.info("【Controller】接口参数: {}", DataeStringUtils.omitOverflowString(Arrays.toString(jt.getArgs()), OVERFLOW));
        logger.info("【Controller】请求用户信息: {} - {} - {}", userInfo.getRoleId(), userInfo.getRoleName(), userInfo.getUserName());
        logger.info("【Controller】请求的方法:{}.{}", signature.getDeclaringTypeName(), signature.getName());

        Object object = jt.proceed();

        //执行方法之后
        long excuteTime = System.currentTimeMillis() - startTime;
        logger.info("【Controller】请求的方法:{}.{}() 执行时间 {} ms", signature.getDeclaringTypeName(), signature.getName(), excuteTime);
        logger.info("【Controller】返回:{}", object);
        
        return object;
    }

    /**
     * service日志Aop
     */
    @Pointcut("execution(* cn.gzsendi.itmp.itmpmanageserver.service.impl.*.*(..))")
    public void enterService() {
    }

    @Before("enterService()")
    public void beforeService(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        logger.info("【Service】服务方法: {}.{}()", signature.getDeclaringTypeName(), signature.getName());
        logger.info("【Service】服务参数: {}", DataeStringUtils.omitOverflowString(Arrays.toString(joinPoint.getArgs()), OVERFLOW));
    }

    @Around("enterService()")
    public Object aroundService(ProceedingJoinPoint jt) throws Throwable {
        MethodSignature signature = (MethodSignature) jt.getSignature();
        long startTime = System.currentTimeMillis();
        Object object = jt.proceed();
        logger.info("【Service】服务方法:{}.{}() 执行时间 {} ms", signature.getDeclaringTypeName(), signature.getName(), System.currentTimeMillis() - startTime);
        return object;
    }
}

异常的全局拦截

@ControllerAdvice
public class ExceptionHandlerAop {

    private Logger logger = LoggerFactory.getLogger(ExceptionHandlerAop.class);

    @ExceptionHandler(BaseException.class)
    @ResponseBody
    public SimpleResponse exception(BaseException e) {
        logger.error("未处理的异常 -> ", e);
        return SimpleResponse.failure(e.getMsg());
    }

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public SimpleResponse exception(IllegalArgumentException e) {
        logger.error("参数错误 -> ", e);
        return SimpleResponse.failure(String.format("参数错误->[%s]", e.getMessage()));
    }

    /**
     * 针对Controller层JSON转换错误的异常
     *
     * @param e 异常对象
     * @return SimpleResponse
     */
    @ExceptionHandler({HttpMessageConversionException.class, HttpMessageNotReadableException.class, HttpMessageNotWritableException.class})
    @ResponseBody
    public SimpleResponse<String> exceptionHandler(Exception e) {
        logger.error("Controller层JSON数据转对象发生异常:", e);
        return SimpleResponse.failure("接口传入参数非法:请检查请求JSON是否正确");
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public SimpleResponse exception(Exception e) {
        logger.error("未处理的异常 -> ", e);
        return SimpleResponse.failure(String.format("未处理的异常->[%s]", e.getMessage()));
    }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值