1、前言java
IOC (Inverse of control) - 控制反轉,spring的IOC實現原理爲利用Java的反射機制並充當工廠的角色完成對象的裝配和注入。 spring
2、實現細節apache
類結構:dom
該例子須要導入如下jar包函數
① Dao接口類: PersonDao單元測試
public interface PersonDao { public void save(); }
② Dao實現類: PersonDaoImpl測試
import com.zdp.dao.PersonDao; public class PersonDaoImpl implements PersonDao { public void save(){ System.out.println("執行PersonDaoImpl中的save()方法"); } }
③ Service接口類: PersonServicethis
public interface PersonService { public void save(); }
④ Service實現類: PersonServiceImplspa
import com.zdp.dao.PersonDao; import com.zdp.service.PersonService; public class PersonServiceImpl implements PersonService { private PersonDao personDao; private String name; private Integer id; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public PersonDao getPersonDao() { return personDao; } public void setPersonDao(PersonDao personDao) { this.personDao = personDao; } public void save() { System.out.println("id: " + id + ", name: " + name); personDao.save(); } }
⑤ Bean定義類: BeanDefinition.net
import java.util.ArrayList; import java.util.List; /** * 封裝Bean * @author zhangjim */ public class BeanDefinition { private String id; private String className; private List<PropertyDefinition> propertys = new ArrayList<PropertyDefinition>(); public BeanDefinition(String id, String className) { this.id = id; this.className = className; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public List<PropertyDefinition> getPropertys() { return propertys; } public void setPropertys(List<PropertyDefinition> propertys) { this.propertys = propertys; } }
⑥ 屬性定義類: PropertyDefinition
/** * 封裝屬性 * @author zhangjim */ public class PropertyDefinition { private String name; private String ref; private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public PropertyDefinition(String name, String ref, String value) { this.name = name; this.ref = ref; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRef() { return ref; } public void setRef(String ref) { this.ref = ref; } }
⑦ Bean工廠類: ClassPathXMLApplicationContext
import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.ConvertUtils; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.XPath; import org.dom4j.io.SAXReader; /** * Spring Bean Factory */ public class ClassPathXMLApplicationContext { private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>(); private Map<String, Object> sigletons = new HashMap<String, Object>(); public ClassPathXMLApplicationContext(String filename) { this.readXML(filename); this.instanceBeans(); this.injectObject(); } /** * 爲bean對象的屬性注入值 */ private void injectObject() { for (BeanDefinition beanDefinition : beanDefines) { Object bean = sigletons.get(beanDefinition.getId()); if (bean != null) { try { PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); for (PropertyDefinition propertyDefinition : beanDefinition.getPropertys()) { for (PropertyDescriptor properdesc : ps) { if (propertyDefinition.getName().equals(properdesc.getName())) { Method setter = properdesc.getWriteMethod(); // 獲取屬性的setter方法 if (setter != null) { Object injectBean = null; if (propertyDefinition.getRef() != null && !"".equals(propertyDefinition.getRef().trim())) { injectBean = sigletons.get(propertyDefinition.getRef()); } else { injectBean = ConvertUtils.convert(propertyDefinition.getValue(), properdesc.getPropertyType()); } setter.setAccessible(true); // private method setter.invoke(bean, injectBean); // 把引用對象注入到屬性 } break; } } } } catch (Exception e) { e.printStackTrace(); } } } } /** * 完成bean的實例化 */ private void instanceBeans() { for (BeanDefinition beanDefinition : beanDefines) { try { if (beanDefinition.getClassName() != null && !"".equals(beanDefinition.getClassName().trim())) sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance()); } catch (Exception e) { e.printStackTrace(); } } } /** * 讀取xml配置文件 * * @param filename */ private void readXML(String filename) { SAXReader saxReader = new SAXReader(); Document document = null; try { URL xmlpath = this.getClass().getClassLoader().getResource(filename); document = saxReader.read(xmlpath); Map<String, String> nsMap = new HashMap<String, String>(); nsMap.put("ns", "http://www.springframework.org/schema/beans");// 加入命名空間 XPath xsub = document.createXPath("//ns:beans/ns:bean");// 建立beans/bean查詢路徑 xsub.setNamespaceURIs(nsMap);// 設置命名空間 List<Element> beans = xsub.selectNodes(document);// 獲取文檔下全部bean節點 for (Element element : beans) { String id = element.attributeValue("id");// 獲取id屬性值 String clazz = element.attributeValue("class"); // 獲取class屬性值 BeanDefinition beanDefine = new BeanDefinition(id, clazz); XPath propertysub = element.createXPath("ns:property"); propertysub.setNamespaceURIs(nsMap);// 設置命名空間 List<Element> propertys = propertysub.selectNodes(element); for (Element property : propertys) { String propertyName = property.attributeValue("name"); String propertyRef = property.attributeValue("ref"); String propertyValue = property.attributeValue("value"); PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyRef, propertyValue); beanDefine.getPropertys().add(propertyDefinition); } beanDefines.add(beanDefine); } } catch (Exception e) { e.printStackTrace(); } } /** * 獲取bean實例 * * @param beanName * @return */ public Object getBean(String beanName) { return this.sigletons.get(beanName); } }
這裏爲核心代碼,固然在實際狀況中,這一塊要複雜的多, 例如:能夠一個bean引用另外一個bean,還能夠有多個配置文件、經過多種方式載入配置文件等等,
不過原理仍是採用Java的反射機制。
⑧ beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="personDao" class="com.zdp.dao.impl.PersonDaoImpl"></bean> <bean id="personService" class="com.zdp.service.impl.PersonServiceImpl"> <property name="personDao" ref="personDao"/> <property name="name" value="zhangsan"/> <property name="id" value="123"/> </bean> </beans>
⑨ 單元測試
import org.junit.Test; import com.zdp.myspring.ClassPathXMLApplicationContext; import com.zdp.service.PersonService; public class PersonServiceImplTest { @Test public void testSave() { ClassPathXMLApplicationContext ctx = new ClassPathXMLApplicationContext("beans.xml"); PersonService personService = (PersonService)ctx.getBean("personService"); personService.save(); } }
上文僅僅是簡單地模擬了spring的IOC的實現,雖然只是完成了spring中依賴注入的一小部分,但仍是很好地展示了Java反射機制在spring中的應用,對於初學者理解IOC應該會有一點幫助。
源碼下載地址: http://download.csdn.net/detail/zdp072/7330769
3、spring的依賴注入
1. 使用構造函數注入:
<bean id="personDao" class="com.zdp.dao.impl.PersonDaoImpl" /> <bean id="personService" class="com.zdp.service.impl.PersonServiceImpl"> <constructor-arg index="0" ref="personDao" /> </bean>
public class PersonServiceImpl implements PersonService { private PersonDao personDao; public PersonServiceImpl(PersonDao personDao) { this.personDao = personDao; } public void save() { personDao.save(); } }
2. setter方法注入:
注入對象, 基本屬性, 集合
<bean id="personDao" class="com.zdp.dao.impl.PersonDaoImpl" /> <bean id="personService" class="com.zdp.service.impl.PersonServiceImpl"> <!-- <property name="personDao"> <ref bean="personDao"/> </property> --> <property name="personDao" ref="personDao" /> <property name="name" value="zhangsan" /> <property name="id" value="123" /> <property name="sets"> <set> <value>1</value> <value>2</value> </set> </property> <property name="lists"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property> <property name="maps"> <map> <entry key="1" value="1"></entry> <entry key="2" value="2"></entry> <entry key="3" value="3"></entry> <entry key="4" value="4"></entry> </map> </property> </bean>
對應Java類:
import java.util.List; import java.util.Map; import java.util.Set; import com.zdp.dao.PersonDao; import com.zdp.service.PersonService; public class PersonServiceImpl implements PersonService { private PersonDao personDao; private String name; private Integer id; private Set<String> sets; private List<String> lists; private Map<String, String> maps; // 省略get set方法 public void save() { System.out.println("id: " + id + ", name: " + name); System.out.println("sets: " + sets.size() + ", lists: " + lists.size() + ", maps: " + maps.size()); personDao.save(); } }
3. 使用註解注入:
具體內容見第四章: http://blog.csdn.net/zdp072/article/details/25558563