java自定义注解,以编写spring日志打印注解@ApiLog为例

1.声明一个注解

基本元素

描述

修饰符

访问修饰符必须为public,不写默认为pubic

关键字

关键字为@interface

注解名称

注解名称为自定义注解的名称,使用时还会用到

注解类型元素

注解类型元素是注解中内容,可以理解成自定义接口的实现部分

例:

public @interface ApiLog {
}

2.@Target修饰注解

@Target用以表明该注解可以应用的java元素类型

类型

描述

ElementType.TYPE

应用于类、接口(包括注解类型)、枚举

ElementType.FIELD

应用于属性(包括枚举中的常量)

ElementType.METHOD

应用于方法

ElementType.PARAMETER

应用于方法的形参

ElementType.CONSTRUCTOR

应用于构造函数

ElementType.LOCAL_VARIABLE

应用于局部变量

ElementType.ANNOTATION_TYPE

应用于注解类型

ElementType.PACKAGE

应用于包

ElementType.TYPE_PARAMETER

1.8版本新增,应用于类型变量

ElementType.TYPE_USE

1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)

例:

@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ApiLog {
}

3.@Retention修饰注解

类型

描述

RetentionPolicy.SOURCE

编译时被丢弃,不包含在类文件中

RetentionPolicy.CLASS

JVM加载时被丢弃,包含在类文件中,默认值

RetentionPolicy.RUNTIME

由JVM 加载,包含在类文件中,在运行时可以被获取到

例:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiLog {
}

4.@Document修饰注解

@Document表明该注解标记的元素可以被Javadoc 或类似的工具文档化

例:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLog {
}

5.@Inherited修饰注解

表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解

例:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ApiLog {
}

6.编写@ApiLog的实现切面类

例:

@Component
@Aspect
@Slf4j
public class MyApiLogAspect {

    @Pointcut("@annotation(boss.xtrain.log.api.ApiLog) || @within(boss.xtrain.log.api.ApiLog)")
    public void pointCut() {
        // 切点
    }

    /**
     * @description 打印请求报文和应答报文
     * @params [joinPoint]
     * @return java.lang.Object
     */
    @Around("pointCut()")
    public Object saveLog(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取当前时间
        long reqTime = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String currentTime = sdf.format(new Date(reqTime));
        // 获取请求ip
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = request.getRemoteAddr();
        // 获取调用方法和参数
        String method = joinPoint.getSignature().getDeclaringType() + "." + joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        // 获取全局流水号,全局流水号为本业务需要,若无请省略
        Class<?> tmpClass = args[0].getClass();
        Field field = tmpClass.getDeclaredField("globalMessageNo");
        field.setAccessible(true);
        Object gMessageNoObj = field.get(args[0]);
        String gMessageNoStr = null;
        if (null != gMessageNoObj) {
            gMessageNoStr = (String) gMessageNoObj;
        }
        // 打印请求报文
        log.info("请求报文:流水号:{} # 时间:{} # ip:{} # 调用方法:{} # 请求参数:>>>>>>>>>>{}", gMessageNoStr, currentTime, ip, method, args);
        // 获取应答结果
        Object result = null;
        result = joinPoint.proceed();
        // 获取处理时间
        long respTime = System.currentTimeMillis() - reqTime;
        // 打印应答报文
        log.info("应答报文:流水号:{} # 时间:{} # 响应时间:{}ms # 返回参数:<<<<<<<<<<{}", gMessageNoStr, currentTime, respTime, result);
        return result;
    }

}

作者:豫若涉川