再说注解

当今的各种框架:Hibernate、Struts、Spring等,包括EJB,都支持注解形式,注解相比XML配置而言确实更为简洁,先来说说关于注解"what is"的问题。

注解

从形式上看,注解是类似注释,它为代码提供了一种新的标识方式,可以在编译器先定义、使用,等到运行时再去解析这些注解对应的含义。在JDK1.5中引入,现在的JDK已经把注解的地位提升到和二进制码相同,当然如果你稍微关注过注解,你应该知道注解解析过程的基础是反射原理。

它为什么可以使用反射原理?对比类加载使用反射可知,虚拟机在加载class文件时,也会为注解分配空间并解析,最终还会为注解和对应的二进制码建立关联,这就为使用反射提供了基础。

注解只是用于标注,并不会主动运行,也不会影响主代码的逻辑,仅仅起到辅助性的作用,但其对代码的说明能力,结合反射技术已经给了我们很大的想象空间。

从宏观上看,注解的执行共分为3部分:

  • 定义注解
  • 使用注解
  • 解析注解

定义注解

说到定义注解需要先说一下元注解,即定义注解的注解,共有四种:@Retention @Target @Document @Inherited:

  • @Retention:注解的生命时长:编译期、运行期……
  • @Target:应用位置:字段、方法、类……
  • @Documented:是否被包含在javadoc中
  • @Inherited:子类是否可以继承父类该注解

有关这几个元注解的说明不再细说,有兴趣可以查看元注解的源码,位于java.lang.annotation下,另有部分注解位于javax.annotation下。

使用注解

使用注解的方式很常见也很简答,如@MyAnnotation(ElementType.RealNew),将此注解加到类、字段、方法等上即表明此注解关联到该类的指定项上。

解析注解

这三步中,其实这是最后也是最关键的一步,上面定义、使用再好,没有一个专门解析注解的类,前面都是白写,解析注解实例会在下面说明,解析的核心步骤是:

  • 得到使用注解的类
  • 使用反射得到类中的字段、方法等
  • 得到使用了指定注解的方法、字段等,及其注解的值
  • 编写一个函数,根据注解类型及注解值进行指定操作

实例

根据以上所说,编写了一个注解实例,实例内容为:当检测到House属性中有我们自定义的注解时,向House注入一个Dog。

文件说明

  • NewMePolicy:定义一个枚举,指定注解可以使用的参数
  • NewMeAnnotation:定义一个注解
  • Dog:辅助使用注解的类
  • House:使用我们自定义注解的类
  • TestAnnotation:解析注解的类

NewMePolicy

package annotation;

public enum NewMePolicy {

	//使用单例模式获取对象
	Singleton,
	//创建新对象
	RealNew,
	//忽视此注解
	Ignore
}

NewMeAnnotation

package annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NewMeAnnotation {
	NewMePolicy category();
	public String className();
}

House

package annotation;

public class House {

	@NewMeAnnotation(category=NewMePolicy.RealNew,className="annotation.Dog")
	private Class<?> cat;

	private String other;

        // 省略getter和setter方法

}

TestAnnotation

package annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;

public class TestAnnotation {

