Java中怎么自定义注解

发布时间:2021-07-29 15:45:34 作者:Leah
来源:亿速云 阅读:137

这篇文章将为大家详细讲解有关Java中怎么自定义注解,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

Java基础,注解与自定义注解

Java 注解Annotation,是 JDK5.0 引入的一种注释机制。

一、自带注解

在学习自定义注解前,先了解一下Java内部定义的一套注解:共有7个,3个在java.lang中,剩下的四个在java.lang.annotation中。

作用在类或者方法上

@Override  检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

@Deprecated  标记已过时方法。不推荐使用

@SuppressWarnings  指示编译器去忽略注解中声明的警告

作用在其他注解上

@Retention  标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。

@Documented  标记这些注解是否包含在用户文档中

@Target  标记这个注解应该是哪种 Java 成员

@Inherited  标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

额外添加的3个注解:

@SafeVarargs  Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

@FunctionalInterface  Java 8 开始支持,标识一个匿名函数或函数式接口。

@Repeatable  Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

二、注解结构

Java中怎么自定义注解

Annotation有三个重要的主干类,分别为:Annotation、ElememtType、RetentionPolicy

1、Annotation:

public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

2、ElementType:

其指定Annotation的类型,如METHOD ,则该Annotation只能修饰方法

public enum ElementType {

    TYPE,					//类、接口、枚举
		
    FIELD,					//字段、枚举常量

    METHOD,					//方法

    PARAMETER,				//参数

    CONSTRUCTOR,			//构造方法

    LOCAL_VARIABLE,			//局部变量

    ANNOTATION_TYPE,		//注释类型

    PACKAGE,				//包

    TYPE_PARAMETER,

    TYPE_USE
}

3、RetentionPolicy:

public enum RetentionPolicy {
    
    SOURCE,		//Annotation仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了

    CLASS,		//编译器将Annotation存储于类对应的.class文件中。默认行为

    RUNTIME		//编译器将Annotation存储于class文件中,并且可由JVM读入
        
}

RUNTIME:注释将由编译器记录在类文件中,并由VM在运行时保留,因此它们可以被反射读取。

CLASS:注释将由编译器记录在类文件中,但不需要在运行时由VM保留。

4、例子

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan{
    
    
}

@interface

@interface定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。而不是声明了一个interface。(@interface和interface是不同的)。

定义 Annotation 时,@interface 是必须的。

注意:它和我们通常的 implemented 实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过 @interface 定义注解后,该注解不能继承其他的注解或接口。

@Document

类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。

@Target

ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。

@Target(ElementType.TYPE) 的意思就是指定该 Annotation 的类型是 ElementType.TYPE。这就意味着,ComponentScan 是来修饰"类、接口(包括注释类型)或枚举声明"的注解。

@Retention

RetentionPolicy 是 Annotation 的策略属性,而 @Retention 的作用,就是指定 Annotation 的策略属性。

@Retention(RetentionPolicy.RUNTIME) 的意思就是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这就意味着,编译器会将该 Annotation 信息保留在 .class 文件中,并且能被虚拟机读取。

注意:定义 Annotation 时,@Retention 可有可无。若没有 @Retention,则默认是 RetentionPolicy.CLASS。

三、自定义Annotation

我们下面自定义一个Log日志注解:一个 在 controller的method上的注解,该注解会将用户对这个方法的操作 记录到数据库中

3.1、操作类型

package com.lee.common.enums;

/**
 * 业务操作类型
 * 
 */
public enum BusinessType
{
    /**
     * 其它
     */
    OTHER,

    /**
     * 新增
     */
    INSERT,

    /**
     * 修改
     */
    UPDATE,

    /**
     * 删除
     */
    DELETE,

    /**
     * 授权
     */
    GRANT,

    /**
     * 导出
     */
    EXPORT,

    /**
     * 导入
     */
    IMPORT,

    /**
     * 强退
     */
    FORCE,

    /**
     * 生成代码
     */
    GENCODE,
    
    /**
     * 清空数据
     */
    CLEAN,
}

3.2、自定义注解

package com.lee.common.annotation;

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 com.lee.common.enums.BusinessType;
import com.lee.common.enums.OperatorType;

