手寫IOC容器
- Spring核心架構
- Bean概述
- Spring IOC相關介面分析
- BeanFactory介面
- BeanDefinition介面
- BeanDefinitionReader介面
- BeanDefinitionRegistry 介面
- SimpleBeanDefinitionRegistry---簡單的bean注冊中心
- DefaultListableBeanFactory探究
- 創建容器
- 手寫SpringIOC容器
- 定義Bean相關的Pojo類
- PropertyValue類
- MultablePropertyValues類
- BeanDenfinition類
- 定義注冊表相關類
- BeanDefinitionRegistry介面
- SimpleBeanDefinitionRegistry--注冊表介面的子實作類
- 定義決議器相關類
- BeanDefinitionReader介面
- XmlBeanDefinitionReader子實作類
- IOC容器相關類
- BeanFactory介面---延時加載
- 子介面ApplicationContext---非延時加載
- AbstractApplicationContext類
- ClassPathXmlApplicationContext類
- StringUtils--負責拼接字串,找到對應需要執行的set方法
- 測驗IOC
- 將上面寫的spring模塊,安裝到maven的本地倉庫中
- 新建專案,引入上面手寫的spring模塊
- 流程圖
- 原始碼地址
- 手寫IOC總結
- 手寫IOC使用的設計模式
- 符合大部分設計原則
- 整個設計和Spring的設計還是有一定的出入
Spring核心架構
Spring大約有20個模塊,由1300多個不同的檔案構成
這些模塊可以分為:
核心容器,AOP和設備支持,資料訪問和集成,Web組件,通信報文和集成測驗,下面是Spring框架的總體架構圖:

核心容器由beans,core,context和expression(Spring Expression Language,SPEL)4個模塊組成
要點一:
- spring-beans和spring-core模塊是Spring框架的核心模塊,包含了控制反轉(IOC)和依賴注入(DI).
- BeanFactory使用控制反轉對應用程式的配置和依賴性規范與實際的應用程式代碼進行了分離,
-
BeanFactory屬于延時加載,也就是說在實體化容器物件后并不會自動實體化Bean,只有當Bean被使用時,BeanFatory才會對該Bean進行實體化與依賴關系的裝配.
要點二:
- spring-context模塊架構與核心模塊之上,擴展了BeanFactory,為它添加了Bean生命周期控制,框架事件體系及資源加載透明化等功能,
- 此外,此模塊還提供了許多企業支持,如郵件訪問,遠程訪問,任務調度,
- ApplicationContext是該模塊的核心介面,它的超類是BeanFactory.
要點三;
- spring-context-support模塊是對Spring IOC容器及IOC子容器的擴展支持
要點四:
- spring-context-indexer模塊是Spring的類管理組件和Classpath掃描組件
要點五:
- spring-expression模塊是統一運算式語言EL的擴展模塊,可以查詢,管理運行中的物件,同時也可以方便地呼叫物件方法,以及操作陣列,集合等,
- 它的語法類似于傳統EL,但提供了額外的功能,最出色的要數函式呼叫和簡單的字串模板函式,
- EL的特性是基于Spring產品的需求而設計的,可以非常方便地同Spring IOC進行互動
Bean概述

Spring IOC相關介面分析
BeanFactory介面


這三個介面共同定義了Bean的集合,Bean之間的關系及Bean行為,
最基本的IOC容器介面是BeanFactory,來看一下它的原始碼
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
//根據Bean的名稱,獲取IOC容器中的Bean物件
Object getBean(String var1) throws BeansException;
//根據Bean的名稱,獲取IOC容器中的Bean物件,并指定獲取到的Bean物件的型別,這樣我們使用時,就不需要進行強制型別轉換
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> var1);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
//判斷容器中是否包含指定名稱的Bean
boolean containsBean(String var1);
//根據Bean的名稱判斷是否是單例
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
//是否是多實體Bean
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}
在BeanFactory里只對IOC容器的基本行為做了定義,根本不關心你的Bean是如何定義及加載的,
正如我們只關心能從工廠里得到什么產品,不關心工廠是怎么生產這些產品的,
BeanFactory有一個很重要的子介面,就是ApplicationContext介面,該介面主要來規范容器中的bean物件是非延時加載的,即在創建容器物件的時候就對Bean進行初始化,并存盤到一個容器中

要知道工廠是如何產生物件的,我們需要看具體的IOC容器實作,Spring提供了許多IOC容器實作,比如:
-
ClasspathXmlApplicationContext :根據類路徑加載xml組態檔,并創建IOC容器物件 -
FileSystemXmlApplicationContext:根據系統路徑加載xml組態檔,并創建IOC容器物件 -
AnnotationConfigApplicationContext:加載注解類配置,并創建IOC容器
BeanDefinition介面
Spring IOC容器管理我們定義的各種Bean物件及其相互關系,而Bean物件在Spring實作中是以BeanDefinition來描述的,如下面的組態檔
<bean id="userDao" class="com.dao.impl.UserDaoImpl"></bean>
bean標簽還有很多屬性: scope,init-method,destory-method等

BeanDefinitionReader介面


BeanDefinitionReader介面定義的功能:
public interface BeanDefinitionReader {
//獲取BeanDefinitionRegistry 注冊器物件
BeanDefinitionRegistry getRegistry();
@Nullable
ResourceLoader getResourceLoader();
@Nullable
ClassLoader getBeanClassLoader();
BeanNameGenerator getBeanNameGenerator();
//下面的loadBeanDefinitions都是從指定的資源中加載bean定義資訊
int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException;
int loadBeanDefinitions(Resource... var1) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String var1) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String... var1) throws BeanDefinitionStoreException;
}
BeanDefinitionRegistry 介面
BeanDefinitionReader用來決議bean定義,并封裝BeanDefinition物件,而我們定義的組態檔中定義了很多Bean標簽,所以就有一個問題,決議的BeanDefinition物件存盤到哪兒?
答案就是BeanDefinition的注冊中心,而該注冊中心頂層介面就是BeanDefinitionRegistry
public interface BeanDefinitionRegistry extends AliasRegistry {
//往注冊表中注冊bean,即bean定義加載后被封裝成的BeanDefinition物件
void registerBeanDefinition(String var1, BeanDefinition var2) throws BeanDefinitionStoreException;
//從注冊表洗掉指定名稱的bean
void removeBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//獲取注冊表中指定名稱的Bean
BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//判斷注冊表中是否已經注冊了指定名稱的bean,即BeanDefinition物件
boolean containsBeanDefinition(String var1);
//獲取注冊表中所有bean的名稱
String[] getBeanDefinitionNames();
//獲取注冊表中注冊的bean的個數
int getBeanDefinitionCount();
//判斷當前的bean名稱在注冊表中是否已經在使用了
boolean isBeanNameInUse(String var1);
}

SimpleBeanDefinitionRegistry—簡單的bean注冊中心
public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {
//重點是這個beanDefinitionMap ---作為容器存放beanDefinition物件
//將beanDefinition物件放入map集合的程序就稱為注冊bean
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(64);
public SimpleBeanDefinitionRegistry() {
}
//下面都是重寫父類的方法
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "'beanName' must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
this.beanDefinitionMap.put(beanName, beanDefinition);
}
....
}
DefaultListableBeanFactory探究
該類中也有一個屬性是用來注冊bean的
private final Map<String, BeanDefinition> beanDefinitionMap;
創建容器
ClassPathXmlApplicationContext對Bean配置資源的載入是從refresh()方法開始的,
refresh()方法是一個模板方法,規定了IOC容器的啟動流程,有些邏輯要交給器其子類實作,
他對Bean配置資源進行載入,ClassPathXmlApplicationContext通過呼叫父類AbstractApplicationContext的refresh()方法啟動整個IOC容器對Bean定義的載入程序.
手寫SpringIOC容器
現在要對下面的組態檔進行決議,并自定義Spring框架的IOC對涉及到的物件進行管理
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userService" class="com.pojo.UserService">
<property name="userDao" ref="userDao"></property>
<bean id="userDao" class="com.pojo.UserDao"></bean>
</beans>
定義Bean相關的Pojo類
PropertyValue類
用于封裝bean的屬性,體現到上面就是封裝bean標簽的子標簽的property標簽資料
每個PropertyValue實體物件,封裝一條property標簽里面的資料
//用來封裝bean標簽下的property標簽下的屬性
//name屬性,ref屬性:給參考型別賦值,value屬性:給基本資料型別及String型別屬性賦值
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PropertyValue
{
private String name;
private String ref;
private String value;
}
MultablePropertyValues類
一個bean標簽可以有多個property子標簽,所以再定義一個MultablePropertyValues類,用來存盤并管理多個PropertyValue物件
//用來存盤和管理多個PropertyValue物件
public class MultablePropertyValues implements
Iterable<PropertyValue> {
//定義list集合物件,用來存盤propertyvlaue物件
private final List<PropertyValue> propertyValueList;
//用final修飾的變數只能賦值一次,并且必須在構造方法結束前進行賦值
public MultablePropertyValues()
{
propertyValueList=new ArrayList<>();
}
public MultablePropertyValues(List<PropertyValue> valueList)
{
if(valueList==null)
{
propertyValueList=new ArrayList<>();
}
else
{
propertyValueList=new ArrayList<>();
}
}
//獲取所有PropertyValue物件,以陣列形式回傳
public PropertyValue[] getPropertyValues()
{
//將集合轉換為陣列并回傳即可
//指定回傳陣列的型別
return propertyValueList.toArray(new PropertyValue[0]);
}
//根據name屬性值獲取PropertyValue物件
public PropertyValue getPropertyValue(String propertyName)
{
//遍歷集合
for (PropertyValue propertyValue : propertyValueList) {
if(propertyValue.getName().equals(propertyName))
{
return propertyValue;
}
}
return null;
}
//判斷集合是否為空
public boolean isEmpty()
{
return propertyValueList.isEmpty();
}
//添加PropertyValue物件
public MultablePropertyValues addPropertyValue(PropertyValue pv)
{
//判斷傳遞進來的PropertyValue物件,是否和集合中已有的重復了,如果重復了,就記性覆寫操作
for(int i=0;i<propertyValueList.size();i++)
{
PropertyValue propertyValue = propertyValueList.get(i);
if(propertyValue.getName().equals(pv.getName()))
{
//進行覆寫操作
propertyValueList.set(i,propertyValue);
return this;//實作鏈式編程
}
}
//沒有就直接添加
propertyValueList.add(pv);
return this;
}
//判斷是否有指定name屬性值的物件
public boolean contains(String propertyName)
{
return getPropertyValue(propertyName)!=null;
}
//獲取迭代器物件
@Override
public Iterator<PropertyValue> iterator()
{
//呼叫list集合里面獲取迭代器的方法
return propertyValueList.iterator();
}
}
BeanDenfinition類
BeanDenfinition用來封裝bean資訊的,主要包含id(即物件的名稱),class(需要交由spring管理類的全類名)及子標簽property資料
//用來封裝bean標簽資料
//id屬性,class屬性,property子標簽資料
@Data
public class BeanDefinition
{
private String id;
private String className;
private MultablePropertyValues propertyValues;
//利用無參構造對PropertyValues里面的list集合進行初始化
public BeanDefinition()
{
propertyValues=new MultablePropertyValues();
}
}
定義注冊表相關類
BeanDefinitionRegistry介面
BeanDefinitionRegistry介面定義了注冊表相關操作,定義了如下功能:
- 注冊BeanDefinition到注冊表中
- 從注冊表中洗掉指定名稱的BeanDefinition物件
- 根據名稱從注冊表中獲取BeanDefinition物件
- 判斷注冊表中是否含有指定名稱的BeanDefinition物件
- 獲取注冊表中BeanDefinition物件的個數
- 獲取注冊表中所有BeanDefinition物件的名稱
public interface BeanDefinitionRegistry
{
//往注冊表中注冊bean,即bean定義加載后被封裝成的BeanDefinition物件
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
//從注冊表洗掉指定名稱的bean
void removeBeanDefinition(String beanName);
//獲取注冊表中指定名稱的Bean
BeanDefinition getBeanDefinition(String beanName);
//判斷注冊表中是否已經注冊了指定名稱的bean,即BeanDefinition物件
boolean containsBeanDefinition(String beanName);
//獲取注冊表中所有bean的名稱
String[] getBeanDefinitionNames();
//獲取注冊表中注冊的bean的個數
int getBeanDefinitionCount();
//判斷當前的bean名稱在注冊表中是否已經在使用了
boolean isBeanNameInUse(String beanName);
}
SimpleBeanDefinitionRegistry–注冊表介面的子實作類
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry{
//定義一個容器,用來存盤BeanDefinition物件
private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();
//往注冊表中注冊bean,即bean定義加載后被封裝成的BeanDefinition物件
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName,beanDefinition);
}
//從注冊表洗掉指定名稱的bean
@Override
public void removeBeanDefinition(String beanName) {
beanDefinitionMap.remove(beanName);
}
//獲取注冊表中指定名稱的Bean
@Override
public BeanDefinition getBeanDefinition(String beanName) {
return beanDefinitionMap.get(beanName);
}
//判斷注冊表中是否已經注冊了指定名稱的bean,即BeanDefinition物件
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
//獲取注冊表中所有bean的名稱
@Override
public String[] getBeanDefinitionNames()
{
//獲取key的set集合,再將set集合,轉換為陣列,陣列的型別為string
return beanDefinitionMap.keySet().toArray(new String[0]);
}
//獲取注冊表中注冊的bean的個數
@Override
public int getBeanDefinitionCount() {
return beanDefinitionMap.size();
}
//判斷當前的bean名稱在注冊表中是否已經在使用了
@Override
public boolean isBeanNameInUse(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
}
定義決議器相關類
BeanDefinitionReader介面
BeanDefinitionReader是用來決議組態檔并在注冊表中注冊bean資訊,定義了兩個規范:
- 獲取注冊表的功能,讓外界可以通過該物件獲取注冊表物件
- 加載組態檔,并注冊bean資料
//用來決議組態檔的,該介面只是定義了規范
public interface BeanDefinitionReader
{
//獲取注冊表物件
BeanDefinitionRegistry getRegistry();
//加載組態檔,并在注冊表中進行注冊
void loadBeanDefinitions(String configLocation) throws Exception;
}
XmlBeanDefinitionReader子實作類
XmlBeanDefinitionReader類專門用來決議xml組態檔的,該類實作BeanDefinitionReader介面,并實作介面中的兩個功能
dom4j的依賴
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
需要進行決議的xml格式,參考下面:

