IoC則是一種 軟件設計模式,簡單來說Spring通過工廠+反射來實現IoC。
原理簡單說明:
其實就是通過解析xml文件,通過反射創建出我們所需要的bean,再將這些bean挨個放到集合中,然後對外提供一個getBean()方法,以便我們獲得這bean。
通俗來講就如同婚姻介紹所,只需要告訴它找個什麼樣的女朋友,然後婚介就會按照我們的要求,提供一個mm,如果婚介給我們的人選不符合要求,我們就會拋出異常。
簡單實現:
1.需要引用maven依賴:
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>1.1.3</version>
</dependency>
2.beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="user" class="com.example.domain.User" />
<bean id="userDAO" class="com.example.dao.impl.UserDAOImpl" />
<bean id="userService" class="com.example.service.UserService">
<property name="userDAO" bean="userDAO" />
</bean>
</beans>
3.BeanFactory
package com.example.ioc;
public interface BeanFactory {
Object getBean(String name);
}
4.ClassPathXmlApplicationContext:讀取xml文件內容,並創建對象及對象關系(使用setter方式)
package com.example.ioc;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ClassPathXmlApplicationContext implements BeanFactory {
private Map<String, Object> beans = new HashMap<String, Object>();
public ClassPathXmlApplicationContext() throws Exception {
SAXBuilder sb = new SAXBuilder();
// 構造文檔對象
Document doc = sb.build(ClassPathXmlApplicationContext.class
.getClassLoader().getResourceAsStream("beans.xml"));
// 獲取根元素
Element root = doc.getRootElement();
// 取到根元素所有元素
List list = root.getChildren();
setBeans(list);
}
//設置Bean
private void setBeans(List list) throws Exception {
for (int i = 0; i < list.size(); i++) {
Element element = (Element) list.get(i);
String id = element.getAttributeValue("id");
//取得class子元素
String clzss = element.getAttributeValue("class");
//通過反射進行實例化
Object o = Class.forName(clzss).newInstance();
beans.put(id, o);
setProperty(element, o);
}
}
//獲取property進行依賴注入
private void setProperty(Element element, Object o) throws Exception {
for (Element property : (List<Element>) element.getChildren("property")) {
String name = property.getAttributeValue("name");
String bean = property.getAttributeValue("bean");
//從beans.xml中根據id取到類的對象
Object beanObj = this.getBean(bean);
System.out.println(beanObj);//com.example.dao.impl.UserDAOImpl@2f4d3709
//組成setXXX方法名
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
// 反射機制對方法進行調用,將對象在加載bean時就注入到環境上下文中
Method m = o.getClass().getMethod(methodName, beanObj.getClass().getInterfaces()[0]);
m.invoke(o, beanObj);
}
}
@Override
public Object getBean(String name) {
return beans.get(name);
}
}
以上為核心代碼,當然在實際情況中,這一塊要復雜的多, 例如:可以一個bean引用另一個bean,還可以有多個配置文件、通過多種方式載入配置文件等等,不過原理還是采用Java的反射機制。
實現的效果為:
Service service=(Service)beans.get("userService");
Dao dao = (Dao)beans.get("userDAO");
//依賴注入,Service實現依賴dao的實現
service.setDao(dao);
5.User:實體類
package com.example.domain;
public class User {
private String userName;
private String password;
/**
* @return the userName
*/
public String getUserName() {
return userName;
}
/**
* @param userName the userName to set
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* @return the password
*/
public String getPassword() {
return password;
}
/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(this.userName);
sb.append(this.password);
return sb.toString();
}
}
6.UserDAO
package com.example.dao;
import com.example.domain.User;
public interface UserDAO {
void save(User u);
void delete();
}
7.UserDAOImpl
package com.example.dao.impl;
import com.example.dao.UserDAO;
import com.example.domain.User;
public class UserDAOImpl implements UserDAO {
@Override
public void save(User u) {
System.out.println("User:" + u.toString());
}
@Override
public void delete() {
System.out.println("delete User");
}
}
8.UserService
package com.example.service;
import com.example.dao.UserDAO;
import com.example.domain.User;
public class UserService {
private UserDAO userDAO;
public void addUser(User u) {
this.userDAO.save(u);
}
/**
* @return the userDAO
*/
public UserDAO getUserDAO() {
return userDAO;
}
/**
* @param userDAO the userDAO to set
*/
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
}
9.測試:
package com.example.ioc;
import com.example.domain.User;
import com.example.service.UserService;
public class RunIoc {
public static void main(String[] args) throws Exception {
BeanFactory factory = new ClassPathXmlApplicationContext();
//通過工廠直接獲取
UserService userService = (UserService) factory.getBean("userService");
//其實User也可以從工廠中獲得
User u = (User) factory.getBean("user");
//User u = new User();
u.setUserName("tom");
u.setPassword("123456");
userService.addUser(u);//打印結果tom123456
}
}
小結
上文僅僅是簡單地模擬了spring的IOC的實現,雖然只是完成了spring中依賴注入的一小部分,但還是很好地展現了Java反射機制在spring中的應用