spring 常见注解记录+ 使用自定义注解与aop 记录接口请求参数

注解定义:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Controller;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

ElementType 配置介绍:

TYPE
描述:类、接口(包括注解类型)、或枚举声明。
示例:@MyAnnotation public class MyClass {}
FIELD
描述:字段声明(包括枚举常量)。
示例:@MyAnnotation private int myField;
METHOD
描述:方法声明。
示例:@MyAnnotation public void myMethod() {}
PARAMETER
描述:形式参数声明。
示例:public void myMethod(@MyAnnotation String param) {}
CONSTRUCTOR
描述:构造器声明。
示例:@MyAnnotation public MyClass(int value) {}
LOCAL_VARIABLE
描述:局部变量声明(但注意,Java 注解在 Java 8 之前不能用于局部变量)。
示例(Java 8 及以上):void myMethod() { @MyAnnotation int localVar; }
ANNOTATION_TYPE
描述:注解类型声明。
示例:@MyAnnotation public @interface MyOtherAnnotation {}
PACKAGE
描述:包声明。从 Java 9 开始,注解可以应用于包声明。
示例(Java 9 及以上):@MyAnnotation package com.example;
TYPE_PARAMETER
描述:类型参数声明(Java 8 引入)。
示例:public class MyClass<@MyAnnotation T> {}
TYPE_USE
描述:类型的使用(Java 8 引入)。这允许注解应用于任何类型的使用,如泛型、强制类型转换等。
示例:public void myMethod(List<@MyAnnotation String> list) {}
// 注解的生命周期
@Retention(RetentionPolicy.RUNTIME)

/**
 * Annotation retention policy.  The constants of this enumerated type
 * describe the various policies for retaining annotations.  They are used
 * in conjunction with the {@link Retention} meta-annotation type to specify
 * how long annotations are to be retained.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
public enum RetentionPolicy {
    /**
     * 源码阶段存在
     */
    SOURCE,

    /**
     * 编译阶段存在
     */
    CLASS,

    /**
     * 运行阶段存在
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}
// 生成java doc文档 时是否增加该注解显示
@Documented
// 别名
@AliasFor 属性可互换, 如 attribute 和 value 写那个配置都一样
可参考:https://www.jb51.net/program/3149973yk.htm

public @interface AliasFor {
    @AliasFor("attribute")
    String value() default "";

    @AliasFor("value")
    String attribute() default "";

    Class<? extends Annotation> annotation() default Annotation.class;
}

常见注解解介绍:

@RestController = @ResponseBody + @Controller
该注解等同于 @ResponseBody + @Controller, 可以在controller 上使用,等同这两个注解。
@Controller
标注该类为控制器
@PostMapping
@GetMapping
标志请求的类型,请求路径
@RequestMapping
标注在类上,为该类所有方法增加请求路径
标注在方法上,指定方法请求路径,可配置请求类型 如 GET POST
@RequestMapping(value = "/test", method = RequestMethod.GET)
@RequestBody
指定在方法入参上,标识该参数通过 请求体传递,一般是json 串 POST请求传
@ResponseBody
指定返回数据为 web返回实体类型
@ControllerAdvice
统一的报错拦截处理,结合 @ExceptionHandler 可以对异常进行统一处理 如包装异常,日志打印等
@ComponentScan
	用在spring boot 启动类上,用于标注spring 扫描路径,如果没有被包含进去的bean,不会被spring 管理与创建,自动注入会报错
@Configuration
	用在类上,标注该类为配置类,在spring 启动时,会取读该类的配置,例如自定义webmvc 配置增加拦截器时会用到
@EnableConfigurationProperties
    @EnableConfigurationProperties注解的作用是:使 使用 @ConfigurationProperties 注解的类生效。
    如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component或者实现了@Component的其他注解,那么在IOC容器中是获取不到properties 	  配置文件转化的bean。
	说白了 @EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入。

@Import
    导入一个或多个Bean(可以实现不在 componentScan 标注路径下的bean包进行扫描)
	导入@Configuration类
    导入ImportSelector的实现类
    导入ImportBeanDefinitionRegistrar的实现类
@Primary
	当存在多个相同类型的Bean注入时,加上@Primary注解,指定更高的优先级。
@Qualifier
    存在多个相同类型的Bean注入时,加上@Qualifier注解,指定注入名称。
@Bean
    标注方法上,返回的结果指定为 bean, 被spring 容器管理
@Value
    标注在变量从上,从配置文件设置变量值,启动时未设置值,会报错
@Service
    标注该类为 service 类,能够会spring 容器管理 在 controller 中可以被自动注入 
@Component
    标注该类为组件类,可以能够会spring 容器管理 在 service等被自动注入
@RequestParam
    写在cotroller 的方法中,变量名默认为通过路径传参的参数名称
@Autowired
    对指定的对象进行自动注入,如果ioc 容器中不存在,则会报错 
@Deprecated
    标注该类/方法被废弃,使用时会有提示,无实际影响

使用spring 自定义注解 + aop 实现接口出入参记录:

注解定义:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 日志记录注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 模块名称
     */
    String modelName() default "";

}

注解切面逻辑:

import com.alibaba.fastjson.JSON;
import com.datadriver.log.annoations.Log;
import com.datadriver.log.dao.LogMapper;
import com.datadriver.log.entity.SystemLog;
import com.datadriver.spring.boot.security.SecurityUserDetails;
import com.datadriver.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

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

/**
 * @ClassName LogAspect
 * @Description 日志切面
 * @Author lvhui7
 * @Date 2024/8/29
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

    @Autowired
    private LogMapper logMapper;
    @Autowired
    private HttpServletRequest request;

    /**
     * 切入点
     */
    @Pointcut("@annotation(com.test.log.annoations.Log)")
    public void logPointCut() {
        // do nothing
    }
	// 环绕
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Date date = new Date();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - beginTime;
        this.saveLog(point, time, date);
        return result;
    }

    /**
     * 记录日志信息。
     * @param joinPoint 切点,用于获取方法签名和目标对象。
     * @param time 执行时间。
     */
    private void saveLog(ProceedingJoinPoint joinPoint, long time, Date beginTime) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            SystemLog systemLog = new SystemLog();
            systemLog.setId(StringUtil.getUUID());
            Log logAnnotation = method.getAnnotation(Log.class);
            if (logAnnotation != null) {
                systemLog.setModule(logAnnotation.modelName());
            }
            systemLog.setTime(time);
            // 获取类名
            String className = joinPoint.getTarget().getClass().getName();
            systemLog.setClassName(className);
            // 获取方法名
            String methodName = signature.getName();
            systemLog.setMethodName(methodName);
            // 从 SecurityContextHolder 中获取当前登录人的相关信息
            SecurityContext context = SecurityContextHolder.getContext();
            Authentication authentication = context.getAuthentication();
            SecurityUserDetails details = (SecurityUserDetails) authentication.getPrincipal();
            String username = details.getUsername();
            systemLog.setNotesId(username);
            Map<String, String[]> parameterMap = request.getParameterMap();
            systemLog.setIp(request.getRemoteAddr());
            systemLog.setParams(JSON.toJSONString(parameterMap));
            systemLog.setRequestedOn(beginTime);
            // 写入数据库
            logMapper.insertLog(systemLog);
        } catch (Exception e) {
            // 出现异常不抛出,避免回滚事务
            log.warn("日志记录异常:", e);
        }
    }
}

启动类增加aspectj 注解@EnableAspectJAutoProxypom 文件,增加相关依赖

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

在需要记录接口出入参的方法上加上注解即可:

    @Log(modelName =  "测试记录日志")
    @RequestMapping(value = "test", method = RequestMethod.POST)
    public ApiResult testMethod(Model model, 
    HttpServletRequest request, 
    @RequestParam("name") String name,
    @RequestParam("type") String type) {
    	// 省略业务逻辑
        return ApiResult.success();
    }

起始还有个地方可以优化一下,记录的请求,可以改成异步的,避免接口请求时间被加长了,不过项目请求量小,就不做处理了~

posted @ 2024-09-29 16:01  charler。  阅读(89)  评论(0编辑  收藏  举报