具體代碼實作:
//針對xml組態檔進行決議的類
public class XmlBeanDefinitionReader implements BeanDefinitionReader{
//宣告注冊表物件
private BeanDefinitionRegistry registry;
//建構式里面進行賦值
public XmlBeanDefinitionReader()
{
registry=new SimpleBeanDefinitionRegistry();
}
@Override
public BeanDefinitionRegistry getRegistry()
{
return registry;
}
//只實作類路徑下的組態檔加載
@Override
public void loadBeanDefinitions(String configLocation) throws Exception {
//使用dom4j進行xml組態檔的決議
SAXReader reader=new SAXReader();
//獲取類路徑下的組態檔
InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation);
//傳入位元組輸入流,獲取檔案物件
Document document = reader.read(is);
//根據Document物件獲取根標簽物件(beans)
Element rootElement=document.getRootElement();
//獲取根標簽下所有的bean標簽物件
List<Element> beanElements = rootElement.elements("bean");
//遍歷集合--獲取每個bean標簽上的屬性值
for (Element beanElement : beanElements) {
//獲取id屬性
String id = beanElement.attributeValue("id");
//獲取class屬性
String className = beanElement.attributeValue("class");
//將id屬性和class屬性封裝到BeanDefinition物件中
//1.創建BeanDefinition物件
BeanDefinition beanDefinition=new BeanDefinition();
beanDefinition.setId(id);
beanDefinition.setClassName(className);
//創建MultablePropertyValues物件
MultablePropertyValues multablePropertyValues=new MultablePropertyValues();
//獲取bean標簽下所有的property標簽物件
List<Element> propertyElements = beanElement.elements("property");
for (Element propertyElement : propertyElements) {
//獲取name屬性
String name = propertyElement.attributeValue("name");
//獲取ref屬性
String ref = propertyElement.attributeValue("ref");
//獲取value屬性
String value = propertyElement.attributeValue("value");
//使用PropertyValue物件封裝上述屬性
PropertyValue propertyValue=new PropertyValue(name,ref,value);
//添加到管理PropertyValue物件的容器中
multablePropertyValues.addPropertyValue(propertyValue);
}
//將multablePropertyValues物件封裝到BeanDefinition物件中
beanDefinition.setPropertyValues(multablePropertyValues);
//將beanDefinition物件注冊到注冊表中
//xml檔案中配置的id做為管理BeanDefinition物件的容器中的名字
registry.registerBeanDefinition(id,beanDefinition);
}
}
}
IOC容器相關類
BeanFactory介面—延時加載
在該介面中定義IOC容器統一規范獲取bean物件
//IOC容器父介面
public interface BeanFactory
{
//根據bean物件的名稱獲取bean物件
Object getBean(String name)throws Exception;
//根據bean物件的名稱獲取bean物件,并進行型別轉換
//方法設定為泛型方法,并且第二個引數為泛型引數,型別必須是T型別的或者其子類
<T> T getBean(String name,Class<? extends T> clazz)throws Exception;
}
子介面ApplicationContext—非延時加載
該介面的所有子實作類的bean物件的創建都是非延時的,所以在該介面中定義refresh()方法,該方法主要完成以下兩個功能:
- 加載組態檔
- 根據注冊表中的
BeanDefinition物件封裝的資料進行bean物件的創建
//定義非延時加載功能---繼承BeanFactory介面
public interface ApplicationContext extends BeanFactory
{
void refresh()throws Exception;
}
AbstractApplicationContext類
- 作為ApplicationContext介面的子類,所以該類也是非延時加載,所以需要在該類中頂一個Map集合,作為bean物件存盤的容器
- 宣告BeanDefinitionReader型別的變數,用來進行xml組態檔的決議,符合單一職責原則
- BeanDefinitionReader型別的物件的創建交由子類實作,因為只有子類明確到底創建BeanDefinitionReader的那個子實作類物件
//ApplicationContext介面的子實作類,用于立即加載
public abstract class AbstractApplicationContext implements ApplicationContext {
//宣告決議器變數,具體是xml決議還是什么決議,由子類決定
protected BeanDefinitionReader beanDefinitionReader;
//定義用于存盤bean物件的map容器
protected Map<String,Object> signletonObjects=new HashMap<>();
//宣告組態檔路徑的變數
protected String configLocation;
//重寫父類refresh方法
@Override
public void refresh() throws Exception {
//加載BeanDefinition物件
beanDefinitionReader.loadBeanDefinitions(configLocation);
//初始化bean
finishBeanInialLization();
}
//bean的初始化
private void finishBeanInialLization()throws Exception{
//獲取注冊表物件
BeanDefinitionRegistry registry=beanDefinitionReader.getRegistry();
//獲取BeanDefinition物件
String[] beanNames = registry.getBeanDefinitionNames();
//進行bean的初始化方法
//將注冊表中已有的BeanDefinition物件,全部通過反射創建物件后,放入IOC容器中
for (String beanName : beanNames) {
//呼叫父類的getBean方法,獲取對應的bean物件
//getBean方法的具體實作,有對應的子類完成
//getBean對應的子類實作方式,會有區別,例如ClassPathXmlApplicationContext重寫getBean方法
//里面邏輯主要是讀取xml,而如果是其他方式創建子類,那么getBean重寫的邏輯也會有區別
getBean(beanName);
}
}
}
ClassPathXmlApplicationContext類
該類主要是加載類路徑下的組態檔,并進行bean物件的創建,主要完成以下功能:
- 在構造方法中,創建BeanDefinition物件
- 在構造方法中,呼叫refresh()方法,用于進行組態檔的加載,創建bean物件并存盤到容器中
- 重新父介面中的getBean()方法,并實作依賴注入的操作
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{
public ClassPathXmlApplicationContext(String configLocation)
{
//組態檔的路徑
this.configLocation=configLocation;
//創建決議器物件
beanDefinitionReader=new XmlBeanDefinitionReader();
//呼叫父類中的refresh方法
try {
refresh();
} catch (Exception e) {
e.printStackTrace();
}
}
//根據bean物件的名稱獲取bean物件
@Override
public Object getBean(String name) throws Exception {
//判斷物件容器中是否包含指定名稱的bean物件,如果包含,直接回傳即可
//如果不包含,需要自行創建
//嘗試從容器中獲取指定bean物件
Object o = signletonObjects.get(name);
//容器中存在該物件
if(o!=null)
{
return o;
}
//獲取BeanDefinition物件
//1.先獲取注冊表物件
BeanDefinitionRegistry beanDefinitionRegistry=beanDefinitionReader.getRegistry();
//2.從注冊表中獲取對應的BeanDefinition物件
BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(name);
//如果注冊表中也沒有對應的BeanDefinition物件,那么拋出例外
if(beanDefinition==null)
return null;
//3.獲取bean資訊中的className:物件的全類名
String className = beanDefinition.getClassName();
//4.通過反射創建物件
Class<?> clazz = Class.forName(className);
//獲得了實體化完后的物件
Object beanObj=clazz.newInstance();
//進行依賴注入操作---創建完物件后的屬性賦值
//先獲取所有property物件--每一個里面property物件封裝了物件的一個屬性的名字和對應的值
MultablePropertyValues propertyValues = beanDefinition.getPropertyValues();
//獲取迭代器,遍歷存放property物件的list集合
Iterator<PropertyValue> iterator = propertyValues.iterator();
//使用迭代器遍歷
while(iterator.hasNext())
{
//獲取name屬性值
PropertyValue next = iterator.next();
String propertyName = next.getName();
//獲取value屬性
String propertyValue = next.getValue();
//獲取ref屬性
String propertyRef = next.getRef();
//ref和value只能出現一個
if(propertyRef!=null&&!"".equals(propertyRef))
{
//ref屬性存在
//獲取依賴的bean物件---使用遞回
Object bean = getBean(propertyRef);
//拼接方法名---傳入屬性名,獲取其對應的set方法的名字
String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
//獲取所有的方法物件
Method[] methods=clazz.getMethods();
//找到我們需要執行的物件屬性的set方法
for (Method method : methods) {
if(methodName.equals(method.getName()))
{
//執行該setter方法
//這里是對依賴物件進行屬性注入
//例如:
// class Person
// {
// Stu stu
// }
method.invoke(beanObj,bean);
}
}
}
//value屬性存在
if(propertyValue!=null&&!"".equals(propertyValue))
{
//拼接方法名--傳入屬性名,獲取對應的set方法
String methodName1= StringUtils.getSetterMethodByFieldName(propertyName);
//獲取method物件,方法名稱,和引數型別,這里引數我們針對的是String型別進行操作
Method method = clazz.getMethod(methodName1, String.class);
//執行哪個物件的方法,實參的值
method.invoke(beanObj,propertyValue);
}
}
//回傳beanObj物件之前,將該物件存盤到map容器中
signletonObjects.put(name,beanObj);
return beanObj;
}
//根據bean物件的名稱獲取bean物件,并進行強制型別轉換
@Override
public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
//首先呼叫上面的getBean方法得到一個bean物件
Object bean = getBean(name);
//如果為null,說明沒有這個物件
if(bean==null)
return null;
//進行型別強轉,并回傳強轉后的物件
return clazz.cast(bean);
}
}
StringUtils–負責拼接字串,找到對應需要執行的set方法
//工具類,負責拼接字串
public class StringUtils
{
//構造器私有,不能創建實體化物件
private StringUtils(){}
//靜態方法---欄位名前面拼接set
//userDao ---> setUserDao
//屬性名第一個字母大寫,前面拼上set
public static String getSetterMethodByFieldName(String fieldName)
{
String methodName="set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
return methodName;
}
}
測驗IOC
將上面寫的spring模塊,安裝到maven的本地倉庫中

