SpringBoot+AOP+自定义注解,实现日志记录

一.定义自定义注解

import java.lang.annotation.*;

/**
 * @author awen
 * 定义注解目的 想让他当作切点
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) //.java .class 字节码
@Documented
public @interface Log {

    /**
     * 处理类型
     *
     * @return {@link String}
     */
    String handleType() default "";

    /**
     * 操作功能
     *
     * @return {@link String}
     */
    String cagn() default "";
}

        1.@Target 这个注解可以往什么上面加 这个就是可以往方法上面加

        2.Retention 在哪个生命周期生效

                生命周期: .java文件  .class文件  字节码文件(最常用 RUNTIME)

        3.@Documented 的含义 @Document 是 java 在生成文档,显示注解

        4.注意注解里面的定义   

                类型 +参数名字() 默认值 xx:

 二.AOP切面类编写

        1.导包

        
            org.springframework.boot
            spring-boot-starter-aop
        

      2.切面类

import com.alibaba.fastjson.JSONObject;
import com.example.demo2.service.CjzlMarkService;
import com.example.demo2.utils.JsonResult;
import com.example.demo2.utils.SpringFactoryUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 操作记录切点
 *
 * @author awen
 * @date 2022/12/03
 */
@Aspect //让springboot 知道我是切点
@Component //让springboot 识别到
@DependsOn("springFactoryUtils")
public class CzjlMark {

    private static Map beanMap = SpringFactoryUtils.getBeanMap(CjzlMarkService.class);

    /**
     * 日志切点
     */

    //1.定义切点  往哪里切 通过注解来去切 
    @Pointcut("@annotation(com.example.demo2.annotation.Log)")
    //这个方法就和形参一样 他不会执行他就是代表着我们真实切入的那些方法
    public void logPointCut(){
        System.out.println(1);
    };


    //2.实现切点 ,切点前执行,和方法一起执行,方法执行完之后执行

    //在切点前执行
    @Before("logPointCut()&&@annotation(log)")  //参数是切点 和 切点对象
    public void beforePointCut(JoinPoint joinPoint,Log log){
        Object[] args = joinPoint.getArgs();
        CjzlMarkService cjzlMarkService = beanMap.get(log.handleType());
        cjzlMarkService.before(args,log);
    }

    //切点执行完之后执行
    @AfterReturning(returning = "result",value = "logPointCut()")    //返回值result拿到这个结果 看看返回成功了嘛
    public void afterPointCut(JoinPoint joinPoint,Object result){
        JSONObject jsonResult = (JSONObject) result;
        if(200==Integer.valueOf(jsonResult.get("code").toString())){
            Object[] args = joinPoint.getArgs();
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            CjzlMarkService cjzlMarkService = beanMap.get(signature.getMethod().getAnnotation(Log.class).handleType());
            cjzlMarkService.after(args,signature);
        }

    }

    //和方法一起执行 很少用
    public void aroundPointCut(){}
}

        3.工具类SpringUtils 用于获取Spring管理的bean

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class SpringFactoryUtils implements ApplicationContextAware {

    //Spring中的核心接口和容器,允许容器通过应用程序上下文环境创建、获取、管理bean
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringFactoryUtils.applicationContext = applicationContext;
    }

    /**
     * 获取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通过name获取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean.
     *
     * @param clazz
     * @param 
     * @return
     */
    public static  T getBean(Class clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param 
     * @return
     */
    public static  T getBean(String name, Class clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

    public static  Map getBeanMap(Class clazz) {
        return getApplicationContext().getBeansOfType(clazz);
    }
}

        4.返回类 JsonResult

import com.alibaba.fastjson.JSONObject;

public class JsonResult {

    private JsonResult() {
        throw new IllegalStateException("Utility class");
    }

    public static JSONObject ok(String msg, Object data) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("msg", msg);
        jsonObject.put("data", data);
        jsonObject.put("code", 200);
        return jsonObject;
    }

    public static JSONObject ok(String msg) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("msg", msg);
        jsonObject.put("code", 200);
        return jsonObject;
    }

    public static JSONObject not0k(String msg) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("msg", msg);
        jsonObject.put("code", 500);
        return jsonObject;

    }
}

        5.Service 前后执行什么

import com.example.demo2.annotation.Log;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Component
@Slf4j
public abstract class CjzlMarkService {
    public  void before(Object[] args, Log loga){
        log.info("handle:{},入参:{}",loga.handleType(),args);
    };
    public abstract void after(Object[] args, MethodSignature signature);
}

        6.实现类  我们可以写多个实现类 然后通过Componet里的value来区分  这个value  和 注解类 Log里面 的handleType是一致的

import com.example.demo2.annotation.Log;
import com.example.demo2.service.CjzlMarkService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Component("bmcx")
@Slf4j
public class bmcxCjzlService extends CjzlMarkService {
    @Override
    public void after(Object[] args, MethodSignature signature) {
        log.info("handle:{},入参:{}",signature.getMethod().getAnnotation(Log.class).handleType(),args);
    }
}

        7.测试

    @Log(handleType = "bmcx",cagn="部门查询")
    @GetMapping("getAll")
    public JSONObject getAll(){
        List userPos = userMapper.selectList(null);
        return JsonResult.ok("成功",userPos);
    }

        8.执行顺序  

                 @Before里面代码  方法里面代码 @AfterReturning里面的代码   @PointCut不会执行

你可能感兴趣的:(SpringBoot+AOP+自定义注解,实现日志记录)