網(wǎng)站建設(shè)項(xiàng)目報價友情鏈接網(wǎng)站大全
繼續(xù)整理記錄這段時間來的收獲,詳細(xì)代碼可在我的Gitee倉庫Java設(shè)計模式克隆下載學(xué)習(xí)使用!
7.4 自定義Spring IOC
創(chuàng)建新模塊,結(jié)構(gòu)如圖![[Pasted image 20230210173222.png]]
7.4.1 定義bean相關(guān)POJO類
7.4.1.1 定義propertyValue類
/** * @Author:Phil * @ClassName: PropertyValue * @Description: * 用來封裝bean標(biāo)簽下的property標(biāo)簽屬性 * name屬性 * ref屬性 * value屬性:給基本數(shù)據(jù)類型及String類型賦值 * @Date 2023/2/8 21:45 * @Version: 1.0 **/public class propertyValue { private String name; private String ref; private String value; public propertyValue() { } public propertyValue(String name, String ref, String value) { this.name = name; this.ref = ref; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRef() { return ref; } public void setRef(String ref) { this.ref = ref; } public String getValue() { return value; } public void setValue(String value) { this.value = value; }
}
7.4.1.2 定義MultiplePropertyValue類
一個bean 標(biāo)簽可以有多個property子標(biāo)簽,故用multiplePropertyValue類來存儲PropertyValue對象
public class MultiplePropertyValues implements Iterable<PropertyValue>{
// 定義list集合對象,用來存儲PropertyValue對象 private final List<PropertyValue> propertyValueList; public MultiplePropertyValues() { this.propertyValueList = new ArrayList<PropertyValue> (); } public MultiplePropertyValues(List<PropertyValue> propertyValueList) { if(propertyValueList == null) this.propertyValueList = new ArrayList<PropertyValue>(); else this.propertyValueList = propertyValueList; }
// 獲取所有propertyValue對象,以數(shù)組形式返回 public PropertyValue[] getPropertyValues(){ return propertyValueList.toArray(new PropertyValue[0]); }
// 根據(jù)name屬性值返回對應(yīng)PropertyValue對象 public PropertyValue getPropertyValues(String propertyName){
// 遍歷集合返回 for (PropertyValue propertyValue : propertyValueList) { if(propertyValue.getName().equals(propertyName)) return propertyValue; } return null; }
// 判斷集合是否為空 public boolean isEmpty(){ return propertyValueList.isEmpty(); }
// 添加PropertyValue對象 public MultiplePropertyValues addPropertyValue(PropertyValue pv){
// 若有則進(jìn)行覆蓋 for (int i = 0; i < propertyValueList.size(); i++) { if(propertyValueList.get(i).getName().equals(pv.getName())){ propertyValueList.set(i,pv); return this;//目的是鏈?zhǔn)骄幊? } }
// 添加新的 this.propertyValueList.add(pv); return this; }
// 判斷是否有指定name的PropertyValue對象 public boolean contains(String propertyName){ return getPropertyValues(propertyName) != null; }
// 獲取迭代器對象 @Override public Iterator<PropertyValue> iterator() { return propertyValueList.iterator(); }
}
7.1.4.3 BeanDefinition類
BeanDefinition類用來封裝bean信息,主要包含id(bean 名稱),class(bean全類名)及子標(biāo)簽property對象數(shù)據(jù)
public class BeanDefinition { private String id; private String className; private MultiplePropertyValues multiplePropertyValues; public BeanDefinition() { multiplePropertyValues = new MultiplePropertyValues(); } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public MultiplePropertyValues getMultiplePropertyValues() { return multiplePropertyValues; } public void setMultiplePropertyValues(MultiplePropertyValues multiplePropertyValues) { this.multiplePropertyValues = multiplePropertyValues; }
}
7.4.2 定義注冊表類
7.4.2.1 定義BeanDefinitionRegistry接口
BeanDefinitionRegistry接口定義了注冊表相關(guān)操作,定義如下功能:
- 注冊BeanDefinition對象到注冊表中
- 根據(jù)名稱從注冊表中獲取后去BeanDefinition對象
- 從注冊表中刪除指定名稱的BeanDefinition對象
- 判斷注冊表中是否包含指定名稱的BeanDefinition對象
- 獲取注冊表中BeanDefinition對象個數(shù)
- 獲取注冊表中所有的Bean
public interface BeanDefinitionRegistry { //往注冊表中注冊bean void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) ; //從注冊表刪掉指定名稱bean void removeBeanDefinition(String beanName) throws Exception; //獲取指定名稱bean BeanDefinition getBeanDefinition(String beanName) throws Exception; //判斷是否包含指定名稱bean boolean containsBeanDefinition(String beanName); //獲取所有bean String[] getBeanDefinitionNames(); int getBeanDefinitionCount(); boolean isBeanNameInUse(String var1);
}
7.4.2.2 SimpleBeanDefinitionRegistry類
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry{
// 創(chuàng)建容器,用于存儲 Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String,BeanDefinition>(); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) { beanDefinitionMap.put(beanName,beanDefinition); } @Override public void removeBeanDefinition(String beanName) throws Exception { beanDefinitionMap.remove(beanName); } @Override public BeanDefinition getBeanDefinition(String beanName) throws Exception { return beanDefinitionMap.get(beanName); } @Override public boolean containsBeanDefinition(String beanName) { return beanDefinitionMap.containsKey(beanName); } @Override public String[] getBeanDefinitionNames() { return beanDefinitionMap.keySet().toArray(new String[0]); } @Override public int getBeanDefinitionCount() { return beanDefinitionMap.size(); } @Override public boolean isBeanNameInUse(String var1) { return beanDefinitionMap.containsKey(var1); }
}
7.4.3 定義解析器類
7.4.3.1 BeanDefinitionReader接口
BeanDefinitionReader用來解析配置文件并在注冊表中注冊bean的信息,定義了兩規(guī)范:
- 獲取注冊表功能,讓外界可通過該對象獲取注冊表對象
- 加載配置文件,并注冊bean數(shù)據(jù)
public interface BeanDefinitionReader{//獲取注冊表對象BeanDefinitionRegistry getRegistry();//加載配置文件斌在注冊表中進(jìn)行注冊void loadBeanDefinitions(String configuration);
}
7.4.3.2 XmlBeanDefinitionReader類
XmlBeanDefinitionReader類是專門來解析xml配置文件,實(shí)現(xiàn)了BeanDefinitionReader接口的兩個功能。
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
// 聲明注冊表對象 private BeanDefinitionRegistry registry; public XmlBeanDefinitionReader() { this.registry = new SimpleBeanDefinitionRegistry(); } @Override public BeanDefinitionRegistry getRegistry() { return registry; } @Override public void loadBeanDefinitions(String configuration) throws Exception{
// 使用dom4j進(jìn)行xml配置文件的解析 SAXReader saxReader = new SAXReader();
// 后去類路徑下的配置文件 InputStream resourceAsStream = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configuration); Document document = saxReader.read(resourceAsStream);
// 根據(jù)Document對象獲取根標(biāo)簽對象(beans) Element rootElement = document.getRootElement();
// 獲取根標(biāo)簽下所有的bean標(biāo)簽對象 List<Element> elements = rootElement.elements("bean");
// 遍歷集合 for (Element element : elements) {
// 獲取id屬性 String id = element.attributeValue("id");
// 獲取className String className = element.attributeValue("class");
// 將id和className封裝到BeanDefinition對象中
// 創(chuàng)建BeanDefinition對象 BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setId(id); beanDefinition.setClassName(className);
// 創(chuàng)建MultiplePropertyValue對象 MultiplePropertyValues multiplePropertyValues = new MultiplePropertyValues();
// 獲取bean標(biāo)簽下的所有property標(biāo)簽對象 List<Element> propertyElements = element.elements("property"); for (Element propertyElement : propertyElements) { String name = propertyElement.attributeValue("name"); String ref = propertyElement.attributeValue("ref"); String value = propertyElement.attributeValue("value"); PropertyValue propertyValue = new PropertyValue(name, ref, value); multiplePropertyValues.addPropertyValue(propertyValue); }
// 將multiplePropertyValues封裝到BeanDefinition中 beanDefinition.setMultiplePropertyValues(multiplePropertyValues);
// 將BeanDefinition注冊到注冊表中 registry.registerBeanDefinition(id,beanDefinition); } }
}
7.4.4 容器相關(guān)類
7.4.4.1 BeanFactory接口
該接口定義IOC容器的統(tǒng)一規(guī)范即獲取bean對象
public interface BeanFactory{//根據(jù)bean對象的名稱獲取bean對象Object getBean(String name) throws Exception;//根據(jù)bean對象的名稱獲取bean對象,并進(jìn)行類型轉(zhuǎn)換<T> T getBean(String name,Class<? extends T> clazz) throws Exception;
}
7.4.4.2 ApplicationContext接口
該接口的子實(shí)現(xiàn)類對bean 對象的創(chuàng)建都是非延時的,所以該接口定義refresh方法,主要有兩功能:
- 加載配置文件
- 根據(jù)注冊表中BeanDefinition對象封裝的數(shù)據(jù)進(jìn)行bean對象的創(chuàng)建
public interface ApplicationContext extends BeanFactory{ void refresh()throws Exception;
}
7.4.4.3 AbstractApplicationContext接口
- 作為ApplicationContext接口的子類,故該類是非延時加載,故需要在該類中定義Map集合,作為bean對象存儲容器
- 聲明BeanDefinition類型變量,用來進(jìn)行xml配置文件解析,符合單一職責(zé)原則
- BeanDefinition類型對象創(chuàng)建交由子類實(shí)現(xiàn),子類明確創(chuàng)建BeanDefinitionReader
public abstract class AbstractApplicationContext implements ApplicationContext {
// 聲明解析器對象 protected BeanDefinitionReader beanDefinitionReader;
// 存儲bean容器,key存儲的bean的id,value是bean對象 protected Map<String,Object> singletonObject = new HashMap<String,Object>();;
// 存儲配置文件路徑 String configLocation; public void refresh() throws Exception{
// 加載BeanDefinition beanDefinitionReader.loadBeanDefinitions(configLocation);
// 初始化bean finishBeanInitialization(); } public void finishBeanInitialization() throws Exception{
// 獲取注冊表對象 BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
// 獲取BeanDefinition對象 String [] beanNames = registry.getBeanDefinitionNames();
// 初始化bean for (String beanName : beanNames) { getBean(beanName); } }
}
7.4.4.4 ClassPathXmlApplicationContext接口
該類主要是加載類路徑下的配置文件,并進(jìn)行bean對象的創(chuàng)建,主要有以下功能:
- 在構(gòu)造方法中,創(chuàng)建BeanDefinitionReader對象
- 在構(gòu)造方法中,調(diào)用refresh方法,用于進(jìn)行配置文件加載,創(chuàng)建bean對象并存儲到容器中
- 重寫父類中的getBean方法,并實(shí)現(xiàn)依賴注入
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{ public ClassPathXmlApplicationContext(String configLocation){ this.configLocation = configLocation;
// 構(gòu)建解析器對象 beanDefinitionReader = new XmlBeanDefinitionReader(); try { this.refresh(); }catch (Exception exception){ exception.printStackTrace(); } }
// 根據(jù)bean對象的名稱獲取bean對象 @Override public Object getBean(String name) throws Exception {
// 判斷對象容器中是否包含指定bean對象,若包含則返回,否則創(chuàng)建 Object object = singletonObject.get(name); if(object != null) return object;
// 獲取BeanDefinition對象 BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); BeanDefinition beanDefinition = registry.getBeanDefinition(name);
// 獲取bean信息中的className String className = beanDefinition.getClassName();
// 通過反射獲取對象 Class<?> clazz = Class.forName(className); Object instance = clazz.newInstance();
// 進(jìn)行依賴注入操作 MultiplePropertyValues multiplePropertyValues = beanDefinition.getMultiplePropertyValues(); for (PropertyValue propertyValue : multiplePropertyValues) {
// 獲取name屬性值 String propertyValueName = propertyValue.getName();
// 獲取value值 String value = propertyValue.getValue();
// 獲取ref值 String ref = propertyValue.getRef(); if(ref != null && !"".equals(ref)){
// 獲取依賴的對象 Object bean = getBean(ref);
// 拼接方法名 String setterMethodByField = StringUtils.getSetterMethodByField(propertyValueName);
// 獲取所有方法 Method[] methods = clazz.getMethods(); for (Method method : methods) { if(method.getName().equals(setterMethodByField))
// 執(zhí)行setter方法 method.invoke(instance,bean); } } if(value != null && !"".equals(value)){
// 拼接方法名 String methodName = StringUtils.getSetterMethodByField(propertyValueName);
// 獲取method對象 Method method = clazz.getMethod(methodName, String.class); method.invoke(instance, value); } }
// 在返回instance對象之前,將該對象存儲到map容器中 singletonObject.put(name,instance); return instance; } @Override public <T> T getBean(String name, Class<? extends T> clazz) throws Exception { Object bean = getBean(name); if(bean == null) return null; return clazz.cast(bean); }
}
7.4.4.5 測試
將前文回顧Spring框架項(xiàng)目中的pom文件的spring-context依賴換為上述新建項(xiàng)目依賴,如圖
運(yùn)行后如圖
7.4.5 總結(jié)
7.4.5.1 使用到的設(shè)計模式
- 工廠模式:工廠模式+ 配置文件
- 單例模式。Spring IOC管理的bean都是單例的,此處單例不是通過構(gòu)造器進(jìn)行單例構(gòu)建,且框架對每個bean只創(chuàng)建一個對象。
- 模板方法模式。AbstractApplicationContext類中的finishInitialization方法調(diào)用getBean方法,因?yàn)間etBean實(shí)現(xiàn)和環(huán)境有關(guān)。
- 迭代器模式。其中MultiplePropertyValyes類使用了迭代器模式,因?yàn)榇祟惔鎯Σ⒐芾鞵ropertyValue對象,也屬于一個容器。
- 還使用了很多設(shè)計模式,如AOP使用到了代理模式,選擇JDK代理或CGLIB代理使用了策略模式,還有適配器模式,裝飾者模式,觀察者模式等。
7.4.5.2 符合大部分設(shè)計原則
7.4.5.3 整個設(shè)計和Spring設(shè)計還有一定出入
Spring框架底層是很復(fù)雜的,進(jìn)行了很深入的封裝,并對外提供了很好的擴(kuò)展性,自定義Spring IOC容器有兩目的:
- 了解Spring底層對對象的大體管理機(jī)制
- 了解設(shè)計模式在具體開發(fā)中的使用
- 以后學(xué)習(xí)Spring源碼,通過該案例實(shí)現(xiàn),可以降低Spring學(xué)習(xí)入門成本