新建專案,引入上面手寫的spring模塊
目前寫的IOC只能做到對String型別的欄位完成依賴注入
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<bean id="animal" class="com.dao.Animal">
<property name="dog" ref="dog">
</property>
<property name="type" value="狗">
</property>
</bean>
<bean id="dog" class="com.dao.Dog">
<property name="name" value="湯姆">
</property>
<property name="age" value="3">
</property>
</bean>
</beans>
測驗
public class main
{
public static void main(String[] args) throws Exception {
//加載組態檔,啟動IOC容器
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
//根據名字獲取指定bean物件
Dog dog = (Dog)applicationContext.getBean("dog");
System.out.println(dog);
//根據名字和型別獲取
Animal animal = applicationContext.getBean("animal", Animal.class);
System.out.println(animal);
//如果傳入的名字,沒有找到對應的bean物件
Object hhhh = applicationContext.getBean("hhhh");
System.out.println(hhhh);
}
}

流程圖




原始碼地址
碼云倉庫:
碼云地址
后續可能也會繼續補充
手寫IOC總結
手寫IOC使用的設計模式
- 工廠模式: 這個使用工廠模式+組態檔的方式
- 單例模式: spring ioc 管理的bean物件都是單例的,此處的單例不是通過構造器進行單例的控制的,而是spring框架對每一個bean只創建一個物件
- 模板方法模式: AbstractApplicationContext類中的finishBeanInialLization()方法呼叫了子類的getBean()方法,因為getBean的實作和環境息息相關
- 迭代器模式: 對于MultablePropertyValues類定義使用到了迭代器模式,因為此類中存盤并管理PropertyValue物件,也屬于一個容器,所以給該容器提供一個遍歷方式
Spring框架其實使用到了很多設計模式,入AOP使用到了代理模式,選擇JDK代理或者CGLIB代理使用到了策略模式,還有配接器模式,裝飾者模式,觀察者模式等
符合大部分設計原則
整個設計和Spring的設計還是有一定的出入
Spring框架底層是很復雜的,進行了很深入的封裝,并對外提供了很好的擴展性,而我們自定義SpringIOC有一下幾個目的:
- 了解Spring底層對物件的大體管理機制
- 了解設計模式在具體開發中的使用
- 以后學習Spring原始碼,通過該案例的實作,可以降低學習成本
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/301140.html
標籤:其他
