Spring Boot : 全局异常处理与自定义切面

工程地址:https://github.com/showsys20/spring-demo-cm.git

1.Spring提供了便利的异常捕获机制,采用注解@ExceptionHandler(具体Exception.class)就可以了。全局的异常可以通过定义专门的异常处理类加上注解@ControllerAdvice来实现。下面是个简单的例子。

@ControllerAdvice
@Slf4j
public class MyExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public String runtimeException(RuntimeException e) {
        log.info("全局的异常处理器");
        log.error("error:", e);
        return "RuntimeException:" + e.getMessage();
    }

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public String throwable(Throwable e) {
        log.info("全局的异常处理器");
        log.error("error:", e);
        return "Throwable: " + e.getMessage();
    }

2.控制反转IOC和面向切面编程AOP是Spring的很重要的特性,都是为了减少代码的耦合程度。传统的设计模式中,若A对象引用B对象,需要在A对象中new B对象的实例;而Spring 通过依赖注入(自动,构造方法注入,Set方法注入),A对象不再需要自己显示的new B对象的实例,将控制权交了出去。这即为控制反转。

AOP在Spring中是通过动态代理来实现的,对原业务代码无侵入的情况下,实现功能的增强。先看个例子:

package com.demo.cm.sample.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class LoggerAspectj {
    Logger logger = LoggerFactory.getLogger(LoggerAspectj.class);

    @Pointcut("execution(* com.demo.cm.sample.controller..*.*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Signature signature = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Class clazz = AopUtils.getTargetClass(proceedingJoinPoint.getTarget());
//        Type[] types = AopUtils.getTargetClass(proceedingJoinPoint.getTarget()).getGenericInterfaces(); // 获取所有接口
//        Annotation nologgingAnno = ((Class)types[0]).getAnnotation(Nologging.class); // type是所有类型的父接口

        Method targetMethod = methodSignature.getMethod();
//        String methodName = targetMethod.getName();
        logger.info("============={}: start================", targetMethod);
        Object o = proceedingJoinPoint.proceed();
        long end = System.currentTimeMillis();
        logger.info("============={}: 执行时间{} end================", targetMethod, end - start);
        return o;
    }

}

分为2个步骤,首先定义一个Pointcut,@Pointcut("execution(* com.demo.cm.sample.controller..*.*(..))"),本次定义的切点意思是执行com.demo.cm.sample.controller包及子包下方法任意方法。根据业务需要,可以定义指定返回值,指定参数的切点。

简单来说就是触发的点。而后通过@Around, @Before,@After等注解设置触发时机。

运行程序,访问/hello接口,可以在控制台看到log。而这整个过程,在Controller的方法里面是完全察觉不到这个过程的。

9  INFO 3944 --- [nio-8080-exec-7] com.demo.cm.sample.aspect.LoggerAspectj  : =============public com.demo.cm.model.User com.demo.cm.sample.controller.UserController.getUser(java.lang.Integer): start================
2020-04-06 23:14:11.757  INFO 3944 --- [nio-8080-exec-7] com.demo.cm.sample.aspect.LoggerAspectj  : =============public com.demo.cm.model.User com.demo.cm.sample.controller.UserController.getUser(java.lang.Integer): 执行时间385097 end================

关于切面的机制,spring是采用动态代理在实现的,分为JDK的动态代理和CGLib的动态代理,在自己定义的方法中加断点,debug运行,访问接口后,可以看到调用过程,这个过程能够看出使用的具体是那种代理,也可以跟进去查看源码。

Spring Boot : 全局异常处理与自定义切面_第1张图片

 

你可能感兴趣的:(Spring,AOP,IOC)