Java注解实现以及注解的作用

时间:2020-8-23 作者:admin


java注解的实现以及注解的作用

四大元注解

java注解是在jdk1.5版本出现的,注解(Annotation)可以理解为对代码的解释,可以做一些扩展或者补充。注解通常有这样几个作用范围、作用在class上、还有就是方法上method,还有就是字段上,还可以再注解上。注解与注释的不同就不用赘述了。可能我们在最开始学习或者入门做些小的开发时,很少去理解注解到底是怎么实现。但是只有当我们能真正理解注解,并能在实际中使用他。自己设计出非常精妙的注解,使我们的代码结构更加优雅,功能更加强大的时候,我想这是非常享受。尽管我也是初学者,但是我也很想享受代码在我指尖跳跃的感觉(哈哈哈哈哈哈哈哈哈)。以下都是我的浅见,只为每天坚持学习。Java注解实现以及注解的作用
在学习注解之前,我认为应该有一定的java反射的知识,还有jvm的知识。这样我们理解起来就会更加深刻。
首先我们看一下什么是元注解,元注解表示的是对注解的注解,也就是我们在自定义注解的时候,通常都要使用到这些元注解,只有掌握好元注解,才能设计好自定注解。

注解名 作用
@Target 通常用于表示该注解作用的范围,有一个枚举类型表示ElementType
@Retention 我理解该注解的生命周期,通过一个RetentionPolicy的枚举类型来表示注解存在的时间
@Documented 描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
@Inherited 使被它修饰的注解具有继承性

以上就是四大元注解,其中上述提到的枚举类型,可以展示一下他们到底枚举的什么内容。

ElementType

类型 解释
TYPE 通常就用来表示该注解作用在类上
FIELD 作用在字段上
METHOD 作用在方法上
PARAMETER 形式参数声明
CONSTRUCTOR 构造函数声明
LOCAL_VARIABLE 局部变量声明
ANNOTATION_TYPE 注解上
PACKAGE 包上
TYPE_PARAMETER
TYPE_USE

常用的就Type、FIELD、METHOD

RetentionPolicy

SOURCE 编译层面
CLASS 注释将由编译器记录在类文件中但不需要在运行时被VM保留。这是默认值行为。
RUNTIME 可以通过反射获取

自定义注解

自定义一个注解非常简单,1、@interface +注解名定义一个注解。2、在自定义的注解上使用元注解。3、注解内的元素类型,只能是public,并且只能是基本的数据类型(int,float…),以及String Class enum,Annotation,和以上数据类型的数组。4、可以指定默认值。但是在非基本类型数据在指定默认值时,都不能使用null作为默认值。例如,创建一个用于配置信息的注解。

public class AppConfig {
    @Config("123")
    private String appId;
    @Config("456")
    private String appSecret;
    private String Key;
    public AppConfig()
    {
        System.out.println("appconfig...");
    }
    public String getKey() {
        return Key;
    }
    public void setKey(String key) {
        Key = key;
    }
    public String getAppId() {
        return appId;
    }
    public void setAppId(String appId) {
        this.appId = appId;
    }
    public String getAppSecret() {
        return appSecret;
    }
    public void setAppSecret(String appSecret) {
        this.appSecret = appSecret;
    }

