java中如何自定义注解

做为一名开发人员,注解的的使用是最多见的了,好比Spring框架里的业务层注解@Service、@Transaction,控制层用的@Controller、@Autowired,SpringBoot框架的启动类注解@SpringBootApplication等等。那么如何自定义注解呢?java

1、什么是注解

注解(Annotation)是元数据的一种形式,从JDK5.0 引入,它能为代码提供一些相关数据,以便于在代码编译或运行时使用。spring

2、注解的做用

Java中的注解能够修饰类、方法、变量、参数等。注解能够经过反射手段获取其内容,在编译器生成类文件时,注解能够被嵌入到字节码中。固然JVM也能够保留注解的内容,在运行时动态,总结起来主要是如下几个层面:数组

  • 编译器根据注解在编译代码时行进行提示警告或错误信息 例如咱们在使用java.util包下的Date类时,调用了类中被@Deprecated标注的方法,IDE会在编译时会有警告信息
public static void main(String[] args) {
    Date date = new Date();
    //JDK源码中,Date类的getDay方法被@Deprecated注解标注,表明方法已过期
    int day = date.getDay();
}
复制代码
  • 编译运行时时根据注解动态生成代码 例如在springboot框架中,咱们实现一个Controller层方法的前置通知,经过使用@Aspect、@Before等注解便可,固然这些注解是框架封装好的,屏蔽了底层的细节,可是AOP的原理,你们应该都很熟悉
@Slf4j
@Component
@Aspect
public class MyAspect {
     
    @Before(value = "execution(public * com.test.controller.*.*(..))")
    public void before(JoinPoint joinPoint) {
        log.info("CLASS_METHOD:[{}]" , joinPoint.getSignature().getName());
    }
}
复制代码
  • 程序运行时使用注解进行动态赋值 好比经过使用@Value注解,将配置文件参数赋给代码里面变量,例如SpringBoot里面集成RabbitMq时,帐户密码等配置信息经过注解进行配置
@Configuration
public class RabbitMqConfig {

    @Value("${spring.rabbitmq.host}")
    private String rabbitMqHost;

    @Value("${spring.rabbitmq.port}")
    private String rabbitMqPort;
}
复制代码

3、自定义注解

首先咱们先看下一个注解示例,下面是javafx.beans包下的@DefaultProperty注解 :springboot

package javafx.beans;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** * Specifies a property to which child elements will be added or set when an * explicit property is not given. * * @since JavaFX 2.0 */
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DefaultProperty {
    /** * The name of the default property. */
    public String value();
}
复制代码

咱们看到定义注解与定义一个class类类似,不过其中class关键字被替换为了@interface,注解里声明了一个String类型的value属性,注意这种声明属性方式!稍后会详细说明。同时还能够看到DefaultProperty注解被@Inherited、@Documented、@Retention(RetentionPolicy.RUNTIME)、@Target(ElementType.TYPE)这些注解所修饰,这就是咱们须要知道另一个概念——元注解(meta-annotations)。bash

1.元注解

元注解是咱们在定义注解时须要用到的一些特殊含义的注解,能够说它们是对声明注解时的注解。java语言为咱们默认提供了如下元注解,在java.lang.annotation包下,咱们来看下:框架

  • @Retention(RetentionPolicy.XX) 注解的保留域,表示注解的保留范围,可选项有ide

    • RetentionPolicy.SOURCE – 源代码级别保留,编译器编译后该类型的注解就被丢弃掉了,生成的.class字节码文件中,将再也不存在该类型的注解.
    • RetentionPolicy.CLASS – .class字节码文件中保留,编译器编译后保留,JVM加载后丢弃掉,运行时没法获取
    • RetentionPolicy.RUNTIME – 运行时保留,在运行时,JVM使用反射,能够获取注解属性内容,绝大多数注解在定义是使用都是该级别
  • @Target( ElementType.XX) 指定该注解可使用的地方,如类声明、方法声明,变量声明等等, 在定义注解时,若是没有使用Target指定,默认均可以使用。若是使用了Target指定使用的位置,那么该注解只能在所指定的位置使用工具

    • ElementType.ANNOTATION_TYPE 注解类型声明
    • ElementType.CONSTRUCTOR 构造方法
    • ElementType.FIELD 字段声明(包括枚举常量)
    • ElementType.LOCAL_VARIABLE 局部变量声明
    • ElementType.METHOD 方法声明
    • ElementType.PACKAGE 包声明
    • ElementType.PARAMETER 方法的参数声明
    • ElementType.TYPE 类、接口(包括注解类型)或enum声明
  • @Documented 表示在使用Javadoc工具生成文档时,包含此注解信息spa

  • @Inherited 表示当前注解是可继承的,父类中所使用的注解若是被@Inherited修饰,子类会继承父类中对应的注解code

