上篇我們自己寫了一個很簡單的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方法調用成功
好,這樣依賴注入就完成了,下篇就要來實現比這個稍微復雜的注解的依賴注入的實現,敬請期待。。。