Annotation自定义注解

Posted

tags:

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

前言:

Sometimes there is no next time, no time-outs, no second chances; sometimes it‘s now or never.

对于注解来说,其实就是用来打标记用的,在别的地方可以根据特殊的标记得到标记的东西(属性、类、方法等),对于这大家应该非常熟悉了,很多第三方框架使用也巨多,比如Dagger、ButterKnife等等,这些第三方注解框架,省略了findviewById(),不禁要问,难道真的不需要这行findviewById()代码就能在xml中得到控件实例吗?答案很明确,肯定不是如此。本篇文章就会给出答案,主要介绍自定义注解相关的知识,模仿实现findviewById功能。

1、自定义注解回顾

在开始实现 findviewById 功能之前,还是复习一下怎么自定义注解,以及注解有啥作用吧。

有这样一种场景:

技术分享图片

主要工作是,要写一些注解类来标识SxtStudent类,然后通过注解把类中的信息跟表对应起来。比如上面SxtStudent类中,id为int类型,对应SQL中int(10);name为String类型,对应SQL中的varchar(10).。方式就是通过注解对类中的信息进行标注,写一个中间程序获取这个类中的注解信息,把注解解析出来,然后恰好可以作为SQL语句中拼接。

首先定义JavaBean类:

public?class?YdlStudent?{??
?private?int?id;
?private?String studentName;
?private?int?age;

?public?int?getId()?{
? ?return?id;
?}

?public?void?setId(int?id)?{
? ?this.id = id;
?}

?public?String?getStudentName()?{
? ?return?studentName;
?}

?public?void?setStudentName(String studentName)?{
? ?this.studentName = studentName;
?}

?public?int?getAge()?{
? ?return?age;
?}

?public?void?setAge(int?age)?{
? ?this.age = age;
?}
}

然后定义类与表对应转换的注解:

/**
* 针对表的注解
* 代表类与表之间对应转换
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
[email protected]?YdlTable {
?String?value();
}

因为表只需要一个表名即可,所以注解中只需要一个信息,这里使用String value();标识。

这个时候就可以对类进行注解了,这里对类的注解可以通过反射技术获取注解信息,然后作为创建的表名称。

技术分享图片

然后还可以对属性添加注解,定义与属性关联的注解类:

/**
* 针对属性的注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
[email protected]?YdlFiled {
?String?columName();//列名
?String?type();//数据类型
?int?length();//年龄
}

这里注解里面的内容是根据Student类和SQL来写的,SQL中的列名称定义为String columName();SQL中的数据类型为String type();SQL中的数据类型占的长度定义为int length();

然后在YdlStudent类中对属性添加注解:

技术分享图片

这里

@YdlFiled(columName = "id",type = "int",length = 10)

private int id;

理解为:YdlStudent中属性id对应表中的字段id这一列,sql中的类型为int,int长度为10。

@YdlFiled(columName = "sname",type = "vachar",length = 10)

private String studentName;

理解为:YdlStudent中属性studentName对应表中的字段sname这一列,sql中的类型为vachar,vachar长度为10。

这样标识以后,就可以写一个解析程序来解析YdlStudent类,获取注解相关信息来创建表了:

public?class?Main?{
?public?static?void?main(String[] args)?{
? ?Class clazz = YdlStudent.class;
? ?Annotation[] annotations = clazz.getAnnotations();
? ?for?(Annotation annotation : annotations) {
? ? ?// 获取所有的【类】注解
? ? ?System.out.println(annotation);
? ?}

? ?// 或得类的指定注解
? ?YdlTable table = (YdlTable) clazz.getAnnotation(YdlTable.class);
? ?// table.value()直接获取注解内容名称[email protected](value = "tb_student")//可以通过反射读取这里,然后创建对应的表
? ?System.out.println(table.value());

? ?try?{
? ? ?// 根据名称获取属性对象
? ? ?Field field = clazz.getDeclaredField("studentName");
? ? ?// 获取该属性上面的注解
? ? ?YdlFiled ydlFiled = field.getAnnotation(YdlFiled.class);
? ? ?// 获取注解属性的内容。
? ? ?System.out.println(ydlFiled.columName() +?"---"?+ ydlFiled.type()
? ? ? ? ?+?"---"?+ ydlFiled.length());

? ? ?// 同理可以获取 YdlStudent 类中 id、age所对应的注解信息

? ?}?catch?(Exception e) {
? ? ?e.printStackTrace();
? ?}

? ?// 根据上方获取到的表名、属性(字段)信息等拼接Sql语句。
?}
}

这里分段来讲:

? ?Class clazz = YdlStudent.class;
? ?Annotation[] annotations = clazz.getAnnotations();
? ?for?(Annotation annotation : annotations) {
? ? ?// 获取所有的【类】注解
? ? ?System.out.println(annotation);
? ?}

这里表示通过反射获取所有的类注解,因类注解只有一个:

技术分享图片

所以打印结果为:@itydl03.YdlTable(value=tb_student)

? ?// 或得类的指定注解
? ?YdlTable table = (YdlTable) clazz.getAnnotation(YdlTable.class);
? ?// table.value()直接获取注解内容名称[email protected](value = "tb_student")//可以通过反射读取这里,然后创建对应的表
? ?System.out.println(table.value());

表示通过反射获取类的指定注解,clazz.getAnnotation(YdlTable.class);表示指定获取YdlTable类的注解。然后table.value()直接获取注解内容名称[email protected](value = "tb_student")。获取的内容就是tb_student,打印结果:

tb_student

try?{
? ? ?// 根据名称获取属性对象
? ? ?Field field = clazz.getDeclaredField("studentName");
? ? ?// 获取该属性上面的注解
? ? ?YdlFiled ydlFiled = field.getAnnotation(YdlFiled.class);
? ? ?// 获取注解属性的内容。
? ? ?System.out.println(ydlFiled.columName() +?"---"?+ ydlFiled.type()
? ? ? ? ?+?"---"?+ ydlFiled.length());

? ? ?// 同理可以获取 YdlStudent 类中 id、age所对应的注解信息

? ?}?catch?(Exception?e) {
? ? ?e.printStackTrace();
? ?}

clazz.getDeclaredField("studentName");表示根据名称获取属性对象,返回值得到一个Field field?表示属性?。 在YdlStudent类中 ?private String studentName; 属性对应的注解为

@YdlFiled(columName = "sname",type = "vachar",length = 10)

field.getAnnotation(YdlFiled.class);就表示获上边属性YdlFiled注解类中的@YdlFiled(columName = "sname",type = "vachar",length = 10)的所有注解信息;返回值为YdlFiled类型。他可以理解为columName = "sname",type = "vachar",length = 10的注解信息的javaBean。最后打印:

sname---vachar---10

最后再把所有log给出:

技术分享图片
2、仿造findviewById()

有了上边的基础回顾,下面就不写那么细致了。

首先定义一个注解类,ViewById:

@Target(value={ElementType.FIELD})//FIELD表示的是成员变量级别可以使用该注解
@Retention(RetentionPolicy.RUNTIME)//RUNTIME级别可以被反射读取注解,都是运行时
[email protected]?ViewById {
? ?int?value();
}

然后在MainActivity中使用该注解:

public?class?MainActivity?extends?AppCompatActivity?{

? [email protected](R.id.tv)
? ?private?TextView mTextView;

? [email protected]
? ?protected?void?onCreate(Bundle savedInstanceState)?{
? ? ? ?super.onCreate(savedInstanceState);
? ? ? ?setContentView(R.layout.activity_main);
? ? ? ?mTextView.setText("自定义注解");
? ?}
}

看着好像挺熟悉的,跟使用第三方框架差不多,但是运行程序肯定报错。这是因为MainActivity类跟自定义注解压根就没有什么连接关系。那么接下来就建立连接关系。而这个中介就是反射。

自定义ViewUtils类:

public?class?ViewUtils?{
? ?public?static?void?inject(Activity activity)?{
? ? ? ?// 1.获取所有的属性
? ? ? ?Field[] fields = activity.getClass().getDeclaredFields();
? ? ? ?// 2.过滤关于 ViewById 属性
? ? ? ?for?(Field field : fields) {
? ? ? ? ? ?//获取注解封装---ViewById类型的注解。相当于对ViewById所标识属性的封装类
? ? ? ? ? ?ViewById viewById = ?field.getAnnotation(ViewById.class);
? ? ? ? ? ?if(viewById !=?null){
? ? ? ? ? ? ? ?// 3.findViewById
? ? ? ? ? ? ? ?View view = activity.findViewById(viewById.value());
? ? ? ? ? ? ? ?field.setAccessible(true);
? ? ? ? ? ? ? ?try?{
? ? ? ? ? ? ? ? ? ?// 4.反射注入
? ? ? ? ? ? ? ? ? ?// activity 属性所在类,view 代表的是属性的值
? ? ? ? ? ? ? ? ? ?field.set(activity,view);
? ? ? ? ? ? ? ?}?catch?(IllegalAccessException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? ?}
? ? ? ? ? ?}
? ? ? ?}
? ?}
}

代码注释非常详细了,主要功能就是反射解析注解,然后在这里进行了findviewById操作获取View,最后再注入到属性对象里面field.set(activity,view);

此时需要在MainActivity中加入如下代码:

ViewUtils.inject(this);

表示注入当前的MainActivity,只有注入了才能使用自定义的注解“框架”。

为了更直观,把MainActivity中代码稍微修改如下:

public?class?MainActivity?extends?AppCompatActivity?{

? ?//注解属性,解析类就是解析这里带注解的属性
? [email protected](R.id.tv)
? ?private?TextView mTextView;

? [email protected](R.id.tv2)
? ?private?TextView mTextView2;

? ?//非注解属性
? ?private?int?age;
? ?private?String name;

? [email protected]
? ?protected?void?onCreate(Bundle savedInstanceState)?{
? ? ? ?super.onCreate(savedInstanceState);
? ? ? ?setContentView(R.layout.activity_main);

? ? ? ?ViewUtils.inject(this);

? ? ? ?mTextView.setText("自定义注解");

? ? ? ?mTextView2.setText("我是SuperMan");
? ?}
}

其中private int age;和private String name;两个属性虽然在注解解析工具类会获取到,但是我们已经对其做了过滤处理。此时运行程序:

技术分享图片

到此为止,注解相关的知识就讲解完了。

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

[2]注解(Annotation)-- 深入理解Java:注解(Annotation)自定义注解入门

Java注解(Annotation)自定义注解入门

深入理解Java:注解(Annotation)自定义注解入门

深入理解Java:注解(Annotation)自定义注解入门

深入理解Java:注解(Annotation)自定义注解入门

Java注解--一张图一案例掌握自定义注解