Java中的自定义注解

Posted 孙琛斌(浮生)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的自定义注解相关的知识,希望对你有一定的参考价值。

说起注解来,大家第一想到的肯定是@Controller、@Service、@Autowired、@Resources、@ResponseBody、@Transactional等,当然还有很多,例举的这些都是大家非常熟悉的。

使用过的人都知道,通过注解减少了我们很多冗余的代码量,用起来也很舒服,本文揭开注解的神秘面纱,自己动手写一个自定的注解。

前几天我写了一套Mybatis根据在实体类上配置注解的方式,自动去创建表更新表结构,那我就用这里面的其中一个注解来讲解自定义注解吧。

首先我们创建一个自定义的注解,代码如下:

package com.sunchenbin.store.annotation;

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;

/**
 * 建表的必备注解
 *
 * @author sunchenbin
 * @version 2016年6月23日 下午6:12:48
 */
// 该注解用于方法声明
@Target(ElementType.FIELD)
// VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息
@Retention(RetentionPolicy.RUNTIME)
// 将此注解包含在javadoc中
@Documented
// 允许子类继承父类中的注解
@Inherited
public @interface Column{

    /**
     * 字段名
     * 
     * @return
     */
    public String name();

    /**
     * 字段类型
     * 
     * @return
     */
    public String type();

    /**
     * 字段长度,默认是255
     * 
     * @return
     */
    public int length() default 255;

    /**
     * 小数点长度,默认是0
     * 
     * @return
     */
    public int decimalLength() default 0;

    /**
     * 是否为可以为null,true是可以,false是不可以,默认为true
     * 
     * @return
     */
    public boolean isNull() default true;

    /**
     * 是否是主键,默认false
     * 
     * @return
     */
    public boolean isKey() default false;

    /**
     * 是否自动递增,默认false 只有主键才能使用
     * 
     * @return
     */
    public boolean isAutoIncrement() default false;

    /**
     * 默认值,默认为null
     * 
     * @return
     */
    public String defaultValue() default "NULL";
}

那么@Column就是我们自定义的注解了,那么怎么用呢,当然要解释下下面这几个注解:

[email protected]

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

    1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

[email protected]

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)

[email protected]

用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。
Documented是一个标记注解,没有成员。

[email protected]

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。
如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

注意:@Inherited annotation类型是被标注过的class的子类所继承。
类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。
如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,
直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

OK,大概就上面描述的这样,然后看下@Column在实际应用中的使用:

public class Test extends BaseModel{

    private static final long serialVersionUID = 5199200306752426433L;

    @Column(name = "id",type = mysqlTypeConstant.INT,length = 11,isNull=false,isKey = true,isAutoIncrement=true)
    private int     id;

    @Column(name = "name",type = MySqlTypeConstant.VARCHAR,length = 111)
    private String  name;

    @Column(name = "description",type = MySqlTypeConstant.TEXT,length = 100)
    private String  description;

    @Column(name = "create_time",type = MySqlTypeConstant.DATETIME,length = 0)
    private Date    create_time;

    @Column(name = "update_time",type = MySqlTypeConstant.DATETIME,length = 0)
    private Date    update_time;

    @Column(name = "number",type = MySqlTypeConstant.DECIMAL,length = 5,decimalLength = 1,isNull = false)
    private Long    number;

    @Column(name = "lifecycle",type = MySqlTypeConstant.CHAR,length = 1)
    private String  lifecycle;

    @Column(name = "dekes",type = MySqlTypeConstant.DOUBLE,length = 5,decimalLength = 2)
    private Double  dekes;
}

可以看到,使用@Column是需要带参数的,参考上面自定义注解的代码,比如name,没有使用默认值,那么他就是必须设置值的,如果有设置default那么就是可以不写的。

OK,配置会配了,那么如何通过代码获取到配置呢?

看下面代码:

private void tableFieldsConstruct(Map<String, Object> mySqlTypeAndLengthMap,Class<?> clas,List<Object> newFieldList){
        Field[] fields = clas.getDeclaredFields();

        for (Field field : fields){
            // 判断方法中是否有指定注解类型的注解
            boolean hasAnnotation = field.isAnnotationPresent(Column.class);
            if (hasAnnotation) {
                // 根据注解类型返回方法的指定类型注解
                Column column = field.getAnnotation(Column.class);
                CreateTableParam param = new CreateTableParam();
                param.setFieldName(column.name());
                param.setFieldType(column.type().toLowerCase());
                param.setFieldLength(column.length());
                param.setFieldDecimalLength(column.decimalLength());
                param.setFieldIsNull(column.isNull());
                param.setFieldIsKey(column.isKey());
                param.setFieldIsAutoIncrement(column.isAutoIncrement());
                param.setFieldDefaultValue(column.defaultValue());
                int length = (Integer) mySqlTypeAndLengthMap.get(column.type());
                param.setFileTypeLength(length);
                newFieldList.add(param);
            }
        }
    }

上面代码参杂了一些业务逻辑,无需关系,只需要领会其精神就可以了,主要就是拿到使用注解的对象的class,比如我们Test.class,由于我们注解是写在实体类的属性上,所以我们就要获取Test.class的所有Fields,然后根据注解的类型去get一下field就得到该注解类型了,然后就可以取出你注解上设置的值了。

好了,到这里自定义注解的创建使用,示例就结束了,有问题的可以留言~~~

以上是关于Java中的自定义注解的主要内容,如果未能解决你的问题,请参考以下文章

java注释的自定义

java里的自定义注解类型 有啥用?

java 自定义事件的触发及监听

SpringAOP的自定义注解实践

片段中 ListView 的自定义适配器不起作用

Java中的APT的工作过程