从理论到应用,你需要掌握的Java注解知识都在这里

1.引言

前段时间因为业务的需求,需要对律所的数据进行拆分与合并,因为整个系统涉及到大量的业务模块,为了尽量减少各具体模块的迁移开发工作量,需要对迁移过程中的共性内容进行抽象,从而设计出一套更通用的迁移框架。

迁移框架在DB层面进行复制,为了更方便的描述表结构及表之间的关联,使用了注解进行定义,故总结一下注解相关的知识。

注解是JDK1.5引入的新特性,是在代码里面的一种特殊标记,针对这些标记,我们可以在源码、编译或运行时对代码做特殊处理。目前很多主流框架都有在使用注解,如:

JDK里面的注解@Override、@Resource等;

Spring里面使用的注解@Autowired、@Configurable等;

JUnit测试框架里面的@Test、@Before、@BeforeClass等。

可以说了解注解是我们设计出优雅代码,通读各类框架源码的必备技能,尤其是在Spring Boot大行其道的今天。

2.自定义注解

2.1 定义及使用

以下代码定义了一个注解Table,有一个属性value用来表示表的名称。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

public @interface Table{

/**

* 表的名称

*/

String value() default "";

}

以下代码使用了刚才定义的注解Table,指定了value的属性值app_matter,因为只有一个属性,所以没有写全称value="app_matter"

@Table("app_matter")

public class MatterDefinition {

@Id(value ="pk_id", mode = IdGeneratorMode.UUID)

String id;

}

定义注解的过程中,还存在很多其他信息,下面的章节对定义注解中涉及的各个信息进行说明。

2.2 元注解

上面在定义注解的过程中,使用到了很多其他注解,这种在描述注解的注解,称之为元注解。

@Target

定义当前注解可以使用在什么地方,上面的Table注解Target使用了TYPE,所以可以应用到类、接口、注解及枚举中,其他Target的类型还有:

其中TYPE_PARAMETER、TYPE_USE是1.8的新增特性,因为并没有提供反射接口获取处理,所以该注解一般要开发者自己实现或第三方开发工具实现。

ElementType.TYPE_PARAMETER:

表示该注解可以应用到类型参数的申明中,如:

public <@MyTypeParameterT> void show(Tmessage){}

ElementType.TYPE_USE:

表示该注解可以使用到类型出现的地方,如:

MatterDefinition instance = new @MyTypeUse MatterDefinition();。

@Retention

定义当前注解的保留策略,如:

RetentionPolicy.SOURCE:

表示该注解仅存在源码中,编译成class文件后,注解信息将丢失,如:@Override注解。

RetentionPolicy.CLASS:

表示该注解存在编译后的class文件中,是注解保留策略的默认方式。

RetentionPolicy.RUNTIME:

表示该注解存在class文件中,并且被jvm加载后还存在,可以使用反射相关接口进行获取。

@Documented

定义该注解是否支持导出到javadoc文档中。

@Inherited

定义该注解是否可以被子类继承,如:ClassA使用了Inherited注解,ClassB继承了ClassA,则ClassB可以获取到该注解。

2.3 注解元素

上例中的注解Table,定义了元素value

String value() default "";

3.重复注解

Repeatable:重复注解,JDK1.8引入的新特性,可以在类或方法等上指定多个相同的注解属性,这种方式只是一种语法糖,下面的Demo类中最终生成的注解其实还是Items。

@Target(ElementType.TYPE)

public @interface Items{

Item[] value();

}

@Repeatable(Items.class)

@Target(ElementType.TYPE)

@interface Item{

String value() default "";

}

@Item("item1")

@Item("item2")

class Demo {

    public static void main(String[] args) {

    }

}

4.注解反射

和注解相关的接口有Annotation和AnnotatedElement,其中:

所有的注解对象annotation都实现了Annotation接口,该对象的annotationType方法返回注解的类型(如:Table.class),该对象自身是一个动态代理对象(如:$Proxy2)。

5.注解的应用

5.1 注解属性的获取

@Table("app_matter")

class MatterDefinition {

@Id(value ="pk_id", mode = IdGeneratorMode.UUID)

String id;

}

public class AnnotationDemo {

public static void main(String[] args)throws NoSuchFieldException {

System.out.println(MatterDefinition.class.isAnnotationPresent(Table.class));//true

Table tableAnnotation = MatterDefinition.class.getAnnotation(Table.class);

System.out.println(tableAnnotation.value());//app_task

Field idField = MatterDefinition.class.getDeclaredField("id");

Id idAnnotation = idField.getAnnotation(Id.class);

System.out.println(idAnnotation.mode());//IdGeneratorMode.UUID

}

}

5.2 获取指定包下含有指定注解的类

方式一:原生反射

通过JDK原生的方式进行反射处理,即获取到指定包下的所有类,然后遍历每个类判断当前类是否出现指定注解,这种方式相当于从零开始撰写很多基础代码。

方式二:借助Spring中注解Component的Bean管理

在定义注解(如:Table)的时候加上元注解@Component,以把使用当前注解的类注册为Bean对象

从容器ApplicationContext中调用:Map getBeansWithAnnotation(Class annotationType)

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Component//此处加上Component注解

@interface MyTableAnnotation{

String value() default "";

}

@MyTableAnnotation("app_task")

class TaskDefinition {

@Id(value ="pk_id", mode = IdGeneratorMode.UUID)

String id;

}

@Component

public class ComponentAnnotationDemo implements ApplicationContextAware {

ApplicationContext applicationContext;

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.applicationContext= applicationContext;

}

public void show() {

//获取容器中指定注解类型的所有Bean对象

Map stringObjectMap =applicationContext.getBeansWithAnnotation(MyTableAnnotation.class);

System.out.println("stringObjectMap:"+ stringObjectMap);

}

}

方式三:借助Spring的包扫描注解ClassPathScanningCandidateComponentProvider

通过ClassPathScanningCandidateComponentProvider扫描指定的包

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@interface MyTableAnnotation{

String value() default "";

}

@MyTableAnnotation("app_task")

class TaskDefinition {

@Id(value ="pk_id", mode = IdGeneratorMode.UUID)

String id;

}

@Component

public class ComponentAnnotationDemo {

public void show() {

//useDefaultFilters:是否扫描默认的Component,Controller,Service,Repository注解

ClassPathScanningCandidateComponentProvider classPathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);

//以注解的方式作为扫描条件,也可以指定正则(如:RegexPatternTypeFilter)等其他扫描方式

classPathScanningCandidateComponentProvider.addIncludeFilter(newAnnotationTypeFilter(MyTableAnnotation.class));

//basePackage:指定扫描的基础包,空字符串为所有包

Set beanDefinitionSet = classPathScanningCandidateComponentProvider.findCandidateComponents("com.icourt");

System.out.println(beanDefinitionSet);

}

}

6.几个注解的简单说明

此处简单列举几个注解的说明,权当混个脸熟,后期再根据实际情况,结合各框架,进行专题深入讲解。

以下三个注解是JDK1.5开始支持注解特性时引入的,也算是鼻祖了。

Override:表示重写了父类的指定方法;如果方法上有该注解,但方法名与超类不同,开发工具将给出错误警告。

Deprecated:表示指定的类或方法已经被废弃,不建议继续使用,而应该使用新的替代方案。

SuppressWarnings:表示忽略指定类型的警告提示。

以下两个注解是JDK1.8引入的新特性。

FunctionalInterface:函数式接口的注解,主要用在Lambda表达式中,表示该接口只能有一个抽象方法,从而可以使用Lambda表达式进行简写。

Repeatable:重复注解,可以在类或方法等上指定多个相同的注解属性,上面章节有说明。

以下三个是通用注解(Common Annotation),原来是Java EE5.0规范的一部分,后面加入到了Java SE6.0中,避免框架或各类开发者重复定义。Java SE里面仅包含了注解类的定义,但没有包含注解类的解析实现,而是由Java EE容器进行实现,如:应用到Servlet规范的生命周期注解;Spring对这几个注解也做了解析支持。

Resource:表示一种资源,如果应用到属性或方法上,则这个类在实例化的时候,将自动注入该类型或命名的对象。

PostConstruct:表示在对象创建之后做的额外处理。

PreDestroy:表示在对象销毁之前做的额外处理。

以下注解是Spring2.5版本引入的,针对MVC和通用组件的一套注解。

Controller:表示这是一个Controller层对象,一般封装参数校验,权限判断,及不可复用的简单逻辑。

Service:表示这是一个业务层Bean对象,一般封装具体的业务逻辑。

Repository:Spring 2.0引入的注解,对数据访问层的对象进行Bean标示。

Component:表示这是一个Bean对象。

Autowired:默认根据类型自动装配指定的Bean对象,如果要按名称进行装配,可以结合Qualifier注解。

以下两个注解是Spring3.0为了减少XML配置而引入的基于Java的注解。

Configuration:表示这是一个配置类,里面会包含一个或多个被Bean标注的方法,用于Spring创建出Bean的实例。

Bean:标注的方法可以构造出Bean的实例,方法名默认为容器中Bean的名称。

以下为Spring4.0引入的条件注解。

Conditional:根据某个条件决定是否创建某个bean,这也是Spring Boot框架实现的基础。

7.要点总结

对前面介绍的内容做一张脑图进行总结,方便大家一览文章的关键要点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容