自己动手写spring容器(2)
上篇我们自己写了一个很简单的spring容器,该容器只是做了简单的bean的实例化,并没有spring的核心之一的IOC(依赖注入),也叫做控制反转,这里我就不讲这个的具体含义,不知道的园友可以自行百度,百度上有很多介绍spring IOC的,在这里我们要实现的就是spring的IOC
首先,我们需要准备一个bean的配置文件,在上篇额配置文件基础上加入了Dao的内容,现在我们要做的就是service对Dao的依赖注入。
1 <bean id="personService" class="com.yangyang.service.impl.PersonServiceImpl"> 2 <property name="personDao" ref="personDao"></property> 3 <property name="age" value="10"></property> 4 </bean> 5 <bean id="personDao" class="com.yangyang.dao.impl.PersonDaoImpl"> 6 </bean>
分析这个xml文件,知需要建立一个PropertyDefinition类,用来存储Property的属性,在此只列举了name,ref,value三个简单的属性,对集合类型的属性暂时没有做处理。
1 package com.juit; 2 3 /** 4 * 属性模型 5 * @author Administer 6 * 7 */ 8 public class PropertyDefinition { 9 10 /** 11 * 属性名称 12 */ 13 private String name; 14 /** 15 * 属性引用值 16 */ 17 private String ref; 18 19 /** 20 * 属性value值 21 */ 22 private String value; 23 public PropertyDefinition(String name, String ref,String value) { 24 this.name = name; 25 this.ref = ref; 26 this.value=value; 27 } 28 public String getName() { 29 return name; 30 } 31 public void setName(String name) { 32 this.name = name; 33 } 34 public String getRef() { 35 return ref; 36 } 37 public void setRef(String ref) { 38 this.ref = ref; 39 } 40 public String getValue() { 41 return value; 42 } 43 public void setValue(String value) { 44 this.value = value; 45 }; 46 }
当然,由于property 在bean 的下面,因此需要在BeanDefinition中加入PropertyDefinition:
完整的BeanDefinition如下:
1 package com.juit; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * Bean对象 8 * @author Administer 9 * 10 */ 11 public class BeanDefinition { 12 13 private String id;//bean的id 14 private String className;//bean的类 15 private List<PropertyDefinition> propertyDefinitions=new ArrayList<PropertyDefinition>();//bean对象的属性 16 17 public BeanDefinition(String id, String className) { 18 this.id = id; 19 this.className = className; 20 } 21 public String getId() { 22 return id; 23 } 24 public void setId(String id) { 25 this.id = id; 26 } 27 public String getClassName() { 28 return className; 29 } 30 public void setClassName(String className) { 31 this.className = className; 32 } 33 public List<PropertyDefinition> getPropertyDefinitions() { 34 return propertyDefinitions; 35 } 36 public void setPropertyDefinitions(List<PropertyDefinition> propertyDefinitions) { 37 this.propertyDefinitions = propertyDefinitions; 38 } 39 }
并在解析xml文件的地方加入对property的解析,完整的readXml如下:
1 private void readXml2(String fileName) { 2 //创建一个读取器 3 SAXReader saxReader=new SAXReader(); 4 Document document=null; 5 try { 6 //获取要读取的配置文件的路径 7 URL xmlPath=this.getClass().getClassLoader().getResource(fileName); 8 //读取文件内容 9 document=saxReader.read(xmlPath); 10 //获取xml中的根元素 11 Element rootElement=document.getRootElement(); 12 for (Iterator iterator = rootElement.elementIterator(); iterator.hasNext();) { 13 Element element = (Element) iterator.next(); 14 String id=element.attributeValue("id");//获取bean的id属性值 15 String clazz=element.attributeValue("class");//获取bean的class属性值 16 BeanDefinition beanDefinition=new BeanDefinition(id,clazz); 17 //获取bean的Property属性 18 for (Iterator subElementIterator = element.elementIterator(); subElementIterator.hasNext();) { 19 Element subElement = (Element) subElementIterator.next(); 20 String propertyName=subElement.attributeValue("name"); 21 String propertyRef= subElement.attributeValue("ref"); 22 String propertyValue=subElement.attributeValue("value"); 23 PropertyDefinition propertyDefinition=new PropertyDefinition(propertyName, propertyRef,propertyValue); 24 beanDefinition.getPropertyDefinitions().add(propertyDefinition); 25 } 26 beanDefines.add(beanDefinition); 27 } 28 } catch (Exception e) { 29 e.printStackTrace(); 30 } 31 }
接下来就要来实现关键的对依赖对象的注入功能的逻辑了。
1 public YhdClassPathXmlApplicationContext(String fileName){ 2 3 //1.读取spring的配置文件 4 this.readXml(fileName); 5 //2.实例化bean 6 this.instanceBeans(); 7 //3.实现对依赖对象的注入功能 8 this.injectObject(); 9 }
下面来完成injectObject这个功能:
1 /** 2 * 为bean对象的属性注入值 3 * 4 * Administer 5 * 2013-8-18 下午7:59:03 6 */ 7 private void injectObject() { 8 //遍历配置文件中定义的所有的bean 9 for (BeanDefinition beanDefinition : beanDefines) { 10 //找到要注入的bean 11 Object bean=sigletons.get(beanDefinition.getId()); 12 if (bean != null) { 13 try { 14 BeanInfo info = Introspector.getBeanInfo(bean.getClass());//通过类Introspector的getBeanInfo方法获取对象的BeanInfo 信息 15 //通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。 16 PropertyDescriptor[] pds = info.getPropertyDescriptors();//获得 bean所有的属性描述 17 //遍历要注入的bean的所有属性 18 for (PropertyDefinition propertyDefinition : beanDefinition.getPropertyDefinitions()) { 19 //遍历要注入bean通过属性描述器得到的所有属性以及行为 20 for (PropertyDescriptor propertyDescriptor : pds) { 21 //用户定义的bean属性与java内省后的bean属性名称相同时 22 if (propertyDefinition.getName().equals(propertyDescriptor.getName())) { 23 Method setter=propertyDescriptor.getWriteMethod();//获取属性的setter方法 24 //取到了setter方法 25 if (setter != null) { 26 Object value=null;//用来存储引用的值 27 if (propertyDefinition.getRef() != null && !propertyDefinition.getRef().equals("")) { 28 value=sigletons.get(propertyDefinition.getRef());//获取引用的对象的值 29 }else { 30 //ConvertUtil依赖两个jar包,一个是common-beanutils,而common-beanutils又依赖common-logging 31 //ConvertUtil将任意类型转化为需要的类型 32 value=ConvertUtils.convert(propertyDefinition.getValue(), propertyDescriptor.getPropertyType()); 33 } 34 setter.setAccessible(true);//保证setter方法可以访问私有 35 try { 36 setter.invoke(bean, value);//把引用对象注入到属性 37 } catch (Exception e) { 38 e.printStackTrace(); 39 } 40 } 41 break;//找到了注入的属性后,跳出循环 42 } 43 } 44 } 45 } catch (IntrospectionException e) { 46 e.printStackTrace(); 47 } 48 } 49 } 50 }
这里用到了commons-beanutils-core-1.8.3.jar、commons-logging-1.1.1.jar这两个jar,大家可以到apache的网站上进行下载,主要是用到了ConvertUtils.convert将任意类型转化为需要的类型的方法。
其实依赖注入的思想也很简单,它是通过反射机制实现的。
最后还剩下一步测试,同理
1 @Test 2 public void testInstanceSping() { 3 YhdClassPathXmlApplicationContext ctx=new YhdClassPathXmlApplicationContext("resources/beans.xml"); 4 PersonService personService=(PersonService)ctx.getBean("personService"); 5 personService.savePerson(); 6 }
personService接口代码:
1 package com.yangyang.service; 2 3 public interface PersonService { 4 public void savePerson(); 5 6 }
PersonServiceImpl实现的代码:
1 package com.yangyang.service.impl; 2 3 import com.yangyang.dao.PersonDao; 4 import com.yangyang.service.PersonService; 5 6 public class PersonServiceImpl implements PersonService{ 7 private PersonDao personDao; 8 private Integer age; 9 10 public PersonDao getPersonDao() { 11 return personDao; 12 } 13 14 public void setPersonDao(PersonDao personDao) { 15 this.personDao = personDao; 16 } 17 18 public Integer getAge() { 19 return age; 20 } 21 22 public void setAge(Integer age) { 23 this.age = age; 24 } 25 26 @Override 27 public void savePerson() { 28 System.out.println("age:"+age); 29 System.out.println("service中的save方法调用成功"); 30 personDao.savePerson(); 31 } 32 33 }
在控制台上我们可以看到:
age:10
service中的save方法调用成功
好,这样依赖注入就完成了,下篇就要来实现比这个稍微复杂的注解的依赖注入的实现,敬请期待。。。
如果您觉得阅读本文对您有帮助,请微信扫码关注作者,与我进行交流!欢迎各位转载,转载文章之后须在文章页面明显位置给出作者和原文连接,谢谢。