探索 Java 注解

Java 注解让我们可以在代码中添加信息元数据,这样这些元数据就可以实现与源代码放在一处管理与维护,而且还可以表述程序所需的信息。

1 标准注解

Java 定义了 5 种标准注解,具体说明如下:

注解JDK 版本说明
@OverrideJDK5表示当前定义的方法将覆盖基类定义的方法。
@DeprecatedJDK5表示当前定义的元素已过时,如果有其它元素调用该元素,那么编译器会记录警告信息。
@SuppressWarningsJDK5表示关闭当前定义元素的编译器警告信息。
@SafeVarargsJDK7表示关闭对调用具有泛型varargs参数的方法或构造函数的编译器警告信息。
@FunctionalInterfaceJDK8表示类型声明为函数式接口。

2 元注解

Java 定义了 5 种元注解,具体说明如下:

注解说明可用值
@Target表示注解可以用于哪些地方。CONSTRUCTOR:构造器;FIELD:字段(包括 enum 实例);LOCAL_VARIABLE:局部变量;METHOD:方法;PACKAGE:包;PARAMETER:参数;TYPE:类、接口(包括注解类型)或者 enum
@Retention表示注解信息存活范围SOURCE:注解只在源代码中,编译器会将其丢弃;CLASS:注解在 class 文件中可用,VM 会将其丢弃;RUNTIME:注解在 VM 运行期也可用,因此,我们可以通过反射机制读取注解信息。
@Documented该注解可保存在 Javadoc 中
@Inherited子类可继承父类的注解
@Repeatable允许该注解被多次使用

3 自定义注解

利用注解语法,我们就可以自行定义注解。假设我们定义一个可以在其中编写 SQL Select 语句的注解:

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

/**
 * Select 语句注解
 * <p/>
 *
 * @author Deniro Lee
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
    String sql();
    String desc() default "无描述";
}

自定义注解很像接口定义,它使用了 @interface 作为描述符,最终会被编译成 class 文件。

注解可以包含元素,比如上述示例中的 Select 注解,就定义了 sql() 与 desc() 两个元素。当然也可以不包含元素,不包含任何元素的注解称为标记注解(marker annotation)。

desc 元素定义了一个 default 值,即默认值。如果在使用该注解时,没有实际给出 desc 的值,则注解处理器会使用这里定义的默认值。

注解定义好后,可以这样使用:

/**
 * 账号 DAO
 * <p/>
 *
 * @author Deniro Lee
 */
public interface UserDao {

    @Select(sql = "select * from t_account where name='deniro'", desc = "依据用户名获取账号")
    public void findUserByName();

    @Select(sql = "select * from t_account where id='1'")
    public void findUserById();
}

以 “名-值”对的形式使用自定义注解元素,,这些元素需要放置在 @Select 声明之后的括号内。

4 处理自定义注解

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Method;

/**
 * @Select 注解处理器
 * <p/>
 *
 * @author Deniro Lee
 */
public class UserDaoTracker {

    private static final Logger logger = LogManager.getFormatterLogger();


    public static void main(String[] args) {
        for(Method m: UserDao.class.getDeclaredMethods()){
            Select select=m.getAnnotation(Select.class);
            if(select!=null){
                logger.info("查询语句 -> %s ;描述 -> %s", select.sql(),select.desc());
            }
        }
    }
}

运行结果:

13:43.153 [main-23] INFO  - 查询语句 -> select * from t_account where id='1' ;描述 -> 无描述
13:43.201 [main-23] INFO  - 查询语句 -> select * from t_account where name='deniro' ;描述 -> 依据用户名获取账号

这里我们利用 Java 反射机制来查找 Select 注解。getAnnotation() 反射方法会返回指定类型的注解对象,如果没找到,则会返回 null。然后通过调用注解对象所定义的方法来提取元素值。

5 注解元素类型

注解元素可以有以下类型:

  1. 所有基本类型(int、float、boolean等)
  2. String
  3. Class
  4. enum
  5. Annotation
  6. 以上类型的数组

如果使用了其他类型,那么编译器就会报错。因为有自动装箱机制,所以也可以使用基本类型的包装类型。

6 默认值

注解元素的默认值只能是以下两种情况:

  1. 有默认值;
  2. 就在使用注解时提供元素的值。

注意: 不能使用 null 作为默认值。但我们可以自定义一些特殊的值,比如空字符串或者负数,来表示某个元素不存在。形如:

String sql() default "";

7 嵌套注解

注解也可以作为元素的类型,即注解嵌套。

(1)定义

假设,我们定义两个注解,用于把查询出的数据转换为结果集。

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

/**
 * 结果集字段
 * <p/>
 *
 * @author Deniro Lee
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Result {
    /**
     * Java 字段
     *
     * @return
     */
    String property() default "";

    /**
     * 表字段
     *
     * @return
     */
    String column() default "";

    /**
     * 是否为主键
     *
     * @return
     */
    boolean id() default false;
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 结果集
 * <p/>
 *
 * @author Deniro Lee
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Results {
    Result[] value();
}

Results 注解定义了一个唯一元素,就是另一个注解 Result 的数组。

(2)使用

结果集注解定义好以后,这样使用:

    @Select(sql = "select * from t_account where id='1'")
    @Results({
            @Result(property = "id", column = "id", id = true),
            @Result(property = "roleName", column = "role_name")
    })
    public void findUserById();

@Results 注解中嵌套了多个 @Result 注解。

(3)处理

注解处理器,通过 getAnnotation 获取类中定义的注解:

Method[] methods = UserDao.class.getDeclaredMethods();
        for (Method method : methods) {
            Annotation annotation = method.getAnnotation(Results.class);
            if (annotation instanceof Results) {
                log.info("annotation -> %s", annotation);

                Results results=(Results)annotation;
                Result[] resultArray=results.value();
                for (Result result : resultArray) {
                    log.info("result -> "+result);
                }
            }
        }

运行结果:

59:13.959 [main-37] INFO  - result -> @net.deniro.jdk.annotations.study.Result(property=id, column=id, id=true)
59:13.960 [main-37] INFO  - result -> @net.deniro.jdk.annotations.study.Result(property=roleName, column=role_name, id=false)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值