1.構造兩個JavaBean
package com.spring.model; public class People { private Car car; public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } }
package com.spring.model; public class Car { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void show() { System.out.println("我是"+name+"車"); } }
2.構建一個類似於spring配置的xml文件 spring-bean.xml
按照spring一樣的格式配置好節點和屬性
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="car" class="com.spring.model.Car"> </bean> <bean id="people" class="com.spring.model.People"> <property name="car" ref="car"></property> </bean> </beans>
3.構建一個類似spring加載配置文件的類 里面運用了反射和內省的機制
package com.spring.core; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.io.SAXReader; /** * * 模仿spring IOC、DI 底層實現 * @author GET_CHEN * */ public class ClassPathXmlApplicationContext { private String configXml; private Map<String,Object> map = new HashMap<String,Object>();//保存配置文件中的id和class所實例化出來的對象 public ClassPathXmlApplicationContext(String configXml) { this.configXml = configXml; this.doImplement(); } /** * 實現文檔的解析 * 通過反射實現IOC * 通過內省實現DI */ private void doImplement() { SAXReader saxReader = new SAXReader(); try { //通過dom4j解析文檔 Document doucment = saxReader.read(this.getClass().getClassLoader().getResourceAsStream(configXml)); //獲取beans下面的所有bean節點 List<Node> beanNodes = doucment.selectNodes("beans/bean"); if(beanNodes == null || beanNodes.size() <= 0) return; //遍歷所有的bean節點,將id和class添加到map中 for (Node bean : beanNodes) { //將節點轉為元素 Element element = (Element)bean; //獲取元素的相關屬性內容 String beanId = element.attributeValue("id"); String beanClass = element.attributeValue("class"); //——————————————IOC的實現———————————————— //通過反射將class所對應的對象,實例化出來,保存到map中 --------------> 這一步實現了IOC map.put(beanId, Class.forName(beanClass).newInstance()); } //——————————————————DI的實現—————————————————— //獲取所有的屬性標簽 List<Node> propertyNodes = doucment.selectNodes("beans/bean/property"); if(propertyNodes != null && propertyNodes.size() > 0) { //遍歷獲取name屬性和ref屬性 for (Node property : propertyNodes) { //將節點轉為元素 Element element = (Element) property; //獲取name屬性和ref屬性 String proName = element.attributeValue("name"); String proRef = element.attributeValue("ref"); //獲取當前元素的直接父元素 Element parent = element.getParent(); //獲取父元素所對應的id屬性 String parentId = parent.attributeValue("id"); //—————————————————— 內省實現依賴注入 ——————————— //獲取父元素的字節碼對象 Class parentClass = map.get(parentId).getClass(); //通過內省類,獲取父元素所指向的類的所有信息(內省對象) //第二個參數為不需要內省的類,除去Object,為了防止遍歷到Object類中的set方法中的參數 BeanInfo beanInfo = Introspector.getBeanInfo(parentClass,Object.class); //通過內省對象,獲取父元素所指向的類的所有屬性描述 PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); //遍歷屬性元素 for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { //獲取set方法所對應的屬性名 String name = propertyDescriptor.getName(); System.out.println(name+"-------"); //如果 父元素所指向的類中的setter方法的名稱 存在 與 配置中其子標簽property中name屬性相同的值就進行注入 //即:<property name="car"/> 中的 car 與 People類中的 public void setCar;中的car全小寫相同時候 if(proName.equals(name)) { //注入 //將ref對應的對象,注入給父級元素指向對象(內省對象)的set方法參數所對應的對象 //即:完成 new People().setCar(car) 的工作 propertyDescriptor.getWriteMethod().invoke(map.get(parentId), new Object[] {map.get(proRef)}); } } } } } catch (Exception e) { e.printStackTrace(); } } /** * * 獲取保存在map中的實例對象,並返回 * @param beanName * @return */ public Object getBean(String beanName) { return map.get(beanName); } }
4.測試代碼
package com.spring.test; import org.junit.Test; import com.spring.core.ClassPathXmlApplicationContext; import com.spring.model.Car; import com.spring.model.People; public class TestDemo { @Test public void h() { ClassPathXmlApplicationContext bean = new ClassPathXmlApplicationContext("com/spring/config/spring-bean.xml"); People people = (People) bean.getBean("people"); Car car = people.getCar(); car.setName("奔馳"); car.show(); } }
5.運行結果
car-------
我是奔馳車
總結:高大上的spring就是利用反射和內省的機制完成對於一個類的管理,和相關類的注入的。控制反轉主要使用的是反射機制,通過Class.fromName,獲取類的字節碼對象並實例化。依賴注入就是通過內省獲取一個類並類中的set方法所set的一個對象,通過這個對象所對應的名稱,獲取在map中與之對應的實例化對象之后。通過內省的對象調用 真實的set方法,將已實例好的對象賦值給內省對象中所對應的成員變量