SpringBoot的AOP实践

 

导读

  之前写过一篇,利用AOP记录用户操作日志:点我直达

核心概念

横切关注点

  • 对那些方法进行拦截,拦截后怎么处理,这些就叫横切关注点
  • 比如:权限认证、日志、事务

通知 Advice

  • 在特定的切入点上执行的增强处理,有5种通知
  • 用途:记录日志、控制事务、提前编写好通用的模块,需要的地方直接调用

连接点 JointPoint

  • 要用通知的地方,业务流程在运行过程中需要插入切面的具体位置
  • 一般是方法的调用前后,全部方法都可以是连接点
  • 只是概念,没啥特殊

切入点 Pointcut

  • 不能全部方法都是连接点,通过特定的规则来筛选连接点,就是Pointcut,选中那几个你想要的方法
  • 在程序中主要体现为书写切入点表达式(通过通配、正则表达式)过滤出特定的一组JointPoint连接点
  • 过滤出相应的Advicce将要发生的joinPoint地方

切面 Aspect

  • 通常是一个类,里面定义 切入点+通知,定义在什么地方;什么时间点,做什么事情
  • 通知 advice致命类时间和做的事情(前置、后置等)
  • 切入点pointcut指定在什么地方干这个事情
  • web接口设计中,web层-》网关层-》服务层-》数据层,每一层之间也是一个切面,对象和对象,方法和方法之间都是一个个切面

目标 target

  • 目标类,真正的业务逻辑,可以在目标类不知情的条件下,增加新的功能到目标类的链路上

织入 Weaving

  • 把切面(某个类)应用到目标函数的过程成为织入

AOP 代理

  • AOP框架创建的对象,代理就是目标对象的加强
  • Spring中的AOP代理可以使用JDK动态代理,也可以是CGLIB代理

添加依赖

        <!-- AOP依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

项目结构

 

自定义注解

package com.ybchen.annotation;

import java.lang.annotation.*;

/**
 * 自定义注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomAnnotation {
    String value() default "";
}

Aspect

package com.ybchen.component;

import com.ybchen.annotation.CustomAnnotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 自定义日志 AOP
 *
 * @Author:chenyanbin
 */
@Aspect
@Component
public class CustomLogAspect {
    private Logger logger = LoggerFactory.getLogger(CustomLogAspect.class);

    /**
     * 定义切入点
     */
    @Pointcut("execution(public * com.ybchen.controller.*.*(..))")
    public void pointCut() {
    }

    /**
     * 被自定义注解修饰的切入点
     */
    @Pointcut("@annotation(com.ybchen.annotation.CustomAnnotation)")
    public void pointCutAnnotation() {
    }

    /**
     * 前置通知
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("pointCut()")
    public void before(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        logger.info("【注解:Before】------------------切面  before");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        logger.info("【注解:Before】浏览器输入的网址=URL : " + request.getRequestURL().toString());
        logger.info("【注解:Before】HTTP_METHOD : " + request.getMethod());
        logger.info("【注解:Before】IP : " + request.getRemoteAddr());
        logger.info("【注解:Before】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("【注解:Before】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 被自定义注解修饰的,前置通知
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("pointCutAnnotation()")
    public void beforeAnnotation(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        logger.info("【自定义注解】-【注解:Before】------------------切面  before");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        logger.info("【自定义注解】【注解:Before】浏览器输入的网址=URL : " + request.getRequestURL().toString());
        logger.info("【自定义注解】【注解:Before】HTTP_METHOD : " + request.getMethod());
        logger.info("【自定义注解】【注解:Before】IP : " + request.getRemoteAddr());
        logger.info("【自定义注解】【注解:Before】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("【自定义注解】【注解:Before】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 后置返回通知
     *
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "pointCut()")
    public void afterReturning(JoinPoint joinPoint, Object ret) throws Throwable {
        // 处理完请求,返回内容
        logger.info("【注解:AfterReturning****】这个会在切面最后的最后打印,方法的返回值 : " + ret);
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        logger.info("【注解:AfterReturning****】浏览器输入的网址=URL : " + request.getRequestURL().toString());
        logger.info("【注解:AfterReturning****】HTTP_METHOD : " + request.getMethod());
        logger.info("【注解:AfterReturning****】IP : " + request.getRemoteAddr());
        logger.info("【注解:AfterReturning****】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("【注解:AfterReturning****】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 被自定义注解修饰的,后置返回通知
     *
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "pointCutAnnotation()")
    public void afterReturningAnnotation(JoinPoint joinPoint, Object ret) throws Throwable {
        // 处理完请求,返回内容
        logger.info("【自定义注解】【注解:AfterReturning】这个会在切面最后的最后打印,方法的返回值 : " + ret);
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        logger.info("【自定义注解】【注解:AfterReturning****】浏览器输入的网址=URL : " + request.getRequestURL().toString());
        logger.info("【自定义注解】【注解:AfterReturning****】HTTP_METHOD : " + request.getMethod());
        logger.info("【自定义注解】【注解:AfterReturning****】IP : " + request.getRemoteAddr());
        logger.info("【自定义注解】【注解:AfterReturning****】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("【自定义注解】【注解:AfterReturning****】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs()));
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        //获取方法上的自定义注解
        CustomAnnotation annotation = method.getAnnotation(CustomAnnotation.class);
        if (annotation == null) {
            return;
        }
        logger.info("【自定义注解】【注解:AfterReturning****】自定义注解的值:" + annotation.value());
    }

    /**
     * 后置异常通知
     *
     * @param joinPoint
     */
    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) {
        logger.info("【注解:AfterThrowing】方法异常时执行.....");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        logger.info("【注解:AfterThrowing】浏览器输入的网址=URL : " + request.getRequestURL().toString());
        logger.info("【注解:AfterThrowing】HTTP_METHOD : " + request.getMethod());
        logger.info("【注解:AfterThrowing】IP : " + request.getRemoteAddr());
        logger.info("【注解:AfterThrowing】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("【注解:AfterThrowing】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs()));
        logger.info("【注解:AfterThrowing】exception : " + e);
    }

    /**
     * 被自定义注解修饰的,后置异常通知
     *
     * @param joinPoint
     */
    @AfterThrowing(value = "pointCutAnnotation()", throwing = "e")
    public void afterThrowingAnnotation(JoinPoint joinPoint, Throwable e) {
        logger.info("【自定义注解】【注解:AfterThrowing】方法异常时执行.....");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        logger.info("【自定义注解】【注解:AfterThrowing】浏览器输入的网址=URL : " + request.getRequestURL().toString());
        logger.info("【自定义注解】【注解:AfterThrowing】HTTP_METHOD : " + request.getMethod());
        logger.info("【自定义注解】【注解:AfterThrowing】IP : " + request.getRemoteAddr());
        logger.info("【自定义注解】【注解:AfterThrowing】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("【自定义注解】【注解:AfterThrowing】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs()));
        logger.info("【自定义注解】【注解:AfterThrowing】exception : " + e);
    }

    /**
     * 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
     *
     * @param jp
     */
    @After("pointCut()")
    public void after(JoinPoint jp) {
        logger.info("【注解:After】方法最后执行.....");
    }

    /**
     * 被自定义注解修饰的,后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
     *
     * @param jp
     */
    @After("pointCutAnnotation()")
    public void afterAnnotation(JoinPoint jp) {
        logger.info("【自定义注解】【注解:After】方法最后执行.....");
    }

    /**
     * 环绕通知,环绕增强,相当于MethodInterceptor
     *
     * @param pjp
     * @return
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) {
        logger.info("【注解:Around . 环绕前】方法环绕start.....");
        try {
            //如果不执行这句,会不执行切面的Before方法及controller的业务方法
            Object o = pjp.proceed();
            logger.info("【注解:Around. 环绕后】方法环绕proceed,结果是 :" + o);
            return o;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 被自定义注解修饰的,环绕通知,环绕增强,相当于MethodInterceptor
     *
     * @param pjp
     * @return
     */
    @Around("pointCutAnnotation()")
    public Object aroundAnnotation(ProceedingJoinPoint pjp) {
        logger.info("【自定义注解】【注解:Around . 环绕前】方法环绕start.....");
        try {
            //如果不执行这句,会不执行切面的Before方法及controller的业务方法
            Object o = pjp.proceed();
            logger.info("【自定义注解】【注解:Around. 环绕后】方法环绕proceed,结果是 :" + o);
            return o;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }
}

vo类

package com.ybchen.vo;

/**
 * @Author:chenyanbin
 */
public class userVo {
    private String account;
    private String password;

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "userVo{" +
                "account='" + account + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

控制器

package com.ybchen.controller;

import com.ybchen.vo.userVo;
import org.springframework.web.bind.annotation.*;

/**
 * @Author:chenyanbin
 */
@RestController
@RequestMapping("user")
public class UserController {

    @GetMapping("login")
    public Object login(String accout, String password) {
        if ("".equalsIgnoreCase(accout)) {
            throw new NullPointerException();
        }
        if (accout.equalsIgnoreCase(password)) {
            return "登录成功";
        } else {
            return "登录失败";
        }
    }

    @PostMapping("login2")
    public Object login2(@RequestBody userVo userVo) {
        if ("".equalsIgnoreCase(userVo.getAccount())) {
            throw new NullPointerException();
        }
        if (userVo.getAccount().equalsIgnoreCase(userVo.getPassword())) {
            return "登录成功";
        } else {
            return "登录失败";
        }
    }
}
package com.ybchen.other;

import com.ybchen.annotation.CustomAnnotation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author:chenyanbin
 */
@RestController
@RequestMapping("annotation")
public class AnnotationController {

    @GetMapping("getName")
    @CustomAnnotation(value = "获取我的名字")
    public Object getName(String name) {
        return "我叫:" + name;
    }

    @GetMapping("getAge")
    @CustomAnnotation(value = "获取我的年龄")
    public void getAge(int age) {
        System.out.println("我今年:" + age + "岁啦");
    }
}

演示

posted @ 2021-08-17 11:32  陈彦斌  阅读(96)  评论(0)    收藏  举报