专栏名称: Java极客技术
Java 人的社区,专注 Java 一百年!
51好读  ›  专栏  ›  Java极客技术

天天都在使用的 Java 注解,你真的了解它吗?

Java极客技术  · 公众号  ·  · 2020-11-20 07:30

正文

每天早上 七点三十 ,准时推送干货



Hello,大家好,我是阿粉,Java 的注解相信大家天天都在用,但是关于注解的原理,大家都了解吗?这篇文章通过意见简单的示例给大家演示一下注解的使用和原理。

Java 元注解

注解(Annotation)是一种可以放在 Java 类上,方法上,属性上,参数前面的一种特殊的注释,用来注释注解的注解叫做元注解。元注解我们平常不会编写,只需要添加到我们自己编写的注解上即可,。

Java 自带的常用的元注解有 @Target @Retention @Documented @Inherited 分别有如下含义

  1. @Target :标记这个注解使用的地方,取值范围在枚举 java.lang.annotation.ElementType TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE
  2. @Retention :标识这个注解的生命周期,取值范围在枚举 java.lang.annotation.RetentionPolicy SOURCE,CLASS,RUNTIME ,一般定义的注解都是在运行时使用,所有要用 @Retention(RetentionPolicy.RUNTIME) ;
  3. @Documented :表示注解是否包含到文档中。
  4. @Inherited :使用 @Inherited 定义子类是否可继承父类定义的 Annotation @Inherited 仅针对 @Target(ElementType.TYPE) 类型的 annotation 有效,并且仅针对 class 的继承,对 interface 的继承无效。

定义注解

上面介绍了几个元注解,下面我们定义一个日志注解来演示一下,我们通过定义一个名为 OperationLog 的注解来记录一些通用的操作日志,比如记录什么时候什么人查询的哪个表的数据或者新增了什么数据。编写注解我们用的是 @interface 关键字,相关代码如下:

package com.api.annotation;

import java.lang.annotation.*;

/**
 * 

 * Function:

 * Author:@author 子悠

 * Date:2020-11-17 22:10

 * Desc:用于记录操作日志

 */

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {

    /**
     * 操作类型
     *
     * @return
     */

    String type() default OperationType.SELECT;

    /**
     * 操作说明
     *
     * @return
     */

    String desc() default "";

    /**
     * 请求路径
     *
     * @return
     */

    String path() default "";

    /**
     * 是否记录日志,默认是
     *
     * @return
     */

    boolean write() default true;

    /**
     * 是否需要登录信息
     *
     * @return
     */

    boolean auth() default true;
   /**
     * 当 type 为 save 时必须
     *
     * @return
     */

    String primaryKey() default "";

    /**
     * 对应 service 的 Class
     *
     * @return
     */

    Class> defaultServiceClass() default Object.class;
}

说明

上面的注解,我们增加了 @Target({ElementType.METHOD}) , @Retention(RetentionPolicy.RUNTIME) , @Documented 三个元注解,表示我们这个注解是使用在方法上的,并且生命周期是运行时,而且可以记录到文档中。然后我们可以看到定义注解采用的u是 @interface 关键字,并且我们给这个注解定义了几个属性,同时设置了默认值。主要注意的是平时我们编写的注解一般必须设置 @Target @Retention ,而且 @Retention 一般设置为 RUNTIME ,这是因为我们自定义的注解通常要求在运行期读取,另外一般情况下,不必写 @Inherited

使用

上面的动作只是把注解定义出来了,但是光光定义出来是没有用的,必须有一个地方读取解析,才能提现出注解的价值,我们就采用 Spring 的 AOP 拦截这个注解,将所有携带这个注解的方法所进行的操作都记录下来。

package com.api.config;

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.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

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

/**
 * 

 * Function:

 * Author:@author 子悠

 * Date:2020-11-17 14:40

 * Desc:aspect for operation log

 */

@Aspect
@Component
@Order(-5)
@Slf4j
public class LogAspect {
    /**
     * Pointcut for methods which need to record operate log
     */

    @Pointcut("within(com.xx.yy.controller..*) && @annotation(com.api.annotation.OperationLog)")
    public void logAspect() {
    }

    /**
     * record log for Admin and DSP
     *
     * @param joinPoint parameter
     * @return result
     * @throws Throwable
     */

    @Around("logAspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object proceed = null;
        String classType = joinPoint.getTarget().getClass().getName();
        Class> targetCls = Class.forName(classType);
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
        OperationLog operation = targetMethod.getAnnotation(OperationLog.class);
        if (null != operation && operation.write()) {
            SysMenuOpLogEntity opLogEntity = new SysMenuOpLogEntity();
            StringBuilder change = new StringBuilder();
            if (StrUtil.isNotBlank(operation.type())) {
                switch (operation.type()) {
                    case OperationType.ADD:
                        proceed = joinPoint.proceed();
                        String addString = genAddData(targetCls, operation.defaultServiceClass(), joinPoint.getArgs());
                        opLogEntity.setAfterJson(addString);
                        change.append(OperationType.ADD);
                        break;
                    case OperationType.DELETE:
                        String deleteString = autoQueryDeletedData(targetCls, operation.primaryKey(), operation.defaultServiceClass(), joinPoint.getArgs());
                        opLogEntity.setBeforeJson(deleteString);
                        change.append(OperationType.DELETE);
                        proceed = joinPoint.proceed();
                        break;
                    case OperationType.EDIT:
                        change.append(OperationType.EDIT);
                        setOpLogEntity(opLogEntity, targetCls, operation.primaryKey(), operation.defaultServiceClass(), joinPoint.getArgs());
                        proceed = joinPoint.proceed();
                        break;
                    case OperationType.SELECT:
                        opLogEntity.setBeforeJson(getQueryString(targetCls, operation.defaultServiceClass(), joinPoint.getArgs()));
                        change.append(operation.type());
                        proceed = joinPoint.proceed();
                        break;
                    case OperationType.SAVE:
                        savedDataOpLog(opLogEntity, targetCls, operation.primaryKey(), operation.defaultServiceClass(), joinPoint.getArgs());
                        change.append(operation.type());
                        proceed = joinPoint.proceed();
                        break;
                    case OperationType.EXPORT:
                    case OperationType.DOWNLOAD:
                        change.append(operation.type());
                        proceed = joinPoint.proceed();
                        break;
                    default:
                }
                opLogEntity.setExecType(operation.type());
            }
            StringBuilder changing = new StringBuilder();
            if (StrUtil.isNotBlank(opLogEntity.getExecType())) {
                if (operation.auth()) {
                    LoginUserVO loginUser = getLoginUser();
                    if (null != loginUser) {
                        opLogEntity.setUserId(loginUser.getUserId());
                        opLogEntity.setUserName(loginUser.getUserName());
                        changing.append(loginUser.getUserName()).append("-"






请到「今天看啥」查看全文