SpringBoot+Aop详解

Aop基本概念

Aop官方定义为面向切面编程,凡事先问下为什么要出现这项技术?

为何引入Aop

Aop的实际作用是为了对公共方法的剥离与对目标方法的织入,具体几个方法的定义为Aspect(切面)、PointCut(切点)、@Befor(切入时刻);
就组织成了一个完整的增强功能,如下将示例展示Log增强与Controller参数校验

Aop需要的SpringBoot依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

Aop的切入时刻有5种

  • @Before:前置通知
  • @After:后置通知
  • @AfterThrowing:异常通知
  • @AfterReturning:返回方法之前通知
  • @Around:环绕通知
    • 环绕通知与其他通知的主要区别在于:
      • 可以自主决定是否调用目标方法
      • 可以控制方法的返回值

废话不多说,开始编写一个Log增强类,并引入参数判断非空的切面

第一步:定义切面

package com.bdcloud.aop;

import com.bdcloud.exception.ParmCheckException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Order(-99) // 控制多个Aspect的执行顺序,越小越先执行, 当然也可以不写这注解, 对于写和不写@order的两个切面, 有@order的优先于无@order的执行; 都有@order时, 越小越执先执行
@Aspect
public class LogAspect {

    /**
     * 定义Logger输出类
     */
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    /****
     * 定义日志增强切点
     */
    @Pointcut("execution(public * com.bdcloud.controller.HelloController.*(..))")
    public void addAdvice(){};

    /***
     * 定义参数校验切点,并引入自定义注解细粒度控制对Controller的参数校验
     */
    @Pointcut("execution(public * com.bdcloud.controller.ParmController.*(..)) && @annotation(com.bdcloud.annotation.MyAnnotaion)")
    public void parmCheck(){};

    /***
     * 定义在对目标方法增强前
     * @param joinPoint
     * @return
     */
    @Before(value = "addAdvice()")
    public Object intrecepter(JoinPoint joinPoint){
        logger.info(joinPoint.getTarget().getClass().getName());
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        logger.info(signature.getMethod().getName());
        System.out.println("测试Logger增强控制");
        return "hello Aspect!";
    }

    /***
     * 参数校验前置通知的处理
     */
    @Before("parmCheck()")
    public void sayParmCheck(){
        logger.info("Start ParmCheck-进行非空验证");
    }

    /***
     * 参数校验环绕通知的处理
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("parmCheck()")
    public Object parmCheck(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法名
        Method method = signature.getMethod();

        //获取方法参数名
        String[] parameterNames = signature.getParameterNames();

        //获取参数值
        Object[] args = joinPoint.getArgs();

        //获取参数类型
        Class<?>[] parameterTypes = method.getParameterTypes();

        for (int i = 0; i < parameterNames.length; i++) {
            parmIsNull(parameterNames[i],args[i],parameterTypes[i]==null?null:parameterTypes[i].getName());
        }

        return joinPoint.proceed();
    }

    /***
     * 参数进行非空校验
     * @param parmName
     * @param value
     * @param parmType
     */
    private void parmIsNull(String parmName,Object value,String parmType){
        if (null == value || "".equals(value.toString().trim())){
            throw new ParmCheckException(parmName,value);
        }
    }
}

第二步通过@Pointcut()定义切点,如下代码

    /****
     * 定义日志增强切点
     */
    @Pointcut("execution(public * com.bdcloud.controller.HelloController.*(..))")
    public void addAdvice(){};

    /***
     * 定义参数校验切点,并引入自定义注解细粒度控制对Controller的参数校验
     */
    @Pointcut("execution(public * com.bdcloud.controller.ParmController.*(..)) && @annotation(com.bdcloud.annotation.MyAnnotaion)")
    public void parmCheck(){};

第三步定义不同时刻切入的执行方法与流程

    /***
     * 定义在对目标方法增强前
     * @param joinPoint
     * @return
     */
    @Before(value = "addAdvice()")
    public Object intrecepter(JoinPoint joinPoint){
        logger.info(joinPoint.getTarget().getClass().getName());
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        logger.info(signature.getMethod().getName());
        System.out.println("测试Logger增强控制");
        return "hello Aspect!";
    }

    /***
     * 参数校验前置通知的处理
     */
    @Before("parmCheck()")
    public void sayParmCheck(){
        logger.info("Start ParmCheck-进行非空验证");
    }

第四步HelloController的实现

package com.bdcloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String sayHello(){
        return "hello world";
    }

    @GetMapping("/hi")
    public String sayHi(){
        return "Hi World!";
    }
}

补充

  • 自定义异常处理
  • 自定义注解细粒度控制Controller参数校验

自定义异常处理的实现

  • 第一步,通过继承RuntimeException类实现自定义异常,重写getMessage()方法
package com.bdcloud.exception;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class ParmCheckException extends RuntimeException {

   private String parameterName;

   private Object ParameterValue;

   @Override
   public String getMessage() {
       return "Throw Exception Parmeter is "+this.parameterName+" : "+this.ParameterValue;
   }
}

  • 第二步,通过@ControllerAdvice与@ExceptionHandler实现异常处理
package com.bdcloud.exception;

import org.aspectj.lang.annotation.After;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/***
 * 全局异常处理类
 */
@ControllerAdvice
public class GlobalExceptionHandler {

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

    /***
     * 参数为该Handler处理的异常
     * @param e
     * @return
     */
    @ExceptionHandler({ParmCheckException.class})
    @ResponseBody
    public Object getException(Exception e){
        logger.error("request Exception:", e);
        return e.getMessage();
    }

}

自定义注解的实现

	package com.bdcloud.annotation;

import java.lang.annotation.*;

/***
 * 自定义注解,用于细粒度控制Aspect
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotaion {
}

自定义注解对Controller的细粒度控制实现

package com.bdcloud.controller;

import com.bdcloud.annotation.MyAnnotaion;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ParmController {

    @GetMapping("/parm")
    public String getParam(@RequestParam String temp){
        return temp;
    }

    @MyAnnotaion //添加该自定义注解后才会走增强代码流程
    @GetMapping("/parm2")
    public String getParam2(@RequestParam String lxf){
        return lxf;
    }
}

你可能感兴趣的:(SpringBoot+Aop详解)