手寫實現Spring IOC
阿新 • • 發佈:2019-02-03
Spring中有兩大核心內容:
一.IOC
二.AOP
今天我手寫實現了IOC,來總結一下IOC的原理和實現方式
首先IOC底層所用到的技術
1>xml配置檔案
2>dom4j解析xml
3>工廠設計模式
4>反射
5>內省
首先看一下IOC demo的流程圖
執行環境
1.jdk1.8,IntelliJ IDEA
ps(如果報錯報找不到xml檔案,請在target目錄下的classes中手動複製)
首先我們構建bean類:
package com.example.writeioc.config;
import java.util.ArrayList;
import java.util.List;
public class Bean {
private String name;
private String className;
private List<Property> properties = new ArrayList<Property>();
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;
}
@Override
public String toString(){
return "Bean[name = ]" + name + " , className = " + className + " , properties" + properties + " ]";
}
}
然後例項寫兩個bean類
package com.example.writeioc.bean;
public class A {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.example.writeioc.bean;
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
然後寫一個屬性類(Property):
package com.example.writeioc.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;
}
}
工廠介面:
package com.example.writeioc.main;
public interface BeanFactory {
//根據bean的name獲得bean物件
Object getBean(String beanName);
}
xml檔案
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean name="A" class="com.example.writeioc.bean.A">
<property name="name" value="Tom"></property>
</bean>
<bean name="B" class="com.example.writeioc.bean.B">
<property name ="a" ref="A"></property>
</bean>
</beans>
解析xml類(ConfigManager)
package com.example.writeioc.config;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ConfigManager {
//讀取配置檔案,並返回結果
public static Map<String,Bean> getConfig(String path){
Map<String,Bean> map = new HashMap<String,Bean>();
//1建立解析器
SAXReader saxReader = new SAXReader();
//2載入配置檔案
InputStream is = ConfigManager.class.getResourceAsStream(path);
Document document = null;
try{
document = saxReader.read(is);
}catch (DocumentException e){
e.printStackTrace();
throw new RuntimeException("請檢查xml配置");
}
//3定義xpath表示式去除所有bean元素
// //從任意節點選擇名稱為bean的節點 /從父節點選擇 不寫則是從當前節點選擇
String xpath = "//bean";
//4對bean元素進行遍歷
List<Element> list = document.selectNodes(xpath);
if(list != null){
for(Element beanFile:list){
Bean bean = new Bean();
//將class,name等屬性封裝到bean物件中
String name = beanFile.attributeValue("name");
String className = beanFile.attributeValue("class");
bean.setName(name);
bean.setClassName(className);
//獲得bean元素下所有property元素,並將其屬性封裝到property子元素中
List<Element> children = beanFile.elements("property");
if(children != null){
for(Element child : children){
Property property = new Property();
String pName = child.attributeValue("name");
String pValue = child.attributeValue("value");
String pRef = child.attributeValue("ref");
property.setName(pName);
property.setRef(pRef);
property.setValue(pValue);
//將property物件封裝到bean中
bean.getProperties().add(property);
}
}
//將bean物件封裝到map中用於返回
map.put(name,bean);
}
}
return map;
}
}
通過內省注入bean屬性類:
package com.example.writeioc.utils;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
/**
* @author Jet
*/
public class BeanUtils {
public static Method getWriteMethod(Object beanObj,String name){
//使用內省實現(基於java反射專門用於操作bean的屬性的api)
Method method = null;
try{
//1:分析bean物件-->BeanInfo
BeanInfo beanInfo = Introspector.getBeanInfo(beanObj.getClass());
//2:根據BeanInfo獲取所有屬性的描述器
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
//3遍歷描述器
if(pds != null){
for(PropertyDescriptor pd : pds){
//判斷當前屬性是否是我們要找的屬性
String pName = pd.getName();
if(pName.equals(name)){
method = pd.getWriteMethod();
}
}
}
//4返回找到的set方法
}catch (IntrospectionException e){
e.printStackTrace();
}
//如果沒有找到-->丟擲異常,提示使用者檢查是否建立對應的set方法
if(method == null){
throw new RuntimeException("請檢查 " + name + "屬性的set方法是否建立");
}
return method;
}
}
裝配初始化bean類(ClassPathXmlApplicationContext)
package com.example.writeioc.main;
import com.example.writeioc.config.Bean;
import com.example.writeioc.config.ConfigManager;
import com.example.writeioc.config.Property;
import com.example.writeioc.utils.BeanUtils;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class ClassPathXmlApplicationContext implements BeanFactory{
//配置資訊
private Map<String,Bean> config;
//用一個Map來做spring的容器,放置spring所管理的物件
private Map<String,Object> context = new HashMap<String,Object>();
//在classPathXmlApplicationContext已建立就初始化容器
@Override
//根據Bean名稱獲得bean例項
public Object getBean(String beanName){
Object bean = context.get(beanName);
return bean;
}
public ClassPathXmlApplicationContext(String path){
//1讀取配置檔案獲取初始化的bean的資訊
config = ConfigManager.getConfig(path);
//2遍歷配置,初始化bean
if(config != null){
for(Map.Entry<String,Bean> en : config.entrySet()){
//獲取配置中的bean資訊
String beanName = en.getKey();
Bean bean = en.getValue();
Object exsitBean = context.get(beanName);
//因為createBean方法也會向context中放置bean,我們在初始化的時候先要檢視是否已經存在bean
//如果不存在再建立bean
if(exsitBean == null){
//根據bean配置建立bean物件
Object beanObj = createBean(bean);
//3將初始化的bean放入容器
context.put(beanName,beanObj);
}
}
}
}
//根據bean配置建立bean物件
private Object createBean(Bean bean){
//1獲得要建立的bean的class
String className = bean.getClassName();
Class clazz = null;
try{
clazz = Class.forName(className);
}catch (ClassNotFoundException e){
e.printStackTrace();
throw new RuntimeException("請檢查bean的class配置 " + className);
}
//將class對應的物件創建出來
Object beanObj = null;
try{
beanObj = clazz.newInstance();
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("bean沒有空參構造" + className);
}
//2獲得bean的屬性,將其注入
if(bean.getProperties() != null){
for(Property property : bean.getProperties()){
//1:簡單value注入
//獲取要注入的屬性名稱
String name = property.getName();
//根據屬性名稱獲得注入屬性對應的set方法
Method setMethod = BeanUtils.getWriteMethod(beanObj,name);
//建立一個需要注入bean中的屬性值
Object parm = null;
if(property.getValue() != null){
//獲取要注入的屬性值
String value = property.getValue();
parm = value;
}
//2其他bean的注入
if(property.getRef() != null){
//先從容器中查詢當前要注入的bean是否已經建立並放入容器中
Object exsitBean = context.get(property.getRef());
if(exsitBean == null){
//如果容器中不存在,則要建立
exsitBean = createBean(config.get(property.getRef()));
//將建立好的bean放入容器
context.put(property.getRef(),exsitBean);
}
parm = exsitBean;
}
//呼叫set方法注入
try {
setMethod.invoke(beanObj,parm);
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("bean的屬性 " + parm + " 沒有對應的set方法,或者引數不正確" + className);
}
}
}
return beanObj;
}
}