第一讲 Annotation
在编Java程序的时候,我们经常会碰到annotation。比如:
@Override 我们在子类继承父类的时候,会经常用到这个annotation。它告诉编译器这个方法是override父类的方法的。
@WebServlet("/myservlet") 在进行Web开发的时候,我们用这个annotation表示这个类是一个servlet。Web容器会识别这个annotation,在运行的时候调用它。
很多人说annotation是注释,初看起来有一点像,它对程序的编写和编译似乎没有什么影响,只是给人看的一个对程序的附注。从这点上,确实有一点像注释。不过,它跟注释不同的是,它会影响程序的运行。比如,上面提到的@Override,如果没有override父类的方法,编译器会给出错误提示;再比如,上面的@WebServlet,如果没有这个注解,程序是运行不起来的。
由此看来,annotation不是注释,注释是给人看的,并不影响程序的编译和运行时候的行为。annotation其实不是给人看的,那么它是给谁看的呢?它被设计出来,用于给另外的程序看的,比如编译器,比如框架,比如Web容器。
这些外在的程序通过某种方式查看到这些annotation后,就可以采取相应的行为。具体解释一下。
假如我们要做一个Web容器,类似于Tomcat这种的。它的一个基本功能就是加载servlet。按照JavaEE的规范,容器需要管理servlet的生命周期,第一件事情就是要识别哪些类是servlet。那么,容器启动的时候,可以扫描全部类,找到包含@WebServlet注解的,识别它们,然后加载它们。那么,这个@WebServlet注解就是在运行时起作用的,Java里面把它的作用范围规定为RUNTIME。
再看@Override,这个是给编译器看的,编译程序读用户程序的源代码,识别出有override注解的方法,就去检查上层父类相应方法。这个@Override注解就是在编译的时候起作用的,编译之后,就不存在了。Java里面把它的作用范围规定为SOURCE。
类似的注解还有@Test,程序员写好了程序,想交给测试框架去测试自己写的方法,就可以用这个注解。测试框架会读取源代码,识别出有@Test注解的方法,就可以进行测试了。
平时,我们可能都是用别人提供的注解来工作。注解的使用其实相当简单,我不打算在讲座中特意讲解,因为这是一个进阶的讲座。我们要研习的东西更加深一点。
接下来,我们自己动手做一个注解看看效果加深理解。
我们想做的例子是一个运行时框架加载别的客户类,并运行其中的初始化方法。作为框架,我们可以提供一个@InitMethod注解给客户程序员。客户类代码如下:
public class InitDemo {
@InitMethod
public void init(){
System.out.println("init ...");
}
}
客户类程序员在init()方法上标注了@InitMethod注解,声明这就是本类的初始化方法。框架程序识别它,并调用它。
接下来我们看怎么提供这个注解的实现。代码如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InitMethod {
}
第一次看到这个注解的实现的时候,人们都会大吃一惊,觉得很像是在定义一个 interface。的确很像,Java5之后,提供了这样的手段,让人定义注解。上面就声明了有一个叫InitMethod的注解,它是修饰方法的,在运行时可见。
问题来了,上面这一段并不是真正的实现,只是一个定义或者声明。那真正的实现怎么做呢?这些当然要定义者来提供实现的。我们作为框架程序的作者,有责任实现它,代码如下:
public class InitProcessor {
public static void main(String[] args)
throws ClassNotFoundException, IllegalAccessException,IllegalArgumentException,InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName("InitDemo");
Method[] methods = clazz.getMethods();
if(methods!=null){
for(Method method:methods){
boolean isInitMethod = method.isAnnotationPresent(InitMethod.class);