首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

手把手實現迷你 Spring IOC 容器:從 XML 解析到依賴注入

     編輯:Java 綜合教學

手把手實現迷你 Spring IOC 容器:從 XML 解析到依賴注入

理解 Spring IOC 最佳途徑是親手寫一個精簡版容器。本文實現核心流程:BeanFactory 介面定義、AbstractBeanFactory 模板方法、SourceReader 介面與 XMLSourceReader 具體實現(採用 Dom4j 解析 XML)、BeanInfo 承載 Bean 定義元資料、反射實例化與屬性注入、XMLContext 整合啟動。完整代碼可直接運行驗證。

核心類設計

BeanFactory (介面)
    ↑
AbstractBeanFactory (抽象模板:getBean、parseBean、registerBeans)
    ↑
XMLContext (具體上下文:指定 XMLSourceReader)
    ↑
XMLSourceReader (實現 SourceReader,Dom4j 解析 XML → Map<String, BeanInfo>)
    ↑
BeanInfo (id、type、properties Map)

關鍵代碼片段

1. BeanFactory 介面

public interface BeanFactory {
    Object getBean(String name);
}

2. AbstractBeanFactory 模板方法

public abstract class AbstractBeanFactory implements BeanFactory {
    private Map<String, BeanInfo> container;
    protected SourceReader reader;

    public AbstractBeanFactory(String filePath) { this.filePath = filePath; }
    protected abstract void setReader(SourceReader reader);

    public void registerBeans() {
        this.container = reader.loadBeans(filePath);
    }

    @Override
    public Object getBean(String name) {
        BeanInfo info = container.get(name);
        return info == null ? null : parseBean(info);
    }

    // 反射實例化 + 屬性注入
    protected Object parseBean(BeanInfo info) {
        Class<?> clazz = Class.forName(info.getType());
        Object bean = clazz.newInstance();
        for (Map.Entry<String, Object> e : info.getProperties().entrySet()) {
            String setter = "set" + Character.toUpperCase(e.getKey().charAt(0)) + e.getKey().substring(1);
            Arrays.stream(clazz.getDeclaredMethods())
                  .filter(m -> m.getName().equals(setter))
                  .findFirst()
                  .ifPresent(m -> m.invoke(bean, e.getValue()));
        }
        return bean;
    }
}

3. XMLSourceReader 解析 XML

public class XMLSourceReader implements SourceReader {
    @Override
    public Map<String, BeanInfo> loadBeans(String filePath) {
        SAXReader reader = new SAXReader();
        InputStream ins = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
        Document doc = reader.read(ins);
        Element root = doc.getRootElement();

        Map<String, BeanInfo> map = new HashMap<>();
        for (Iterator i = root.elementIterator("bean"); i.hasNext(); ) {
            Element ele = (Element) i.next();
            String id = ele.attributeValue("id");
            String cls = ele.attributeValue("class");

            BeanInfo info = new BeanInfo();
            info.setId(id); info.setType(cls);

            // 解析 yyy
            for (Iterator it = ele.elementIterator("property"); it.hasNext(); ) {
                Element prop = (Element) it.next();
                String name = prop.attributeValue("name");
                String value = prop.elementText("value");
                info.addProperty(name, value);
            }
            map.put(id, info);
        }
        return map;
    }
}

4. XMLContext 啟動類

public class XMLContext extends AbstractBeanFactory {
    public XMLContext(String filePath) {
        super(filePath);
        setReader(new XMLSourceReader());
        registerBeans();
    }
    @Override protected void setReader(SourceReader r) { this.reader = r; }
}

5. 配置文件 config.xml

<beans>
    <bean id="person" class="ioc.bean.Person">
        <property name="username"><value>zzf</value></property>
        <property name="password"><value>12345678</value></property>
    </bean>
</beans>

6. 測試驗證

public class Test {
    public static void main(String[] args) {
        BeanFactory factory = new XMLContext("configuration/config.xml");
        Person p = (Person) factory.getBean("person");
        System.out.println(p.getUsername()); // zzf
        System.out.println(p.getPassword()); // 12345678
    }
}

運行結果

控制台輸出 zzf 和 12345678

擴展方向(邁向生產級)

  1. 支援 constructor-arg 建構子注入。
  2. 實現 BeanPostProcessor、InitializingBean 生命週期回調。
  3. 加入 FactoryBean、@Autowired 註解掃描。
  4. 循環依賴檢測與三級快取解決。
  5. 替換 Dom4j 為標準 JAXP/Xerces,移除第三方依賴。

總結

通過 6 個核心類、不足 200 行代碼,實現了「XML 配置 → 解析 → 反射實例化 → 屬性注入 → 容器獲取」的完整 IOC 閉環。這正是 Spring IOC 最精華的骨架,理解它能助你在排查 Bean 創建失敗、屬性未注入、循環依賴等問題時直擊本質。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved