spring的核心是IOC,控制反轉(zhuǎn),也叫DI依賴注入,意思就是以前類的實(shí)例化由自己控制,什么時(shí)候需要什么時(shí)候就new一個(gè),現(xiàn)在改了,new的動(dòng)作交由spring來(lái)管理,spring使用BeanFactory來(lái)實(shí)例化、配置和管理對(duì)象,但是它只是一個(gè)接口,里面有一個(gè)getBean()方法。我們一般都不直接用BeanFactory,而是用它的實(shí)現(xiàn)類ApplicationContext,這個(gè)類會(huì)自動(dòng)解析我們配置的applicationContext.xml,然后根據(jù)我們配置的bean來(lái)new對(duì)象,將new好的對(duì)象放進(jìn)一個(gè)Map中,鍵就是我們bean的id,值就是new的對(duì)象。
原理聽起來(lái)比較清晰,下面就模擬一個(gè)具體實(shí)現(xiàn)。
整個(gè)工程的結(jié)構(gòu)圖如下所示:
pom.xml添加解析xml的依賴,用最簡(jiǎn)單的即可。
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>2.0.2</version>
</dependency>
定義一個(gè)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的實(shí)現(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接口的實(shí)現(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的實(shí)例,當(dāng)然不用new出來(lái),我們就通過(guò)spring的IOC把這里的dao層注入進(jìn)來(lái)。不要忘了對(duì)dao提供set get方法,因?yàn)镮OC的底層其實(shí)就是利用反射機(jī)制實(shí)現(xiàn)的,他把dao注入進(jìn)來(lái),其實(shí)底層就是通過(guò)反射set進(jìn)來(lái)的。
核心配置文件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);
}
里面就一個(gè)方法,就是根據(jù)id來(lái)獲取Bean
ClassPathXmlApplicationContext 來(lái)實(shí)現(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)造文檔對(duì)象
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類,通過(guò)他,在我們new出他的對(duì)象的時(shí)候,他來(lái)加載配置文件,然后把我們的dao操作注入到我們的service層,在spring中,ClassPathXmlApplicationContext類實(shí)現(xiàn)了BeanFactory接口,在此我們也定義一個(gè)BeanFactory接口,其實(shí)這個(gè)接口沒什么具體的作用,我們就是為了來(lái)模擬spring。
這一段代碼是核心,解釋一下:
首先我們定義了一個(gè)容器Map<String, Object> beans,這個(gè)容器的作用就是用來(lái)裝我們從配置文件里解析來(lái)的一個(gè)個(gè)bean,為什么要用map類型,很簡(jiǎn)單就能想到,整個(gè)工程中的bean id一定要唯一,不能沖突,我們配置文件中每一個(gè)bean都有一個(gè)id來(lái)作為自己的唯一身份。我們把這個(gè)id存到map的key里面,然后value就裝我們的具體bean對(duì)象。
說(shuō)完這個(gè)容器之后,下面我們?cè)趤?lái)看一下ClassPathXmlApplicationContext的構(gòu)造方法,這個(gè)構(gòu)造方法是我們spring管理容器的核心,這個(gè)構(gòu)造方法的前半部分是利用的jdom解析方式,把xml里面的bean一個(gè)個(gè)的解析出來(lái),然后把解析出來(lái)的bean在放到我們bean容器里。后半部分主要是在對(duì)配置文件進(jìn)行解析出bean的同時(shí)去查看一下這個(gè)bean中有沒有需要注射bean的,如果有的話,他就去通過(guò)這些里面的property屬性獲取他要注射的bean名字,然后構(gòu)造出set方法,然后通過(guò)反射,調(diào)用注入bean的set方法,這樣我們所需要的bean就被注入進(jìn)來(lái)了。
最后我們就來(lái)看一下實(shí)現(xiàn)接口的getBean放了,其實(shí)這個(gè)方法很簡(jiǎn)單,就是根據(jù)提供的bean的id,從bean容器內(nèi)把對(duì)應(yīng)的bean取出來(lái)。
我們所需的東西都定義好了,下面我們據(jù)來(lái)測(cè)試一下,看看模仿的spring到底能不能自動(dòng)把我們所需要的dao層給我們注入進(jìn)來(lái)。
Test.java測(cè)試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);
}
}
打印出
整個(gè)的原理就是這些,不過(guò)Spring的BeangongFactory實(shí)現(xiàn)要比這復(fù)雜的多,還要涉及注解、多種注入方式等等,不過(guò)原理就是這些。