spring的核心是IOC,控制反轉(zhuǎn)溺职,也叫DI依賴注入位喂,意思就是以前類的實例化由自己控制,什么時候需要什么時候就new一個忆某,現(xiàn)在改了弃舒,new的動作交由spring來管理状原,spring使用BeanFactory來實例化颠区、配置和管理對象,但是它只是一個接口器贩,里面有一個getBean()方法朋截。我們一般都不直接用BeanFactory部服,而是用它的實現(xiàn)類ApplicationContext,這個類會自動解析我們配置的applicationContext.xml奉芦,然后根據(jù)我們配置的bean來new對象,將new好的對象放進一個Map中烦却,鍵就是我們bean的id,值就是new的對象短绸。
原理聽起來比較清晰筹裕,下面就模擬一個具體實現(xiàn)。
整個工程的結(jié)構(gòu)圖如下所示:
pom.xml添加解析xml的依賴证逻,用最簡單的即可囚企。
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>2.0.2</version>
</dependency>
定義一個model龙宏,User.java
public class User {
private int id;
private String name;
//get set省略
}
定義接口UserDao.java
public interface UserDao {
/**
* 新增用戶
* @param user
*/
public void add(User user);
}
定義UserDao的實現(xiàn)類UserDaoImpl.java
public class UserDaoImpl implements UserDao {
@Override
public void add(User user) {
System.out.println("新增用戶成功");
}
}
定義接口UserService.java
public interface UserService {
/**
* 新增用戶
* @param user
*/
public void add(User user);
}
定義UserService接口的實現(xiàn)類UserServiceImple.java
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void add(User user) {
userDao.add(user);
System.out.println("Service執(zhí)行新增用戶银酗。黍特。锯蛀。");
}
}
這里面定義Userdao旁涤,并加入set方法。這里是模擬spring瞳遍,主要模擬spring中的IOC功能傅蹂,所以在此我們一樣要在service層中定義dao的實例,當然不用new出來犁功,我們就通過spring的IOC把這里的dao層注入進來婚夫。不要忘了對dao提供set get方法案糙,因為IOC的底層其實就是利用反射機制實現(xiàn)的,他把dao注入進來怒医,其實底層就是通過反射set進來的稚叹。
核心配置文件Beans.xml
<beans>
<bean id="userDao" class="com.critc.dao.UserDaoImpl"/>
<bean id="userService" class="com.critc.service.UserServiceImpl">
<property name="userDao" bean="userDao"/>
</bean>
</beans>
Bean工廠接口BeanFactory.java
public interface BeanFactory {
public Object getBean(String id);
}
里面就一個方法扒袖,就是根據(jù)id來獲取Bean
ClassPathXmlApplicationContext 來實現(xiàn)BeanFactory
public class ClassPathXmlApplicationContext implements BeanFactory {
private Map<String, Object> beans = new HashMap<String, Object>();
public ClassPathXmlApplicationContext() throws Exception, Exception {
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(this.getClass().getClassLoader()
.getResourceAsStream("beans.xml")); // 構(gòu)造文檔對象
Element root = doc.getRootElement(); // 獲取根元素HD
List list = root.getChildren("bean");// 取名字為bean的所有元素
for (int i = 0; i < list.size(); i++) {
Element element = (Element) list.get(i);
String id = element.getAttributeValue("id");
String clazz = element.getAttributeValue("class");
Object o = Class.forName(clazz).newInstance();
System.out.print("bean id is " + id);
System.out.println(", clazz is " + clazz);
beans.put(id, o);
// 遍歷property
for (Element propertyElement : (List<Element>) element.getChildren("property")) {
String name = propertyElement.getAttributeValue("name");// userDAO
String bean = propertyElement.getAttributeValue("bean");// u
Object beanObject = beans.get(bean);// UserDAOImpl instance
// 構(gòu)造setter方法
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
System.out.println("setter method name = " + methodName);
Method m = o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);
m.invoke(o, beanObject);
}
}
}
@Override
public Object getBean(String id) {
return beans.get(id);
}
}
定義模擬的ClassPathXmlApplicationContext類亩码,通過他,在我們new出他的對象的時候描沟,他來加載配置文件,然后把我們的dao操作注入到我們的service層蠢络,在spring中衰猛,ClassPathXmlApplicationContext類實現(xiàn)了BeanFactory接口,在此我們也定義一個BeanFactory接口娜睛,其實這個接口沒什么具體的作用卦睹,我們就是為了來模擬spring畦戒。
這一段代碼是核心结序,解釋一下:
首先我們定義了一個容器Map<String, Object> beans,這個容器的作用就是用來裝我們從配置文件里解析來的一個個bean,為什么要用map類型邀层,很簡單就能想到遂庄,整個工程中的bean id一定要唯一寥院,不能沖突涛目,我們配置文件中每一個bean都有一個id來作為自己的唯一身份。我們把這個id存到map的key里面估蹄,然后value就裝我們的具體bean對象。
說完這個容器之后元媚,下面我們在來看一下ClassPathXmlApplicationContext的構(gòu)造方法苗沧,這個構(gòu)造方法是我們spring管理容器的核心,這個構(gòu)造方法的前半部分是利用的jdom解析方式待逞,把xml里面的bean一個個的解析出來识樱,然后把解析出來的bean在放到我們bean容器里。后半部分主要是在對配置文件進行解析出bean的同時去查看一下這個bean中有沒有需要注射bean的当犯,如果有的話割疾,他就去通過這些里面的property屬性獲取他要注射的bean名字宏榕,然后構(gòu)造出set方法,然后通過反射奠支,調(diào)用注入bean的set方法抚芦,這樣我們所需要的bean就被注入進來了迈螟。
最后我們就來看一下實現(xiàn)接口的getBean放了井联,其實這個方法很簡單您旁,就是根據(jù)提供的bean的id,從bean容器內(nèi)把對應的bean取出來蚕脏。
我們所需的東西都定義好了侦锯,下面我們據(jù)來測試一下,看看模仿的spring到底能不能自動把我們所需要的dao層給我們注入進來尺碰。
Test.java測試IOC
public class Test {
public static void main(String[] args)throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
User user = new User();
UserService userService = (UserService) context.getBean("userService");
userService.add(user);
}
}
打印出
整個的原理就是這些,不過Spring的BeangongFactory實現(xiàn)要比這復雜的多洛心,還要涉及注解题篷、多種注入方式等等,不過原理就是這些法严。