/**
 * 自定义操作日志记录注解
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
    /**
     * 模块 
     */
    public String title() default "";

    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;

    /**
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;

    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;
}

3.3、日志处理Aspect

package com.lee.framework.aspectj;

import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import com.alibaba.fastjson.JSON;
import com.lee.common.annotation.Log;
import com.lee.common.core.domain.model.LoginUser;
import com.lee.common.enums.BusinessStatus;
import com.lee.common.enums.HttpMethod;
import com.lee.common.utils.ServletUtils;
import com.lee.common.utils.StringUtils;
import com.lee.common.utils.ip.IpUtils;
import com.lee.common.utils.spring.SpringUtils;
import com.lee.framework.manager.AsyncManager;
import com.lee.framework.manager.factory.AsyncFactory;
import com.lee.framework.web.service.TokenService;
import com.lee.system.domain.SysOperLog;

/**
 * 操作日志记录处理
 * 
 * @author lee
 */
@Aspect
@Component
public class LogAspect
{
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    // 配置织入点
    @Pointcut("@annotation(com.lee.common.annotation.Log)")
    public void logPointCut()
    {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
    {
        handleLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作
     * 
     * @param joinPoint 切点
     * @param e 异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e)
    {
        handleLog(joinPoint, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult)
    {
        try
        {
            // 获得注解
            Log controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null)
            {
                return;
            }

            // 获取当前的用户
            LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());

            // *========数据库日志=========*//
            SysOperLog operLog = new SysOperLog();
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            // 请求的地址
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            operLog.setOperIp(ip);
            // 返回参数
            operLog.setJsonResult(JSON.toJSONString(jsonResult));

            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
            if (loginUser != null)
            {
                operLog.setOperName(loginUser.getUsername());
            }

            if (e != null)
            {
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog);
            // 保存数据库
            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
        }
        catch (Exception exp)
        {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     * 
     * @param log 日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception
    {
        // 设置action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().ordinal());
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData())
        {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, operLog);
        }
    }

    /**
     * 获取请求的参数,放到log中
     * 
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
    {
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
        {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        }
        else
        {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
    {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null)
        {
            return method.getAnnotation(Log.class);
        }
        return null;
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (int i = 0; i < paramsArray.length; i++)
            {
                if (!isFilterObject(paramsArray[i]))
                {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     * 
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    public boolean isFilterObject(final Object o)
    {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}

3.4、注解的使用

/**
 * 修改推荐位
 */
@Log(title = "推荐位", businessType = BusinessType.UPDATE)
@PutMapping("/edit")
public AjaxResult edit(@RequestBody GsRecommPosition gsRecommPosition)
{
    Long gsRecommPositionId = gsRecommPosition.getGsRecommPositionId();
    boolean condition = recommPositionHasCategory(gsRecommPositionId);
    return toAjax(gsRecommPositionService.updateGsRecommPosition(gsRecommPosition));
}

/**
  * 删除推荐位
  */
@Log(title = "推荐位", businessType = BusinessType.DELETE)
@DeleteMapping("delete/{gsRecommPositionId}")
public AjaxResult remove(@PathVariable Long gsRecommPositionId)
{
    boolean condition = recommPositionHasCategory(gsRecommPositionId);
    return toAjax(gsRecommPositionService.deleteGsRecommPositionById(gsRecommPositionId));
}

四、JoinPoint解析

1、JoinPoint

对象封装了SpringAop切面方法中的信息

方法名功能
Signature getSignature();获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();获取传入目标方法的参数对象
Object getTarget();获取被代理的对象
Object getThis();获取代理对象
joinPoint.getSignature().getName(); // 获取目标方法名

joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名

joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名

joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)

Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组

joinPoint.getTarget(); // 获取被代理的对象

joinPoint.getThis(); // 获取代理对象自己




//获取自定义注解的信息
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
CustomLog customLog = methodSignature.getMethod().getAnnotation(CustomLog.class);

2、ProceedingJoinPoint

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中

Object proceed() throws Throwable //执行目标方法 

Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法

关于Java中怎么自定义注解就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

推荐阅读:
  1. java 自定义注解
  2. Java中自定义注解类及使用实例解析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:flume中如何自定义Interceptor

下一篇:markdown中怎么自定义排版文章

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》