`
lushuaiyin
  • 浏览: 670862 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring 编码剖析@Resource注解的实现原理

 
阅读更多
Spring 编码剖析@Resource注解的实现原理
June 15, 2011 | tags 传智播客Spring2.5观看笔记 | views 619
Comments 1
下面解剖一下Spring内部是如何实现@Resource注解的,现在从头到尾通过传智播客版的Spring微量容器让它实现通过注解方式,来进行依赖对象的注入

先建一个传智播客的Resource注解 ItcastResource.java
packagejunit.test;

importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
/*
指定一下注解保留的范围,注解的信息是保留在源代码上面呢?还是只保留在源代码和编译过后的class文件中?
还是保留在运行期?如果保留在运行期那么源代码也会存在注解,编译过后的class文件也会存在注解,运行期
也会把注解加载进来
这里选择运行期,因为在运行期我们需要得到注解的信息
*/
@Target({ElementType.FIELD,ElementType.METHOD})
/*
注解可以标注在什么地方?有些可以标注在属性上面,有些可以标注在字段上面,有些可以标注在类型上面
*/
public@interfaceItcastResource{
publicStringname()default"";
}


然后将该注解加入PersonServiceBean里
PersonServiceBean.java
packagecn.itcast.service.impl;

importjunit.test.ItcastResource;

importcn.itcast.dao.PersonDao;
importcn.itcast.service.PersonService;

publicclassPersonServiceBeanimplementsPersonService{
privatePersonDaopersonDao;
privateStringname;

@ItcastResource
publicvoidsetPersonDao(PersonDaopersonDao){
this.personDao=personDao;
}

publicPersonServiceBean(){}

publicPersonServiceBean(PersonDaopersonDao,Stringname){
this.personDao=personDao;
this.name=name;
}

publicvoidsave(){
//System.out.println(name);
personDao.add();
}
}



注意了,@ItcastResource这个注解能够把beans.xml里的personDaoxxxx对象注入到属性里面吗?是不能的。通过看@ItcastResource的源代码,它什么也没做,如果我们要用注解把依赖对象注入到属性上面呢,我们后面是不是应该有个控制器
对它进行处理啊?否则它是干不了活的。 所以在这里要通过一个处理器对它进行处理,在这里我们这样做。。首先解析PersonServiceBean这个类里面的所有属性,在属性的set方法上面是否标注了@ItcastResource这个注解,如果标注了这个注解,我们就判断它是否有name属性,如果没配name属性,我们就取属性的名称,到Spring容器里寻找这个bean;如果没寻找到呢,我们再根据属性的类型去Spring容器寻找跟这个类型匹配的bean,然后再把它给注入进来,过程挺复杂的,通过代码一句一句敲,就好理解了

ItcastClassPathXMLApplicationContext.java
packagejunit.test;

importjava.beans.Introspector;
importjava.beans.PropertyDescriptor;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
importjava.net.URL;
importjava.util.ArrayList;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;

importorg.apache.commons.beanutils.ConvertUtils;
importorg.dom4j.Document;
importorg.dom4j.Element;
importorg.dom4j.XPath;
importorg.dom4j.io.SAXReader;

/**
*传智播客版的Spring容器
*/
publicclassItcastClassPathXMLApplicationContext{
privateList<BeanDefinition>beanDefines=newArrayList<BeanDefinition>();
privateMap<String,Object>sigletons=newHashMap<String,Object>();

publicItcastClassPathXMLApplicationContext(Stringfilename){
this.readXML(filename);
this.instanceBeans();
this.annotationInject();//专门处理注解方式
this.injectObject();
}
/**
*
*/
privatevoidannotationInject(){
for(StringbeanName:sigletons.keySet()){//循环所有的bean对象
Objectbean=sigletons.get(beanName);
if(bean!=null){//获取到bean对象后就判断下bean对象是否存在
try{
//对属性进行处理
PropertyDescriptor[]ps=Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
//获取bean的属性
for(PropertyDescriptorproperdesc:ps){//for循环属性描述
Methodsetter=properdesc.getWriteMethod();//获取属性的setter方法
if(setter!=null&&setter.isAnnotationPresent(ItcastResource.class)){
ItcastResourceresource=setter.getAnnotation(ItcastResource.class);
Objectvalue=null;
if(resource.name()!=null&&!"".equals(resource.name())){
value=sigletons.get(resource.name());
}else{
value=sigletons.get(properdesc.getName());
if(value==null){//当找不到与名称匹配的bean会按类型去寻找
for(Stringkey:sigletons.keySet()){//for循环所有的bean对象
if(properdesc.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())){
//判断属性的类型是否和bean想匹配
//isAssignableFrom这个方法判断properdesc.getPropertyType()这个类型是否是
//sigletons.get(key).getClass()的接口或者父类,或者是它类的本身
value=sigletons.get(key);
break;
}
}
}
}
setter.setAccessible(true);
setter.invoke(bean,value);//把引用对象注入属性
}
}
//对字段进行处理
Field[]fields=bean.getClass().getDeclaredFields();
//找到申明的所有字段
for(Fieldfield:fields){
if(field.isAnnotationPresent(ItcastResource.class)){
ItcastResourceresource=field.getAnnotation(ItcastResource.class);
Objectvalue=null;
if(resource.name()!=null&&!"".equals(resource.name())){
value=sigletons.get(resource.name());
}else{
value=sigletons.get(field.getName());
if(value==null){
for(Stringkey:sigletons.keySet()){
if(field.getType().isAssignableFrom(sigletons.get(key).getClass())){
value=sigletons.get(key);
break;
}
}
}
}
field.setAccessible(true);//设置允许访问private字段
field.set(bean,value);
}
}
}catch(Exceptione){
e.printStackTrace();
}
}
}
}

/**
*为bean对象的属性注入值
*/
privatevoidinjectObject(){
for(BeanDefinitionbeanDefinition:beanDefines){
Objectbean=sigletons.get(beanDefinition.getId());
if(bean!=null){
try{
PropertyDescriptor[]ps=Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
//Introspector通过这个类可以取得bean的定义信息
for(PropertyDefinitionpropertyDefinition:beanDefinition.getPropertys()){
for(PropertyDescriptorproperdesc:ps){
if(propertyDefinition.getName().equals(properdesc.getName())){
Methodsetter=properdesc.getWriteMethod();//获取属性的setter方法,private
if(setter!=null){//属性可能没有set方法,所以这里要判断一下
Objectvalue=null;
if(propertyDefinition.getRef()!=null&&!"".equals(propertyDefinition.getRef().trim())){
value=sigletons.get(propertyDefinition.getRef());
}else{
value=ConvertUtils.convert(propertyDefinition.getValue(),properdesc.getPropertyType());
/*
我们怎么将本身是字符串的值转成相应的属性类型的值呢?
这时我们可以使用一个叫beanutils的工具,是Apache开源组织给我们提供的,使用它的话,可以很容易的
把字符串类型的值转换成我需要的属性类型的值
*/
}
setter.setAccessible(true);//如果set方法是私有的话,要设置它允许被访问
setter.invoke(bean,value);//把引用对象注入到属性
}
break;
}
}
}
}catch(Exceptione){
}
}
}
}
/**
*实现bean的实例化
*/
privatevoidinstanceBeans(){
for(BeanDefinitionbeanDefinition:beanDefines){
try{
if(beanDefinition.getClassName()!=null&&!"".equals(beanDefinition.getClassName().trim()))
sigletons.put(beanDefinition.getId(),Class.forName(beanDefinition.getClassName()).newInstance());
}catch(Exceptione){
//通过反射技术把bean都创建出来
e.printStackTrace();
}
}

}
/**
*读取xml配置文件
*/
privatevoidreadXML(Stringfilename){
SAXReadersaxReader=newSAXReader();
Documentdocument=null;
try{
URLxmlpath=this.getClass().getClassLoader().getResource(filename);
document=saxReader.read(xmlpath);
Map<String,String>nsMap=newHashMap<String,String>();
nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
XPathxsub=document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
xsub.setNamespaceURIs(nsMap);//设置命名空间
List<Element>beans=xsub.selectNodes(document);//获取文档下所有bean节点
for(Elementelement:beans){
Stringid=element.attributeValue("id");//获取id属性值
Stringclazz=element.attributeValue("class");//获取class属性值
BeanDefinitionbeanDefine=newBeanDefinition(id,clazz);
XPathpropertysub=element.createXPath("ns:property");
propertysub.setNamespaceURIs(nsMap);//设置命名空间
List<Element>propertys=propertysub.selectNodes(element);
for(Elementproperty:propertys){
StringpropertyName=property.attributeValue("name");
Stringpropertyref=property.attributeValue("ref");
StringpropertyValue=property.attributeValue("value");
PropertyDefinitionpropertyDefinition=newPropertyDefinition(propertyName,propertyref,propertyValue);
beanDefine.getPropertys().add(propertyDefinition);
}
beanDefines.add(beanDefine);
}
}catch(Exceptione){
e.printStackTrace();
}
}
/**
*获取bean实例
*/
publicObjectgetBean(StringbeanName){
returnthis.sigletons.get(beanName);
}
}


接下来是测试 SpringTest.java
packagejunit.test;

importorg.junit.BeforeClass;
importorg.junit.Test;

importcn.itcast.service.PersonService;

publicclassSpringTest{

@BeforeClass
publicstaticvoidsetUpBeforeClass()throwsException{
}

@TestpublicvoidinstanceSpring(){
ItcastClassPathXMLApplicationContextctx=newItcastClassPathXMLApplicationContext("beans.xml");
PersonServicepersonService=(PersonService)ctx.getBean("personService");
personService.save();
//ctx.close();
}
}


我们就来观察一下,它能否帮我们采用注解@ItcastResource把依赖对象注入到属性中去,如果注入成功的话就能正确打出一句话,如果注入不成功就会有空指针异常
运行单元测试代码,控制台输出:执行PersonDaoBean里的add()方法
证明我们注入成功了。
由此可见,注解到底是怎么的工作原理呢?在这里已经给大家充分展示出来了,柚子舍CC霜注解本身干不了任何事情,它和XML文件一样,只起到配置的作用,注解代表某个业务意义(好比刚才的加入@ItcastResource注解就能注入对象,但是注解它背后可以有个处理器,这里的处理器代码里到底怎么做呢?就和annotationInject方法里的一样了,首先解析所有属性,判断属性上面是否存在这个注解,如果存在这个注解,再根据搜索规则来取得这个bean,然后通过反射技术注入进去;如果注入到字段上面呢?也一样....)
在PersonServiceBean.java里,也可以把@ItcastResource加到字段上面去,比如@ItcastResource private PersonDao personDao;
之后运行单元测试代码,控制台输出"执行PersonDaoBean里的add()方法",字段方式注入成功。

现在我们对Spring的一些采用注解方式注入依赖对象的原理进行了一个更深层次的解剖,相信以后大家在理解起来这个注解是怎么工作的,应该是很清晰的了
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics