Java注解浅谈

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

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

  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初始化时,做额外的增强型处理。
 
  拆分了注解后,我们会发现注解也没那么神秘。接下来可以设计个属于自己的注解了:
  自定义注解:

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


 注解使用类:

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


 测试:

  // 通过反射获得注解
  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注解浅谈

上一篇:cocos2dx入门视频教程 (cocos2d-x)


下一篇:有趣的装B利器-快速打出代码