    @Param(clazz = Person.class,id = 1)
    public void doPost(){

    }
    @Override
    public String toString() {
        return "AppConfig{" +
                "appId='" + appId + '\'' +
                ", appSecret='" + appSecret + '\'' +
                ", Key='" + Key + '\'' +
                '}';
    }
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {
    Class<?> clazz();
    int id();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeTest {
    boolean flag() default true;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Config {
    public String value() default "";
}
public class AppConfigService {
    public static void main(String[] args) {
        AppConfig appConfig = initAppConfig();
        System.out.println("通过初始化的值:"+appConfig.toString());

    }
    public static AppConfig initAppConfig(){
        try{
            Class clazz = Class.forName("cn.hbmy.AppConfig");
            AppConfig appConfig = (AppConfig) clazz.newInstance();
            /**
            * 对于private 属性通过getDeclaredFields方法可以获取到
            * */
            Field[] fields = clazz.getDeclaredFields();
            for(Field field:fields){
                System.out.println("获取class中的字段"+field);
            }
            /**
             *获取类上的注解
             * */
            TypeTest typeTest =(TypeTest) clazz.getAnnotation(TypeTest.class);
            System.out.println("获取类上注解值:"+typeTest.flag());
            /**
            * 获取方法
            * */
            Method[] methods = clazz.getDeclaredMethods();
            for(Method method:methods)
            {
                if(method.isAnnotationPresent(Param.class)){
                    //判断这个方法是否包含Param注解
                    method.setAccessible(true);
                    Param param = (Param) method.getAnnotation(Param.class);//得到方法上的注解
                    Method[] methods1 = param.annotationType().getDeclaredMethods();
                    for(Method me:methods1){
                        System.out.println("注解值:"+me.invoke(param, null));
                    }
                    System.out.println("获取class中的方法"+method.getName()+" "+method.getParameters());
                }
            }
            /**
            * 获取构造函数,获取私有的通过getDeclaredConstructors方法
            * */
            Constructor<AppConfig> [] constructors = clazz.getConstructors();
            for(Constructor constructor:constructors)
            {
                System.out.println("构造函数"+constructor);
            }
            /**
             * 获取注解
             * */
            for(Field field:fields){
                if(!field.isAccessible()){
                    field.setAccessible(true);
                }
                Config config = field.getAnnotation(Config.class);
                if(config!=null)
                {
                    field.set(appConfig,config.value());//设置注解值
                    System.out.println("config.value:"+field.getName()+" "+config.value());
                }
            }
            return appConfig;
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

通过上述的例子可以总结自定注解主要包括两步,1、定义一个注解。2、编写注解解析器。如果注解没有解析恐怕注解将毫无作用。
上述例子可能不能很好地体现出注解的作用,现在假设你希望提供一些对象映射的功能,能够自动生成数据库表。用以存储javaBean对象。如果使用xml的话,需要指明类名,以及字段的相关信息。然而如果使用注解的话,我们只需将相关信息保存在javaBean中。为此我们需要定义一些新注解,用以定义与bean关联的表名,以及与bean属性相关连的列的名字和数据类型。

定义表名注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TableDB {
    String name();
}

定义两种数据类型

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    int value() default 0;
    String name() default "";
    Constraints constraints() default @Constraints;//默认初始
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SQLInteger {
    String name() default "";
    Constraints constraints() default @Constraints;
}

定义约束

public @interface Constraints {
    boolean primary_key()default false;
    boolean unique() default false;
    boolean allow_null() default true;
}

定义Bean

@TableDB(name = "Member_T")
public class Member {
    @SQLInteger(constraints =  @Constraints(primary_key = true))
    public int id;//主键

    @SQLString(20)
    public String userName; //姓名

    @SQLString(20)
    public String passWord; //密码

    @SQLInteger
    public int  age;        //年龄
}

注解解析器

public class AnnotationParse {

    public static String parseDBAnnotation(){
        Class clazz = Member.class;
        TableDB tableDb =(TableDB)clazz.getAnnotation(TableDB.class);
        StringBuffer res = new StringBuffer();

        String name = tableDb.name();
        res.append("CREATE TABLE ").append(name).append("(").append("\n");
        //
        Field[] fields = clazz.getDeclaredFields();
        for(Field field:fields){
            field.setAccessible(true);
            if(field.isAnnotationPresent(SQLInteger.class)){
                SQLInteger sqlInteger = field.getAnnotation(SQLInteger.class);
                res.append(field.getName()).append(" ").append("INT ").append(getConstraints(sqlInteger.constraints())).append(",");
                res.append("\n");
            }else if(field.isAnnotationPresent(SQLString.class)){
                SQLString sqlString = field.getAnnotation(SQLString.class);
                res.append(field.getName()).append(" ").append("VARCHAR(").append(sqlString.value()).append(")");
                res.append(getConstraints(sqlString.constraints())).append(",");
                res.append("\n");
            }
        }
        res.append(");");
        System.out.println(res.toString());
        return res.toString();
    }

    public static String getConstraints(Constraints constraints){
        StringBuffer s = new StringBuffer();
        if(!constraints.allow_null()){
            s.append("NOT NULL");
        }
        if(constraints.primary_key()){
           s.append("PRIMARY KEY");
        }
        if(constraints.unique()){
            s.append("UNIQUE");
        }
        return s.toString();
    }
    public static void main(String[] args) throws ClassNotFoundException {
        parseDBAnnotation();
    }
}

运行效果图,处理一下最后一个,就可以了。
Java注解实现以及注解的作用

java反射

Java的注解需要依赖于Java的反射机制,Java反射是在运行时可以通过类名.class或者Class.forName等方法获取Class。该Class时类加载过程中加载这一阶段会实现的。

java注解在java框架中的使用

注解在框架中使用有@Autowire、@RequestMapping、@Mapper、@Controller、@Service…等等,还有SpringBoot中的自动装配原理也是通过组合注解实现的,因此注解使得我们需要进行繁琐的操作变得更加简单。用好注解,写好注解,理解注解的底层实现可以让我们更能理解这一特性。不在每天依靠记忆去记住。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。