Spring 框架的核心在於控制反轉(IoC)與依賴注入(DI)。本文透過手寫一個簡易版 IoC 容器,深入剖析 Spring 如何管理 Bean 生命週期、解析 XML 設定、實現依賴注入。
採用標準分層架構:
model:實體類別dao:資料存取介面dao.impl:DAO 實作類別service:業務邏輯層spring:自訂 IoC 容器核心// model/User.java
public class User {
private String username;
private String password;
// getter/setter 略
}
// dao/UserDAO.java
public interface UserDAO {
void save(User user);
}
// dao/impl/UserDAOImpl.java
public class UserDAOImpl implements UserDAO {
@Override
public void save(User user) {
System.out.println("儲存用戶: " + user.getUsername());
}
}
服務類別透過 setter 注入 DAO 依賴,體現「面向介面程式設計」與「依賴倒置原則」:
// service/UserService.java
public class UserService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public void add(User user) {
userDAO.save(user);
}
}
定義 Bean 及其依賴關係:
<beans>
<bean id="userDAO" class="com.example.dao.impl.UserDAOImpl" />
<bean id="userService" class="com.example.service.UserService">
<property name="userDAO" ref="userDAO"/>
</bean>
</beans>
// spring/BeanFactory.java
public interface BeanFactory {
Object getBean(String name);
}
// spring/ClassPathXmlApplicationContext.java
public class ClassPathXmlApplicationContext implements BeanFactory {
private Map<String, Object> beans = new HashMap<>();
public ClassPathXmlApplicationContext() throws Exception {
// 1. 解析 XML
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(getClass().getClassLoader()
.getResourceAsStream("beans.xml"));
Element root = doc.getRootElement();
List<Element> beanElements = root.getChildren("bean");
// 2. 建立所有 Bean 實例
for (Element elem : beanElements) {
String id = elem.getAttributeValue("id");
String className = elem.getAttributeValue("class");
Object bean = Class.forName(className).newInstance();
beans.put(id, bean);
}
// 3. 處理依賴注入
for (Element elem : beanElements) {
String id = elem.getAttributeValue("id");
Object bean = beans.get(id);
List<Element> props = elem.getChildren("property");
for (Element prop : props) {
String name = prop.getAttributeValue("name"); // 屬性名
String ref = prop.getAttributeValue("ref"); // 依賴的 bean id
Object depBean = beans.get(ref);
// 反射呼叫 setter
String setterName = "set" + name.substring(0,1).toUpperCase() + name.substring(1);
Method setter = bean.getClass().getMethod(setterName, depBean.getClass().getInterfaces()[0]);
setter.invoke(bean, depBean);
}
}
}
@Override
public Object getBean(String name) {
return beans.get(name);
}
}
// service/UserServiceTest.java
public class UserServiceTest {
@Test
public void testAdd() throws Exception {
BeanFactory factory = new ClassPathXmlApplicationContext();
UserService service = (UserService) factory.getBean("userService");
User user = new User();
user.setUsername("testUser");
service.add(user); // 輸出: 儲存用戶: testUser
}
}
<property> 元素,識別 Bean 間依賴getBean() 提供存取實際 Spring 框架在此基礎上擴展了: