接觸Spring快半年了,前段時(shí)間剛用Spring4+S2H4做完了自己的畢設(shè),但是很明顯感覺對(duì)Spring尤其是IOC容器的實(shí)現(xiàn)原理理解的不到位,說白了,就是僅僅停留在會(huì)用的階段,有一顆想讀源碼的心于是買了一本計(jì)文柯的《Spring技術(shù)內(nèi)幕》,第二章沒看完,就被我扔一邊了,看的那是相當(dāng)痛苦,深深覺得自己資質(zhì)尚淺,能力還不夠,昨天在網(wǎng)上碰巧看到一個(gè)實(shí)現(xiàn)簡(jiǎn)單的SpringIOC容器的視頻教程,于是跟著做了一遍,竟然相當(dāng)順利,至少每一行代碼都能理解,于是細(xì)心整理了一番,放在這里.
創(chuàng)新互聯(lián)是專業(yè)的遠(yuǎn)安網(wǎng)站建設(shè)公司,遠(yuǎn)安接單;提供網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站,網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行遠(yuǎn)安網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
主要思想:
提到IOC,第一反應(yīng)就是控制反轉(zhuǎn),我以前以為SpringIOC就是控制反轉(zhuǎn),控制反轉(zhuǎn)就是SpringIOC,當(dāng)然這種理解是錯(cuò)誤的,控制反轉(zhuǎn)是一種思想,一種模式,而Spring的IOC容器是實(shí)現(xiàn)了這種思想這種模式的一個(gè)載體.
使用過Spring的人都熟知,SpringIOC容器可以在對(duì)象生成或初始化時(shí)就直接將數(shù)據(jù)注入到對(duì)象中,如果對(duì)象A的屬性是另一個(gè)對(duì)象B,還可以將這個(gè)對(duì)象B的引用注入到注入到A的數(shù)據(jù)域中.
如果在初始化對(duì)象A的時(shí)候,對(duì)象B還沒有進(jìn)行初始化,而A又需要對(duì)象B作為自己的屬性,那么就會(huì)用一種遞歸的方式進(jìn)行注入,這樣就可以把對(duì)象的依賴關(guān)系清晰有序的建立起來.
IOC容器解決問題的核心就是把創(chuàng)建和管理對(duì)象的控制權(quán)從具體的業(yè)務(wù)對(duì)象手中搶過來.由IOC容器來管理對(duì)象之間的依賴關(guān)系,并由IOC容器完成對(duì)象的注入.這樣就把應(yīng)用從復(fù)雜的對(duì)象依賴關(guān)系的管理中解放出來,簡(jiǎn)化了程序的開發(fā)過程.
下圖是這個(gè)簡(jiǎn)單IOC容器的類圖(原諒我真沒學(xué)過UML,湊合看吧):

