Java 反射和內省實現spring的IOC和DI


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方法,將已實例好的對象賦值給內省對象中所對應的成員變量


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM