Java中自定义注解的使用

Java中自定义注解的使用

一般来说,市面上有一些的框架,企业都不会直接拿过来就用,通过会做二次开发或封装,为了更加适配自己的开发规范和业务。那么在封装或适配的过程中,自定义注解就起着比较重要的作用。

1 注解定义、原理及作用

1.1 什么是注解

Annotation(注解)是Java5引入的新特性。它提供了一种安全的类似于注解的机制,它可以用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员、变量等)进行关联。为程序的类、方法、成员变量等加上更加直观的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annotation就像一种修饰符一样,应用于包、类型、构造方法、普通方法、成员变量、参数及本地变量的声明语句中。

注解是附加在代码中的一些元信息,用于一些工具在编译、运行时,进行解析和使用,起到说明、配置的功能。
注解不会也不能影响代码的实际逻辑。仅仅起到辅助的作用。包含在java.lang.annotation包中。

1.2 注解的用处

  1. 生成文档。这是最常见的,也是java中提供最早的注释。例如:@param @return等
  2. 跟踪代码依赖性,实现替代配置文件功能。比如@Bean、@Service等,并且可以遇见的是未来的开发,将大量使用注解。
  3. 在编译时进行格式检查。如@Override放在方法上,如果该方法不是覆盖了父类的方法,则编译时就能检查出来。

1.3 注解的原理

注解的本质就是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类,而我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。

1.4 元注解

java.lang.anntotaion提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要用到元注解):

  • @Documented 注解是否将包含在JavaDoc中
  • @Retention 什么时候使用该注解
  • @Target 注解用于什么地方
  • @Inherited 是否允许子类继承该注解
  1. @Retention - 定义该注解的生命周期
- RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义。因此不会写入
  字节码。@Override,@SuppressWarnings,@Deprecated都属于这类注解。
- RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。`注解默认使用这种方式`
- RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的
  信息。我们`自定义的注解通常使用这种方式`
  1. Target - 表示该注解用于什么地方。默认值为任何元素。
- ElementType.CONSTRUCTOR: 用于描述构造器
- ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
- ElementType.LOCAL_VARIABLE: 用于描述局部变量
- ElementType.METHOD: 用于描述方法
- ElementType.PACKAGE: 用于描述包
- ElementType.PARAMETER: 用于描述参数
- ElementType.TYPE: 用于描述类、接口(包括注解类型) 或enum声明
  1. @Documented - 一个简单的Annotations标记注解,表示是否将注解信息添加到java文档中
  2. @Inherited - 定义该注解和子类的关系
@Inherited元注解是一个标记注解。阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited的注解
被加在了某个类上,那么这个注解将被用于该class的子类。

1.5 Java内置的三大注解(@Override、@Deprecated、@SuppressWarnings)

除上述四种元注解外,JDK预定了另外三种注解:

@Override
@Deprecated
@SuppressWarnings

1.)@Override

java.lang.Override 是一个标记类型注解,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种注解在一个没有覆盖父类方法的方法时,java 编译器将以一个编译错误来警示。

2.)@Deprecated

Deprecated 也是一种标记类型注解。当一个类型或者类型成员使用@Deprecated 修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的“延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated,但编译器仍然要报警。

  • 表明已经过时,不推荐使用

3.)@SuppressWarnings

SuppressWarning 不是一个标记类型注解。它有一个类型为String[] 的成员,这个成员的值为被禁止的警告名。对于javac 编译器来讲,被-Xlint 选项有效的警告名也同样对@SuppressWarings 有效,同时编译器忽略掉无法识别的警告名。@SuppressWarnings(“unchecked”)

  • 用于抑制编译器的检查。

2 自定义注解实战

自定义注解类编写的一些规则:

  1. Annotation 型定义为@interface, 所有的Annotation 会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
  2. 参数成员只能用public 或默认(default) 这两个访问权修饰
  3. 参数成员只能用基本类型byte、short、char、int、long、float、double、boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
  4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation 对象,因为你除此之外没有别的获取注解对象的方法
  5. 注解也可以没有定义成员,,不过这样注解就没啥用了

自定义注解需要使用到元注解

- 自定义注解会自动继承Annotation接口,无法在继承别的类或接口
- 参数只能用public或默认修饰
- 参数只能用八大基本数据类型+String+Enum等及该类型的数组
- 只能通过反射获取类方法和字段的注解信息
- 注解中可无成员,但那样就没有意义了

①自定义一个水果有关注解

@FruitName:

/**
 * 水果名称注解
 * @author 夏末
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {

    String value() default "";
}

@FruitColor:

/**
 * @author 夏末
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {

    //颜色枚举
    public enum Color{BLUE, RED, GREEN}

    //颜色属性,默认是绿色
    Color fruitColor() default Color.GREEN;
}

@FruitProvider:

/**
 * @author 夏末
 * @description TODO
 * @date 2022/9/29 11:38
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//水果供应商注解
public @interface FruitProvider {

    //水果供应商编号
    public int id() default -1;

    //水果供应商名称
    public String name() default "";

    //供应商地址
    public String address() default "";
}

②定义注解处理器FruitInfoHandler

/**
 * @author 夏末
 * @description 注解处理器
 * @date 2022/9/29 11:40
 */
public class FruitInfoHandler {

    public static void getFruitInfo(Class<?> clazz){
        String strFruitName = " 水果名称: ";
        String strFruitColor = " 水果颜色: ";
        String strFruitProvider = " 供应商信息: ";

        Field[] fields = clazz.getDeclaredFields();

        for(Field field : fields){
            if(field.isAnnotationPresent(FruitName.class)){
                //获取水果名
                FruitName fruitName = (FruitName)field.getAnnotation(FruitName.class);
                strFruitName = strFruitName + fruitName.value();
                System.out.println(strFruitName);
            } else if(field.isAnnotationPresent(FruitColor.class)){
                //获取水果颜色
                FruitColor fruitColor = field.getAnnotation(FruitColor.class);
                strFruitColor = strFruitColor + fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            } else if (field.isAnnotationPresent(FruitProvider.class)){
                FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class);
                strFruitProvider = "供应商编号: " + fruitProvider.id() + "供应商名称:" + fruitProvider.name() +
                        "供应商地址: " + fruitProvider.address();
                System.out.println(strFruitProvider);
            }
        }
    }
}

③使用注解(创建某个类并使用注解)

Apple.java:

/**
 * @author 夏末
 * @description 定义一个水果类,并使用注解
 * @date 2022/9/29 11:56
 */
public class Apple {

    @FruitName("Apple")
    private String appleName;

    @FruitColor(fruitColor = FruitColor.Color.RED)
    private String appleColor;

    @FruitProvider(id=1, name = "红富士集团", address = "长虹大道")
    private String appleProvider;

    public String getAppleName() {
        return appleName;
    }

    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }

    public String getAppleColor() {
        return appleColor;
    }

    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }

    public String getAppleProvider() {
        return appleProvider;
    }

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
}

④测试结果

public class Test {

    public static void main(String[] args) {
        FruitInfoHandler.getFruitInfo(Apple.class);
        // 水果名称: Apple
        // 水果颜色: RED
        //供应商编号: 1供应商名称:红富士集团供应商地址: 长虹大道
    }
}

参考文章:
https://www.cnblogs.com/UncleWang001/p/11044902.html
https://www.cnblogs.com/zhongqifeng/p/14693074.html

你可能感兴趣的:(实习,java,spring,annotation,自定义注解)