程序中所有的Bean之間的依賴關(guān)系我們是放在一個(gè)xml文件中進(jìn)行維護(hù)的,就是applicationContext.xml
ConfigManager類完成的功能是讀取xml,并將所有讀取到有用的信息封裝到我們創(chuàng)建的一個(gè)Map<String,Bean>集合中,用來在初始化容器時(shí)創(chuàng)建bean對(duì)象.
定義一個(gè)BeanFactory的接口,接口中有一個(gè)getBean(String name)方法,用來返回你想要?jiǎng)?chuàng)建的那個(gè)對(duì)象.
然后定義一個(gè)該接口的實(shí)現(xiàn)類ClassPathXmlApplicationContext.就是在這個(gè)類的構(gòu)造方法中,初始化容器,通過調(diào)用ConfigManager的方法返回的Map集合,通過反射和內(nèi)省一一創(chuàng)建bean對(duì)象.這里需要注意,對(duì)象的創(chuàng)建有兩個(gè)時(shí)間點(diǎn),這取決與bean標(biāo)簽中scope屬性的值:
使用的主要知識(shí)點(diǎn):
項(xiàng)目結(jié)構(gòu)圖及介紹如下:
項(xiàng)目需要的jar包與項(xiàng)目結(jié)構(gòu)已經(jīng)在上圖中介紹了,這個(gè)項(xiàng)目所能實(shí)現(xiàn)的功能如下:
1. IOC容器能管理對(duì)象的創(chuàng)建以及對(duì)象之間的依賴關(guān)系.
2. 能夠?qū)崿F(xiàn)數(shù)據(jù)的自動(dòng)類型轉(zhuǎn)換(借助BeanUtils).
3. 能夠?qū)崿F(xiàn)scope="singleton"和scope="prototype"的功能,即能夠控制對(duì)象是否為單例.
下面介紹代碼部分:
application.xml:
<?xml version="1.0" encoding="utf-8"?>
<beans>
<bean name="student" class="com.wang.entity.Student" >
<property name="name" value="123"></property>
</bean>
<bean name="teacher" class="com.wang.entity.Teacher">
<property name="student" ref="student"></property>
</bean>
<bean name="person" class="com.wang.entity.Person" scope="prototype">
<property name="teacher" ref="teacher"></property>
<property name="student" ref="student"></property>
</bean>
</beans>實(shí)體類Student,Teacher,Person:
package com.wang.entity;
//Student類
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/************************************/
package com.wang.entity;
//Teacher類
public class Teacher {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
/************************************/
package com.wang.entity;
//Person類
public class Person {
private Student student;
private Teacher teacher;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
用于封裝Bean標(biāo)簽信息的Bean類:
package com.wang.config;
import java.util.ArrayList;
import java.util.List;
public class Bean {
private String name;
private String className;
private String scope="singleton";
private List<Property> properties=new ArrayList<Property>();
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public List<Property> getProperties() {
return properties;
}
public void setProperties(List<Property> properties) {
this.properties = properties;
}
}
用與封裝Bean子標(biāo)簽property內(nèi)容的Property類:
package com.wang.config;
public class Property {
private String name;
private String value;
private String ref;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
}
ConfigManager類:
package com.wang.config.parse;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Test;
import com.wang.config.Bean;
import com.wang.config.Property;
public class ConfigManager {
private static Map<String,Bean> map=new HashMap<String,Bean>();
//讀取配置文件并返回讀取結(jié)果
//返回Map集合便于注入,key是每個(gè)Bean的name屬性,value是對(duì)應(yīng)的那個(gè)Bean對(duì)象
public static Map<String, Bean> getConfig(String path){
/*dom4j實(shí)現(xiàn)
* 1.創(chuàng)建解析器
* 2.加載配置文件,得到document對(duì)象
* 3.定義xpath表達(dá)式,取出所有Bean元素
* 4.對(duì)Bean元素繼續(xù)遍歷
* 4.1將Bean元素的name/class屬性封裝到bean類屬性中
* 4.2獲得bean下的所有property子元素
* 4.3將屬性name/value/ref分裝到類Property類中
* 5.將property對(duì)象封裝到bean對(duì)象中
* 6.將bean對(duì)象封裝到Map集合中,返回map
*/
//1.創(chuàng)建解析器
SAXReader reader=new SAXReader();
//2.加載配置文件,得到document對(duì)象
InputStream is = ConfigManager.class.getResourceAsStream(path);
Document doc =null;
try {
doc = reader.read(is);
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException("請(qǐng)檢查您的xml配置是否正確");
}
// 3.定義xpath表達(dá)式,取出所有Bean元素
String xpath="http://bean";
//4.對(duì)Bean元素繼續(xù)遍歷
List<Element> list = doc.selectNodes(xpath);
if(list!=null){
//4.1將Bean元素的name/class屬性封裝到bean類屬性中
// 4.3將屬性name/value/ref分裝到類Property類中
for (Element bean : list) {
Bean b=new Bean();
String name=bean.attributeValue("name");
String clazz=bean.attributeValue("class");
String scope=bean.attributeValue("scope");
b.setName(name);
b.setClassName(clazz);
if(scope!=null){
b.setScope(scope);
}
// 4.2獲得bean下的所有property子元素
List<Element> children = bean.elements("property");
// 4.3將屬性name/value/ref分裝到類Property類中
if(children!=null){
for (Element child : children) {
Property prop=new Property();
String pName=child.attributeValue("name");
String pValue=child.attributeValue("value");
String pRef=child.attributeValue("ref");
prop.setName(pName);
prop.setRef(pRef);
prop.setValue(pValue);
// 5.將property對(duì)象封裝到bean對(duì)象中
b.getProperties().add(prop);
}
}
//6.將bean對(duì)象封裝到Map集合中,返回map
map.put(name, b);
}
}
return map;
}
}
BeanFactory接口:
package com.wang.main;
public interface BeanFactory {
//核心方法getBean
Object getBean(String name);
}
ClassPathXmlApplicationContext類:
package com.wang.main;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.beanutils.BeanUtils;
import org.junit.Test;
import com.wang.config.Bean;
import com.wang.config.Property;
import com.wang.config.parse.ConfigManager;
import com.wang.entity.Student;
//import com.wang.utils.BeanUtils;
import com.wang.utils.BeanUtil;
public class ClassPathXmlApplicationContext implements BeanFactory {
// 獲得讀取的配置文件中的Map信息
private Map<String, Bean> map;
// 作為IOC容器使用,放置sring放置的對(duì)象
private Map<String, Object> context = new HashMap<String, Object>();
public ClassPathXmlApplicationContext(String path) {
// 1.讀取配置文件得到需要初始化的Bean信息
map = ConfigManager.getConfig(path);
// 2.遍歷配置,初始化Bean
for (Entry<String, Bean> en : map.entrySet()) {
String beanName = en.getKey();
Bean bean = en.getValue();
Object existBean = context.get(beanName);
// 當(dāng)容器中為空并且bean的scope屬性為singleton時(shí)
if (existBean == null && bean.getScope().equals("singleton")) {
// 根據(jù)字符串創(chuàng)建Bean對(duì)象
Object beanObj = createBean(bean);
// 把創(chuàng)建好的bean對(duì)象放置到map中去
context.put(beanName, beanObj);
}
}
}
// 通過反射創(chuàng)建對(duì)象
private Object createBean(Bean bean) {
// 創(chuàng)建該類對(duì)象
Class clazz = null;
try {
clazz = Class.forName(bean.getClassName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("沒有找到該類" + bean.getClassName());
}
Object beanObj = null;
try {
beanObj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("沒有提供無參構(gòu)造器");
}
// 獲得bean的屬性,將其注入
if (bean.getProperties() != null) {
for (Property prop : bean.getProperties()) {
// 注入分兩種情況
// 獲得要注入的屬性名稱
String name = prop.getName();
String value = prop.getValue();
String ref = prop.getRef();
// 使用BeanUtils工具類完成屬性注入,可以自動(dòng)完成類型轉(zhuǎn)換
// 如果value不為null,說明有
if (value != null) {
Map<String, String[]> parmMap = new HashMap<String, String[]>();
parmMap.put(name, new String[] { value });
try {
BeanUtils.populate(beanObj, parmMap);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("請(qǐng)檢查你的" + name + "屬性");
}
}
if (ref != null) {
// 根據(jù)屬性名獲得一個(gè)注入屬性對(duì)應(yīng)的set方法
// Method setMethod = BeanUtil.getWriteMethod(beanObj,
// name);
// 看一看當(dāng)前IOC容器中是否已存在該bean,有的話直接設(shè)置沒有的話使用遞歸,創(chuàng)建該bean對(duì)象
Object existBean = context.get(prop.getRef());
if (existBean == null) {
// 遞歸的創(chuàng)建一個(gè)bean
existBean = createBean(map.get(prop.getRef()));
// 放置到context容器中
// 只有當(dāng)scope="singleton"時(shí)才往容器中放
if (map.get(prop.getRef()).getScope()
.equals("singleton")) {
context.put(prop.getRef(), existBean);
}
}
try {
// setMethod.invoke(beanObj, existBean);
//通過BeanUtils為beanObj設(shè)置屬性
BeanUtils.setProperty(beanObj, name, existBean);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("您的bean的屬性" + name
+ "沒有對(duì)應(yīng)的set方法");
}
}
}
}
return beanObj;
}
@Override
public Object getBean(String name) {
Object bean = context.get(name);
// 如果為空說明scope不是singleton,那么容器中是沒有的,這里現(xiàn)場(chǎng)創(chuàng)建
if (bean == null) {
bean = createBean(map.get(name));
}
return bean;
}
}
最后就是一個(gè)測(cè)試類TestBean:
package com.wang.main;
import org.junit.Test;
import com.wang.entity.Person;
import com.wang.entity.Student;
import com.wang.entity.Teacher;
public class TestBean {
@Test
public void func1(){
BeanFactory bf=new ClassPathXmlApplicationContext("/applicationContext.xml");
Person s=(Person)bf.getBean("person");
Person s1=(Person)bf.getBean("person");
System.out.println(s==s1);
System.out.println(s1);
Student stu1=(Student) bf.getBean("student");
Student stu2=(Student) bf.getBean("student");
String name=stu1.getName();
System.out.println(name);
System.out.println(stu1==stu2);
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
當(dāng)前名稱:Spring實(shí)現(xiàn)一個(gè)簡(jiǎn)單的SpringIOC容器
本文URL:http://chinadenli.net/article0/goecoo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營(yíng)銷、商城網(wǎng)站、微信小程序、云服務(wù)器、用戶體驗(yàn)、響應(yīng)式網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)