目录
1 什么是注解?
从JDK5开始,Java增加对元数据的支持,也就是注解,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
如下代码:
@Service
public class HelloServiceImpl implements HelloService {
@Autowired
private UserMapper userMapper;
@Autowired
CacheManager cacheManager;
@Override
public String sayHello() {
String sRes = "Hello world from Service";
return sRes;
}
}
@Service, @Autowired, @Override都是注解。
2 注解的作用
- 提供信息给编译器: 编译器可以利用注解来检测出错误或者警告信息,打印出日志。(格式检查)
- 编译阶段时的处理: 软件工具可以用来利用注解信息来自动生成代码、文档或者做其它相应的自动处理。(减少配置,减少重复工作)
- 运行时处理: 某些注解可以在程序运行的时候接受代码的提取,自动做相应的操作。(减少配置,减少重复工作)
3 注解的分类
3.1 内置注解
@Override, 表示当前的方法定义将覆盖超类中的方法。
@Deprecated,使用了注解为它的元素编译器将发出警告,因为注解@Deprecated是不赞成使用的代码,被弃用的代码。
@SuppressWarnings,关闭不当编译器警告信息。
…………
3.2 元注解
『元注解』是用于修饰注解的注解,通常用在注解的定义上,例如:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
这是我们 @Override 注解的定义,你可以看到其中的 @Target,@Retention 两个注解就是我们所谓的『元注解』,『元注解』一般用于指定某个注解生命周期以及作用目标等信息。
JAVA 中有以下几个『元注解』:
@Target | 表示该注解可以用于什么地方,可能的ElementType参数有: CONSTRUCTOR:构造器的声明 FIELD:域声明(包括enum实例) LOCAL_VARIABLE:局部变量声明 METHOD:方法声明 PACKAGE:包声明 PARAMETER:参数声明 TYPE:类、接口(包括注解类型)或enum声明 |
@Retention | 表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括: SOURCE:注解将被编译器丢弃 CLASS:注解在class文件中可用,但会被VM丢弃 RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。 |
@Document | 将注解包含在Javadoc中 |
@Inherited | 允许子类继承父类中的注解 |
3.3 自定义注解
通过元注解,用户自己编写注解。例如下面代码就是用户定义的自定义注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
public String id();
public String description() default "no description";
}
使用自定义注解
public class test {
@MyTest
public void testAnnotation(){
// do something
}
}
4 注解的工作原理
4.1 注解的本质
「java.lang.annotation.Annotation」接口中有这么一句话,用来描述『注解』。
The common interface extended by all annotation types
所有的注解类型都继承自这个普通的接口(Annotation)
这句话有点抽象,但却说出了注解的本质。我们看一个 JDK 内置注解的定义:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
这是注解 @Override 的定义,其实它本质上就是:
public interface Override extends Annotation{
}
一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。
4.2 注解的工作原理
注解仅仅是元数据,和业务逻辑无关,所以当你查看注解类时,发现里面没有任何逻辑处理,
如果注解不包含业务逻辑处理,必然有人来实现这些逻辑。注解的逻辑实现是元数据的用户来处理的,注解仅仅提供它定义的属性(类/方法/变量/参数/包)的信息,注解的用户来读取这些信息并实现必要的逻辑。
当使用java中的注解时(比如@Override、@Deprecated、@SuppressWarnings)JVM就是用户,它在字节码层面工作。如果是自定义的注解,比如JUnit的@Test注解,他的用户就是JUnit框架本身,JUnit负责解析使用了@Test注解的类,并进行响应操作。
5 注解与配置文件的区别
其实注解干的很多事情,通过配置文件也可以干,比如为类设置配置属性;但注解和配置文件是有很多区别的,在实际编程过程中,注解和配置文件配合使用在工作效率、低耦合、可拓展性方面才会达到权衡。
配置文件:
使用场合:
- 外部依赖的配置,比如pom.xml中的依赖配置;
- 同一项目团队内部达成一致的时候;
- 非代码类的资源文件(比如图片、布局、数据、签名文件等);
优点:
- 降低耦合,配置集中,容易扩展,比如应用多语言支持;
- 对象之间的关系一目了然,比如strings.xml;
- xml配置文件比注解功能齐全,支持的类型更多,比如drawable、style等;
缺点:
- 繁琐;
- 类型不安全,比如R.java中的都是资源ID,用TextView的setText方法时传入int值时无法检测出该值是否为资源ID,但@StringRes可以;
注解:
使用场合:
- 动态配置信息;
- 代为实现程序逻辑(比如xUtils中的@ViewInject代为实现findViewById);
- 代码格式检查,比如Override、Deprecated、NonNull、StringRes等,便于IDE能够检查出代码错误;
优点:
- 在class文件中,提高程序的内聚性;
- 减少重复工作,提高开发效率,比如findViewById。
缺点:
- 如果对annotation进行修改,需要重新编译整个工程;
- 业务类之间的关系不如XML配置那样一目了然;
- 程序中过多的annotation,对于代码的简洁度有一定影响;
- 扩展性较差;
参考资料:
https://www.jianshu.com/p/9471d6bcf4cf
https://www.cnblogs.com/zhengcheng/p/4264215.html
https://www.runoob.com/w3cnote/java-annotation.html
https://zhuanlan.zhihu.com/p/21410338
https://www.cnblogs.com/yangming1996/p/9295168.html