Java Annotation详解 理解和使用Annotation

Wesley13
• 阅读 501

系统中用到了java注解:

查了一下如何使用注解,到底注解是什么;

(1)创建方法:MsgTrace Java Class==>

在Create New Class中:

name:输入MsgTrace;

Kind:Annotation;

就可以了;

public @interface MsgTrace {
    String traceId() default "";
}

 这个traceId是属性,认值是空,这样,如果使用这个注解的话,就不需要显式指定traceId,

如果用户在类上面添加了这个注解,那么就会走代理类;

代理类:

package com.sankuai.qcs.regulation.aop;

import com.dianping.cat.util.MetricHelper;
import com.meituan.mtrace.Tracer;
import lombok.Data;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 消息链路跟踪,主要记录过程时间
 * http请求是时间
 * 接口http请求次数
 *
 * @Author:chenzying
 * @Description:
 * @Date: Created in 上午11:14 2019/1/25
 * @Modified By:
 **/
@Aspect
@Component
public class MsgTraceAspect {

    private static final Integer MAX_TRACE_TIME = 1000;
    private static Map<String, MsgTraceAspect.TraceTime> timeMap = new HashMap<String, MsgTraceAspect.TraceTime>();
    private Logger logger = LoggerFactory.getLogger(MsgTraceAspect.class);

    @Pointcut("@annotation( com.sankuai.qcs.regulation.annotation.MsgTrace)")
    public void point() {
    }


    @Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        String traceId = "";

        try {
            traceId = Tracer.getServerSpan().getTraceId();
        } catch (Exception e) {

        }

        String functionName =
                String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
                        proceedingJoinPoint.getSignature().getName());


        long startTime = System.currentTimeMillis();


        Object o = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());

        long endTime = System.currentTimeMillis();

        long useTime = endTime - startTime;

        Object result = proceedingJoinPoint.proceed();

        cal(functionName, useTime, traceId);


        try {

            MetricHelper.build().name("Core_Function_Time").tag("function", functionName).duration(useTime);

            MetricHelper.build().name("Core_Function_QPS").tag("function", functionName).count();

            MetricHelper.build().name("Core_Function_QPS").tag("function", "ALL").count();


        } catch (Exception e) {

        }
        return o;
    }

    private void cal(String functionName, long useTime, String traceId) {
        if (timeMap.containsKey(functionName)) {
            TraceTime tracetime = timeMap.get(functionName);
            tracetime.setMax(tracetime.getMax() > useTime ? tracetime.getMax() : useTime);
            tracetime.setMin(tracetime.getMin() < useTime ? tracetime.getMin() : useTime);
            tracetime.setAvg(
                    (tracetime.getAvg() * tracetime.getUse() + useTime) / (tracetime.getUse() + 1));
            tracetime.setUse(tracetime.getUse() + 1);

            if (tracetime.getUse() > MAX_TRACE_TIME) {
                tracetime.setUse(MAX_TRACE_TIME);
            }


            logger.debug("{} trace: {} 用时 {} ms,max-{} ,min-{} ,avg-{} "
                    , functionName, traceId, useTime, tracetime.getMax()
                    , tracetime.getMin(), tracetime.getAvg());


        } else {
            TraceTime traceTime = new TraceTime();
            traceTime.setMin(useTime);
            traceTime.setMax(useTime);
            traceTime.setAvg(useTime);
            traceTime.setUse(1);
            timeMap.put(functionName, traceTime);

            logger.debug("{} trace: {} 用时 {} ms", functionName, traceId, useTime);
        }
    }


    @Data
    class TraceTime {
        private long min;
        private long max;
        private long avg;
        private long use;
    }
}

上面就是代理类,

代理类 最开始有个:@Aspect 代表了切面编程;

注意:

@Pointcut("@annotation( com.sankuai.qcs.regulation.annotation.MsgTrace)")
    public void point() {
    }

这就是说 切点在MsgTrace,如果你在任何方法上面添加了MsgTrace就会走point()这个方法;

因为这仅仅是个个类似于XML的配置,所以point为空了;

下面:

@Around("point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

Around方法,就是说,如果你在你的方法上面添加了MsgTrace,那么会产生一个代理类,围绕这个方法,执行自定义的方法;

以下是网络上的解释: 

JDK1.5之后,引入了元数据的概念,也就是Annotation(注释),其实它是代码里的特殊标记,这些标记可以再编译、类加载、运行时被读取,并执行相应的处理。

元数据的作用:

如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:

1.  编写文档:通过代码里标识的元数据生成文档。
2.  代码分析:通过代码里标识的元数据对代码进行分析。
3.  编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。

一、 系统内建的Annotation:

    @Override 覆写的Annotation

注释能实现编译时检查,你可以为你的方法添加该注释,以声明该方法是用于覆盖父类中的方法。如果该方法不是覆盖父类的方法,将会在编译时报错。例如我们为某类重写toString()方法却写成了tostring(),并且我们为该方法添加了@Override注释,则会提示编译错误。
     @Deprecated 不赞成使用的Annotation

其作用是对不应该在使用的方法添加注释,当编程人员使用这些方法时,将会在编译时显示提示信息,不推荐在使用该方法或该类。
    @SuppressWarnings 压制安全警告的Annotation

与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数值都是已经定义好了的,我们选择性的使用就好了,参数如下:
deprecation   使用了过时的类或方法时的警告
unchecked  执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型
fallthrough   当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
path   在类路径、源文件路径等中有不存在的路径时的警告
serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告
finally    任何 finally 子句不能正常完成时的警告
all 关于以上所有情况的警告

在为@SuppressWarnings设置注释信息的时候,是以key-value的形式出现的,所以以上的@SuppressWarnings也可以直接使用,所以@SuppressWarnings可以使用”value={"unchecked","deprecation"}“的方式来设置。

@Deprecated
class Demo{
private T var ;
public T getVar(){
return this.var ;
}
public void setVar(T var){
this.var = var ;
}
};
public class SuppressWarningsAnnotationDemo03{
// @SuppressWarnings(value={"unchecked","deprecation"})
public static void main(String args[]){
Demo d = new Demo() ;
d.setVar("沉缘") ;
System.out.println("内容:" + d.getVar()) ;
}
};

上面,我们将 注释掉,编译后,会出现警告提示:
---------- javac ----------
注: SuppressWarningsAnnotationDemo03.java使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
注: SuppressWarningsAnnotationDemo03.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。

打开@SuppressWarnings注释,再次编译,发现,警告已被抑制。

二、 自定义Annotation

定义简单的Annotation形式:

[public] @interface Annotation名称{

        数据类型  变量名称();

}

例如:

public @interface MyDefaultAnnotationNoneParam{
}
之后,就可以直接使用@Annotation名称:

@MyDefaultAnnotationNoneParam
class Demo
{
}
此时,就表示在Demo类上使用Annotation。

还可以向Annotation中设置变量,使用变量接受参数。

public @interface MyDefaultAnnotationSingleParam{
public String value(); //接受设置的参数
}
在使用的时候,必须清楚的指定变量的名称,变量的内容:

@MyDefaultAnnotationSingleParam("沉缘")
class Demo
{
}
或者使用明确的标记,表示内容赋给哪个参数:

@MyDefaultAnnotationSingleParam(value="沉缘")
class Demo
{
}
以上的参数,是要赋给value属性的。既然可以设置一个参数,则也就可以同时设置多个参数。

public @interface MyDefaultAnnotationMoreParam{
public String key() ;
public String value() ; // 接收设置的内容
}
此Annotation在使用时,需要设置两个参数,一个key,一个value。

@MyDefaultAnnotationMoreParam(key="Linkage",value="沉缘")
class Demo{
};
当然,我们可以设置数组进去,@SuppressWarnings就使用了数组。

public @interface MyDefaultAnnotationArrayParam{
public String[] value() ; // 接收设置的内容
}
接收内容本身是一个数组类型,要传递数组。

@MyDefaultAnnotationArrayParam(value={"沉缘","流烬"})
class Demo{
};
以上的定义Annotation都未指定属性的默认值,必须在使用时设置。 其实,也可以直接使用default来定义默认值:

public @interface MyDefaultAnnotationValue{
public String key() default "Linkage" ; // 指定好了默认值
public String value() default "沉缘" ; // 指定好了默认值
}
在实际的操作中,对于一个Annotation而言,有时候会固定其取值范围,只能使用固定的几个值。那么这时候实际上就需要依靠枚举:

public enum MyName{ // 定义枚举类型
WUQING,WUYUAN,WULEI ;
}

public @interface MyDefaultAnnotationEnum{
public MyName name() default MyName.WUQING ; // 指定默认值
}

三、 限定注释使用范围Target

当我们的自定义注释不断的增多也比较复杂时,就会导致有些开发人员使用错误,主要表现在不该使用该注释的地方使用。为此,Java提供了一个ElementType枚举类型来控制每个注释的使用范围,比如说某些注释只能用于普通方法,而不能用于构造函数等。下面是Java定义的ElementType枚举:

package java.lang.annotation;

public enum ElementType {

TYPE, // Class, interface, or enum (but not annotation)

FIELD, // Field (including enumerated values)

METHOD, // Method (does not include constructors)

PARAMETER, // Method parameter

CONSTRUCTOR, // Constructor

LOCAL_VARIABLE, // Local variable or catch clause

ANNOTATION_TYPE, // Annotation Types (meta-annotations)

PACKAGE // Java package

}
想要使用ElementType,只需要为注释添加@Target即可:

@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR })

public @interface TargetTest {

}

正如上面代码所展示的,我们只允许Greeting注释标注在普通方法和构造函数上,使用在包申明、类名等时,会提示错误信息。

四、 Retention和RetentionPolicy,注释保持性策略

public enum RetentionPolicy {

SOURCE,// Annotation is discarded by the compiler

CLASS,// Annotation is stored in the class file, but ignored by the VM

RUNTIME// Annotation is stored in the class file and read by the VM

}

RetentionPolicy的使用方法的简单代码示例如下:

@Retention(RetentionPolicy.RUNTIME)
而,在RetentionPolicy的三个范围中,最需要注意的就是RUNTIME范围,因为在执行的时候起作用。

import java.lang.annotation.Retention ;
import java.lang.annotation.RetentionPolicy ;
@Retention(value=RetentionPolicy.RUNTIME) // 表示此Annotation在运行时有效
public @interface MyDefaultRententionAnnotation{
public String name() default "沉缘" ;
}

我们看下内建的Annotation的RetentionPolicy:
@Override定义采用的是@Retention(value=SOURCE),只能在源文件中出现。

@Deprecated定义采用的是@Retention(value=RUNTIME),可以在执行时出现。

@SuppressWarnings定义采用的是@Retention(value=SOURCE),只能在源文件中出现。

五、 文档化功能

Java提供的Documented元注释跟Javadoc的作用是差不多的,其实它存在的好处是开发人员可以定制Javadoc不支持的文档属性,并在开发中应用。它的使用跟前两个也是一样的,简单代码示例如下:

import java.lang.annotation.Documented ;
@Documented
public @interface MyDocumentedAnntation{
public String key() default "Linkage" ;
public String value() default "沉缘" ;
}

成功后,在使用此Annotation的时候,可以增加一些信息进去:

@MyDocumentedAnntation(key="Baidu",value="www.baidu.com")
public class SimpleBeanDocumented{
/**
* 此方法在对象输出时调用,返回对象信息
*/
@MyDocumentedAnntation(key="Xinlang",value="www.sina.com")
public String toString(){
return "Hello World!!!" ;
}
};

之后,在生成jdk文档的时候,使用@Document修饰的方法将被注释下来。
六、 标注继承

继承应该是Java提供的最复杂的一个元注释了,它的作用是控制注释是否会影响到子类(一个Annotation是否可以被继承下来),简单代码示例如下:

package com.test.inheriteddemo ;
import java.lang.annotation.Retention ;
import java.lang.annotation.RetentionPolicy ;
import java.lang.annotation.Documented ;
import java.lang.annotation.Inherited ;
@Documented
@Inherited
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyInheritedAnnotation{
public String name() ;

}
使用该注释,标注一个父类Person:

package com.test.inheriteddemo ;
@MyInheritedAnnotation(name="沉缘")
public class Person{
};

按照所解释的,使用Inherited声明的Annotation是可以被子类继承下来的:
import java.lang.annotation.Annotation ;
import org.lxh.demo16.inheriteddemo.MyInheritedAnnotation ;
public class ReflectInheritedDemo{
public static void main(String args[]) throws Exception{
Class<?> c = null ;
c = Class.forName("com.test.inheriteddemo.Student") ;
Annotation ann[] = c.getAnnotations() ; // 取得全部的Annotation
for(Annotation a:ann){ // 输出
System.out.println(a) ;
}
// 继续取得此Annotation设置的内容
if(c.isAnnotationPresent(MyInheritedAnnotation.class)){
MyInheritedAnnotation mda = null ;
mda = c.getAnnotation(MyInheritedAnnotation.class) ;
String name = mda.name() ; // 取出name的内容
System.out.println("name = " + name) ;
}
}

参考:Java Annotation详解(一): 理解和使用Annotation

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
Java元注解
1、注解数据类型注解是写在.java文件中,使用@interface作为关键字,所以注解也是Java的一种数据类型,从广泛的定义来说,Class、Interface、Enum、Annotation都属于Class类型。2、元注解在创建注解的时候,需要使用一些注解来描述自己创建的注解(即注解的注解),就是写在@interfac
Wesley13 Wesley13
3年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这