注解定义(来自百度百科):指示编译器如何对待您的自定义 Annotation,预设上编译器会将Annotation资讯留在class档案中,但不被虚拟机器读取,而仅用于编译器或工具程式运行时提供资讯。

随着零配置的流行,注解的使用也越来越大众化,注解的学习也很有必要。最近学习了下Spring的几个注解,这里与大家分享下自己对注解的理解。
首先我们来看下@Controller这个注解的源码

1
2
3
4
5
6
7
8
9
10
package org.springframework.stereotype;
// 省略import以及一些注释
 
@Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Component
  public @interface Controller {
      String value() default "";
  }

不难看出,注解的关键字是@interface,很像一个接口,是不能够实例化的,然而我们在实际使用的时候,通常通过反射机制,得到注解接口的一个实例,进行逻辑处理,后面的样例会看到这种使用。(命名为value的注解方法有一个比较特别的用法,后面会提到。)

它的主体部分,定义了一个value()方法,实际上,它不仅是一个方法定义,也是注解的一个属性定义。我们使用注解进行标注的时候,是这样的:@Controller(value="MyController"),而在解析判断时,会通过controller.value()方法,得到这个具体的value值"MyController"。再看看value()后面跟着的default,这个default表面上的意思是默认值为某个值,实际上还有一个功效,表示value属性可以不输入。因此我们使用Controller的时候,可以直接@Controller这样使用,不需要给定value,若去掉default,不指定value,会编译失败。

再看看这个注解定义前面的注解。@Target,顾名思义,就是指定当前注解使用的作用目标。如果大家使用Eclipse等开发工具,将鼠标放到ElementType.TYPE处,会看到其注释内容,大致意思是说,这个注解要放到类、接口或者枚举类型声明的地方。也就是说,我们的@Controller注解只能放到类、接口、枚举定义前面,不能放到成员属性、方法、参数等地方。大家可以跟进ElementType,这里定义了“TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE”这么些枚举,根据注释(或者从枚举英文含义也不难猜测。这也从另一方面说明,合理的命名有助于提高代码可读性)很容易知道,注解可以实现注解到类文件的各个地方。

@Retention(RetentionPolicy.RUNTIME),Retention可能不好猜,但是看到后面的RUNTIME,精神一震,猜测应该是和“运行时”有关。再查看API帮助文档,Retention“指示注释类型的注释要保留多久”。这个保留多久,就要和RetentionPolicy(Retention的策略)配合使用了。Controller的Retention策略是运行时的,这样在代码运行时,可以通过反射获得这个注解。这中策略的好处,具体实现案例可以参考Spring的bean扫描以及AOP拦截的注解实现(Spring配置文件的component-scan base-package配置后,Spring bean工厂会逐个扫描包下所有类,根据其注解来生成相关bean。大家可以分析@Component、@Service、@Autowired等,其策略也是RUNTIME的)。

@Documented,如果需要通过javadoc工具文档化时,会判断这个注解,从而保留注释(具体没有实践过,不瞎诌了)。

@Component这个注解放在Controller上面,可以看做“Controller同样具有Component的作用”。实际上,目前的Spring扫描bean的时候,只认准了Component注解的。我们会看到,@Service、@Repository上面也有@Component注解。说道这里又不得不岔开下话题,来比照下@Service、@Controller、@Repository、@Component这几个注解的区别了。理论上讲,@Service是注解提供服务性质的bean上的,@Controller是注解MVC的C上的,@Repository是存储层bean使用的,而@Component是注解不区分服务还是控制的bean。实际上,这几类注解最终在Spring里都是以bean形式放到bean工厂里,没有什么区别对待。因此Spring扫描bean的时候,一律以@Component作为标记,@Service、@Controller、@Repository可以看做是一种功能预留:将来可能会对这三种注解的类做bean初始化时,做额外的增强型处理。

拆分了注解后,我们会发现注解也没那么神秘。接下来可以设计个属于自己的注解了:
自定义注解:

1
2
3
4
5
6
7
@Target(ElementType.TYPE)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface MyAnnotation {
  String name() default "";// 名字
  Class<!--?--> procClass() default Object.class;// 处理类的类型
  String value() default "";// 比较特别
  }

注解使用类:

1
2
3
// 标注时,name、procClass和value当做属性直接设值
@MyAnnotation(name="Lily",procClass=TestAnnotation.class,value="abc")
public class SomeClass{}

测试:

1
2
3
4
5
6
7
8
9
// 通过反射获得注解
SomeClass some = new SomeClass();
// 得到MyAnnotation的一个实例
MyAnnotation annotationClass = some.getClass().getAnnotation(MyAnnotation.class);
// 判断逻辑里,name和procClass当做方法用以调用
String annotationName = annotationClass.name();
System.out.println(annotationName);
Class<!--?--> clazz = annotationClass.procClass();
System.out.println(clazz);

上面定义的value()在使用时,如果不设置其它属性,只设置value,可以这样简写:@MyAnnotation("abc"),此时,value的值为"abc",其它取默认值。(不局限String类型。当然,MyAnnotation的其它方法需要提供default值。)

以上是一个简单的样例,大家可以修改Target,增减方法,实现自己需要的注解。

最后,欢迎大家拍砖。

Java注解浅谈的更多相关文章

  1. 【推荐】JAVA基础◆浅谈3DES加密解密

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  2. JAVA随笔----浅谈lombok注解

    在Java开发中,注解可谓是帮了大忙.注解的使用帮助我们简化了代码,让代码更加简洁.今天就来谈谈常用的Lombok注解. lombok注解文档 lombok官方下载地址 先看一下lombok支持的一些 ...

  3. Java注解一谈

    阅读目录 1.元注解 2.自定义注解 3.注解处理器 android注解框架解析 我们经常会在java代码里面看到:“@Override”,“@Target”等等样子的东西,这些是什么? 在java里 ...

  4. java多线程浅谈

    当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 分这几种情况:    1.其他方法前是否加了synchronized关键字,如果没加,则能.    2 ...

  5. Java:浅谈InputStream的close方法

    原则:最好在任何时候使用InputStream或者OutputStream的时候,在finally中调用close()方法,显式关闭. 一个典型的示例 InputStream in = null; t ...

  6. 【JAVA】浅谈java内部类

    一.什么是内部类? 到底什么是内部类呢?通俗的讲,就是在类内部定义的类,包括定义在一个类的方法外面.方法里面或者代码块中. 二.为什么要使用内部类? 为什么我们要不走寻常路,把一个类定义在另一个类的内 ...

  7. 【JAVA】浅谈java枚举类

    一.什么情况下使用枚举类? 有的时候一个类的对象是有限且固定的,这种情况下我们使用枚举类就比较方便? 二.为什么不用静态常量来替代枚举类呢? public static final int SEASO ...

  8. java - 异常浅谈

    java提供异常处理机制中,可以分为RuntimeException和checked Exception两种. RuntimeException 是运行时异常,是程序本身无法解决的.例如,对于一个用户 ...

  9. 通过Java代码浅谈HTTP协议

    最近刚看了http协议,想写点东西加深一下理解,如果哪儿写错了,请指正. 1 介绍 HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写.它的发展是万维网协会(W ...

随机推荐

  1. 杭电 1772 cake

    Cake Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  2. 【原】Django常用命令总结

    1.终端命令 # 查看django版本 $ python -m django --version # 创建项目,名为mysite $ django-admin startproject mysite ...

  3. 确定BP神经网络中的节点数

    输入层 输入层节点数=输入向量维数 MNIST例子中,单张MNIST图片大小为28*28,reshape为一维数组,长度为784,所以输入层节点数为784: network = Network([78 ...

  4. Ubuntu配置Python开发环境(PyCharm、Tensorflow)

    安装JDK: https://www.cnblogs.com/wanghuixi/p/9837229.html 安装Anaconda: 安装PyCharm: https://www.cnblogs.c ...

  5. linux Shell(待学)

    2. Shell 2.1 简介 shell脚本执行方式Shell 是一个用 C 语言编写的程序,通过 Shell 用户可以访问操作系统内核服务.它类似于 DOS 下的 command 和后来的 cmd ...

  6. iOS 开发之 FMDB 源码分析

    概念: FMDB 是用于数据存储的框架,它是 iOS 平台下对 SQLite 数据库的封装.FMDB 是面向对象的,它以 OC 的方式封装了 SQLite 的 C 语言 API,使用起来更加方便. C ...

  7. js 子窗口调用父框框方法

    父窗口 子窗口

  8. HDU 1035 Robot Motion(dfs + 模拟)

    嗯... 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1035 这道题比较简单,但自己一直被卡,原因就是在读入mp这张字符图的时候用了scanf被卡. ...

  9. 使用python实现离散时间傅里叶变换

    以下内容引用链接:https://blog.csdn.net/baidu_37352210/article/details/79596633 (注意:通过如下内容可知,将序列信号进行傅里叶变换后,得到 ...

  10. Mybatis之foreach用法----List、Array、Map三种类型遍历

    在mybatis的xml文件中构建动态sql语句时,经常会用到标签遍历查询条件.特此记录下不同情况下书写方式!-------仅供大家参考------ 1. foreach元素的属性 collectio ...