	public static void main(String [] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException
	{
		//定义一个临时的HashMap,用于保存所有涉及到的用户自定义类实例
		HashMap<String, Object> temp=new HashMap<String, Object>();
		//定义另外一个HashMap,用于保存处理过的用户自定义类实例
		HashMap<String, Object> objMap=new HashMap<String, Object>();
		//加载House类
		Class<?> uaClass=Class.forName("annotation.House");
		//利用反射得到其所有的属性
		Field[] fields=uaClass.getDeclaredFields();
		//遍历得到的属性
		for (Field field:fields) {
			//该字段是否使用了我们自定义的注解
			boolean hasAnnotation=field.isAnnotationPresent((Class<? extends Annotation>) NewMeAnnotation.class);
			if (hasAnnotation) {
				//得到有NewMeAnnotation注解的字段
				NewMeAnnotation annotations=field.getAnnotation(NewMeAnnotation.class);
				//打印查看注解实例化的策略
				System.out.println("注入的策略为: "+annotations.category());
				//打印要注入的内容
				System.out.println("注入的类为: "+annotations.className());
				if (NewMePolicy.RealNew.equals(annotations.category())) {
					//找到对应的类,实例化
					Class<?> cat=Class.forName(annotations.className().toString());
					House house= (House) uaClass.newInstance();
					//将两个类的实例保存到temp中
					temp.put(uaClass.toString(), house);
					temp.put(Dog.class.toString(), cat);
					//注入实例
					house.setCat(cat);
					//保存定义好的UseAnnotation实例
					objMap.put(uaClass.toString(), house);
				}

				System.out.println("类: "+uaClass+"已经完成初始化");
			}
			else {
				System.out.println("字段:"+field+" 没有NewMeAnnotation注解!");
			}
		}
	}
}

控制台输出

注入的策略为: RealNew
注入的类为: annotation.Dog
类: class annotation.House已经完成初始化
字段:private java.lang.String annotation.House.other 没有NewMeAnnotation注解!

可以看到,我们可以使用反射获取到字段,及字段的注解,根据注解内容,我们可以动态的将注解规定的类Dog注入到House中,当然这个例子在解析的时候还不是很全,比如没有解析如果注解的category为singleton、Ignore时如何处理,但是作为一个解释注解的例子,我认为足够了。

总结

这个注解的例子是使用setter将一个bean注入到另外一个bean中,有没有觉得有些眼熟,对Spring,稍后的文章会解释Spring是如何根据注解管理bean之间的关系。

再说注解,布布扣,bubuko.com

时间: 2024-10-12 19:47:25

再说注解的相关文章

2、@RequestMapping注解的用法

@RequestMapping有如下属性值: 1.@RequestMapping来映射URL    注解 @RequestMapping 可以用在类定义处和方法定义处.    类定义处:规定初步的请求映射,相对于web应用的根目录:    方法定义处:进一步细分请求映射,相对于类定义处的URL.如果类定义处没有使用该注解,则方法标记的URL相对于根目录而言: package com.springmvc.helloworld_1; import org.springframework.stereo

Java注解(2)-注解处理器(运行时|RetentionPolicy.RUNTIME)

如果没有用来读取注解的工具,那注解将基本没有任何作用,它也不会比注释更有用.读取注解的工具叫作注解处理器.Java提供了两种方式来处理注解:第一种是利用运行时反射机制:另一种是使用Java提供的API来处理编译期的注解. 反射机制方式的注解处理器 仅当定义的注解的@Retention为RUNTIME时,才能够通过运行时的反射机制来处理注解.下面结合例子来说明这种方式的处理方法. Java中的反射API(如java.lang.Class.java.lang.reflect.Field等)都实现了接

Java注解(1)-注解基础

注解(Annotation)是在JAVA5中开始引入的,它为在代码中添加信息提供了一种新的方式.注解在一定程度上把元数据与源代码文件结合在一起,正如许多成熟的框架(Spring)所做的那样.那么,注解到底可以做什么呢? 1.注解的作用. 提供用来完整地描述程序所需要的信息,如编译期校验程序信息. 生成描述符文件,或生成新类的定义. 减轻编写"样板"代码(配置文件)的负担,可以使用注解自动生成. 更加干净易读的代码. 编译期类型检查. 2.Java提供的注解 Java5内置了一些原生的注

Java注解(3)-注解处理器(编译期|RetentionPolicy.SOURCE)

注解的处理除了可以在运行时通过反射机制处理外,还可以在编译期进行处理.在编译期处理注解时,会处理到不再产生新的源文件为止,之后再对所有源文件进行编译. Java5中提供了apt工具来进行编译期的注解处理.apt是命令行工具,与之配套的是一套描述"程序在编译时刻的静态结构"的API:Mirror API(com.sun.mirror.*).通过Mirror API可以获取到被注解的Java类型元素的信息,从而提供自定义的处理逻辑.具体的处理工具交给apt来处理.编写注解处理器的核心是两个

Spring 注解详解03

@Controller @Service @Autowired @RequestMapping @RequestParam @ModelAttribute @Cacheable @CacheFlush @resource @PostConstruct @PreDestroy @repository @component (不推荐使用) @scope @SessionAttributes @InitBinder @Required @qualifier // @Controller 例如 @Con

java注解中的元注解

一:java注解中的元注解 四个元注解分别是:@Target,@Retention,@Documented,@Inherited , 再次强调下元注解是java API提供,是专门用来定义注解的注解,其作用分别如下: @Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括: ElemenetType.CONSTRUCTOR----------------------------构造器声明 ElemenetType.FIELD ----------------

手动添加arraylist注解类(Contact联系人对象)

因为在Java核心库不支持arraylist xml直接注解,这里可以自己写个小工具类 Contact.java: package com.newer.xml; import java.util.ArrayList; import org.simpleframework.xml.Attribute; import org.simpleframework.xml.Element; import org.simpleframework.xml.ElementList; import org.simp

Java自定义注解

自定义注解类编写的一些规则: 1. Annotation型定义为@interface, 所有的Annotation会自动继承Java.lang.Annotation这一接口,并且不能再去继承别的类或是接口. 2. 参数成员只能用public或默认(default)这两个访问权修饰 3. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String.Enum.Class.annotations等数据类型,以及这一些类

MyBatis应用开发(3)应用之开发方式注解方式篇

1.1. 注解方式 1.1.1. 开发步骤 目标:使用MyBatis从数据库中查询t_person表的全部记录. MyBatis还可以使用注解来配置数据库中的记录与Java对象之间的映射关系.此时SQL语句出现在Mapper接口的方法的注解中.优点是不再需要编写独立的Mapper配置文件. 使用MyBatis 的注解方式开发数据库应用的步骤如下所示: (1)编写POJO类Person. (2)编写Mapper接口PersonMapper. (3)编写业务接口PersonService. (4)编

注解方式实现声明式事务管理

使用注解实现Spring的声明式事务管理,更加简单! 步骤: 1) 必须引入Aop相关的jar文件 2) bean.xml中指定注解方式实现声明式事务管理以及应用的事务管理器类 3)在需要添加事务控制的地方,写上: @Transactional @Transactional注解: 1)应用事务的注解 2)定义到方法上: 当前方法应用spring的声明式事务 3)定义到类上:   当前类的所有的方法都应用Spring声明式事务管理; 4)定义到父类上: 当执行父类的方法时候应用事务. 修改bean