2.注解属性定义方式

明白了注解的外在定义形式,那么咱们就来看下注解内部的属性的定义方式,Talk is cheap. Show me the code 多说无益,直接上代码

@Documented
@Inherited
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    /** *一、属性的定义和接口中方法声明相似,访问修饰符默认是public,可省略,注意属性名后面跟了() */
    String name();
    
    /** * 二、能够经过default为属性指定默认值,在注解使用时,能够为之赋值, 也能够不赋值 * 固然若是不经过default为属性指定默认值,在注解使用必须使用该属性而且为之赋值 */
    int age() default 1;
    
    /** * 三、注解中的属性value比较特殊,若是使用注解时仅为该属性赋值,"value="能够省略掉, * 可是若是和其余属性同时赋值,“value=”则不能省略,这个特性和value的属性类型无关 */
    String value() default "";
   
    /** * 4.注解中属性的类型能够是基本数据类型及其数组、类、枚举、注解 */
    boolean sex() default true;
    
}

复制代码

4、经过反射获取注解属性值

在上面代码中,咱们自定义了@MyAnnotation注解,而且指明@Target({ElementType.METHOD})代表此注解只能用在方法声明上, @Retention(RetentionPolicy.RUNTIME)指定其保留到代码运行时,因此咱们能够经过反射获取MyAnnotation的属性值。下面咱们定义了一个Person类,并在其中定义了一个sayHello方法,在方法声明上,咱们使用@MyAnnotation注解,咱们将使用反射调用sayHello方法,而且使用MyAnnotation注解中的属性值

package com.test.annotation;
import com.test.enu.Color;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Person {

    @MyAnnotation(name="韩梅梅",age = 30,sex = true,clothes = Color.YELLOW)
    public void sayHello(String name){
        System.out.println("Hello!" + name);
    }

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        //获取Person类的Class对象
        Class<? extends Person> personClass = person.getClass();
        //获取类中声明的方法列表
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            //判断当前方法是否含有MyAnnotation注解
            if(method.isAnnotationPresent(MyAnnotation.class)){
                //获取MyAnnotation类型注解
                MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
                //反射调用方法,并传递注解name属性值为参数
                Object invoke = method.invoke(person,myAnnotation.name());
                //打印注解中定义的各个类型的值
                System.out.println(myAnnotation);
                System.out.println(myAnnotation.name()+","+myAnnotation.sex()+","+myAnnotation.age());
            }
        }
    }
}

复制代码

此时的IDE控制台输出,说明咱们经过反射在运行时获取到了@MyAnnotation注解的值

Hello!韩梅梅
韩梅梅,true,30
@com.test.annotation.MyAnnotation(value=, age=30, sex=true, name=韩梅梅, clothes=YELLOW)
复制代码

5、JDK内部自带注解

JDK自带了一些原先定义好的注解,咱们能够直接使用

  • @Override 表示当前方法覆盖了父类的方法
  • @Deprecation 表示方法已通过时,方法上有横线,使用时会有警告。
  • @SuppviseWarnings 表示关闭一些警告信息(通知java编译器忽略特定的编译警告)

至于注解内部详细内容,你们能够点进去源码查看。但愿本篇文章对你有所帮助!

相关文章
相关标签/搜索