千家信息网

Java中怎么自定义注解

发表于:2024-10-12 作者:千家信息网编辑
千家信息网最后更新 2024年10月12日,这篇文章将为大家详细讲解有关Java中怎么自定义注解,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Java基础,注解与自定义注解Java 注解Anno
千家信息网最后更新 2024年10月12日Java中怎么自定义注解

这篇文章将为大家详细讲解有关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 开始支持,标识某注解可以在同一个声明上使用多次。

二、注解结构

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

1、Annotation:

public interface Annotation {    boolean equals(Object obj);    int hashCode();    String toString();    Class 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)@Documentedpublic @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@Componentpublic 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中怎么自定义注解就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0