IOC:Inversion of Control控制反轉,也稱依賴倒置(反轉)java
問題:如何理解控制反轉?git
反轉:依賴對象的得到被反轉了。由本身建立,反轉爲從IOC容器中獲取(和自動注入)github
1)代碼更簡潔,不須要去new須要使用的對象了。spring
2)面向接口編程,使用者與具體類解耦,易擴展、替換實現者。apache
3)能夠方便進行AOP加強。進行AOP的前提是有IOC編程
IOC容器的工做:負責建立、管理類實例,向使用者提供實例。數組
是的。IOC容器負責來建立類實例對象,使用者須要實例就從IOC容器中get。也稱IOC容器爲bean工廠。緩存
問:bean是什麼?安全
bean是組件,就是類對象!ide
1)IOC容器應該具有什麼行爲(對外接口)?
對外提供bean實例,getBean()方法
2) 這個getBean()方法是否須要參數?須要幾個參數、什麼類型的參數?
簡單工廠模式中,當工廠能建立不少類產品時,如要建立某類產品,須要告訴工廠。
3)這個getBean()方法的返回值應是什麼類型?
各類類型的bean,那就只能是Object了。
通過上面的問題Bean工廠的接口就能夠定義出來了!!!
Bean工廠代碼:
package com.study.spring.beans; /** * * @Description: IOC容器(bean工廠)接口:負責建立bean實例 * @author leeSmall * @date 2018年11月29日 * */ public interface BeanFactory { /** * 獲取bean * * @param name bean的名字 * * @return bean 實例 * @throws Exception */ Object getBean(String name) throws Exception; }
Bean工廠怎麼知道該如何建立bean?
如何告訴Bean工廠?
就是一個定義註冊,咱們能夠給它定義一個bean定義註冊接口
Bean定義註冊接口代碼:
package com.study.spring.beans; /** * * @Description: bean定義BeanDefinition定義好了就須要告訴IOC容器(bean工廠): * 經過bean定義註冊接口BeanDefinitionRegistry把bean定義BeanDefinition註冊到IOC容器(bean工廠)BeanFactory * @author leeSmall * @date 2018年11月29日 * */ public interface BeanDefinitionRegistry { //註冊bean定義 void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException; //獲取bean定義 BeanDefinition getBeanDefinition(String beanName); //判斷是否包含bean定義 boolean containsBeanDefinition(String beanName); }
問題1:bean定義的用途是什麼?
告訴Bean工廠該如何建立某類bean
問題2:得到類的實例的方式有哪些?
new 構造方法
Person p = new Person();
工廠方法
靜態的
public class PersonFactory { public static Person getPerson() { return new Person(); } }
成員方法
public class PersonFactory { public Person getPerson() { return new Person(); } }
問題3:Bean工廠幫咱們建立bean時,它須要獲知哪些信息?
1)new 構造方法的方式建立bean時,Bean工廠須要知道要建立的類的類名
2)靜態工廠方法的方式建立bean時,Bean工廠須要知道工廠類名、工廠方法名
3)成員工廠方法的方式建立bean時,Bean工廠須要知道工廠類名(工廠bean名)、工廠方法名
由於須要獲取工廠類對象去調用工廠方法名建立bean,因此直接給工廠bean名先建立工廠bean對象
問題4:每次從Bean工廠獲取bean實例時,是否都須要建立一個新的?
否,有的只須要單實例
問題5:Bean定義是給Bean工廠建立bean用的,那麼Bean定義接口應向Bean工廠提供哪些方法?
new 構造方法的方式建立bean時,須要告訴bean工廠怎麼獲取類的名稱——獲取bean的類名:getBeanClass (): Class
靜態工廠方法的方式建立bean時,須要告訴bean工廠怎麼獲取工廠方法名:getFactoryMethodName() : String
成員工廠方法的方式建立bean時,須要告訴bean工廠怎麼獲取工廠bean名:getFactoryBeanName() : String
是不是單例等方法:getScope() : Sting、isSingleton()、isPrototype()
問題6:類對象交給IOC容器來管理,類對象的生命週期中還可能有什麼生命階段事情要作嗎?
好比建立對象後可能須要進行一些初始化
還有一些對象在銷燬時可能要進行一些特定的銷燬邏輯(如釋放資源)
那就在Bean定義中提供讓用戶可指定初始化、銷燬方法。
對Bean工廠就需提供getInitMethodName()、getDestroyMethodName()
bean定義接口BeanDefinition代碼:
package com.study.spring.beans; import org.apache.commons.lang3.StringUtils; /** * * @Description: bean定義接口:要IOC容器(bean工廠)建立bean實例,就得告訴IOC容器(bean工廠)須要建立什麼樣的bean-BeanDefinition * @author leeSmall * @date 2018年11月29日 * */ public interface BeanDefinition { String SCOPE_SINGLETION = "singleton"; String SCOPE_PROTOTYPE = "prototype"; /** * 類:new 構造方法的方式建立bean時,須要告訴bean工廠怎麼獲取類的名稱 */ Class<?> getBeanClass(); /** * Scope */ String getScope(); /** * 是否單例 */ boolean isSingleton(); /** * 是否原型 */ boolean isPrototype(); /** * 工廠bean名:成員工廠方法的方式建立bean時,須要告訴bean工廠怎麼獲取工廠bean名 */ String getFactoryBeanName(); /** * 工廠方法名:靜態工廠方法的方式建立bean時,須要告訴bean工廠怎麼獲取工廠方法名 */ String getFactoryMethodName(); /** * 初始化方法 */ String getInitMethodName(); /** * 銷燬方法 */ String getDestroyMethodName(); /** * 校驗bean定義的合法性 */ default boolean validate() { // 沒定義class,工廠bean或工廠方法沒指定,則不合法。 if (this.getBeanClass() == null) { if (StringUtils.isBlank(getFactoryBeanName()) || StringUtils.isBlank(getFactoryMethodName())) { return false; } } // 定義了類,又定義工廠bean,不合法 if (this.getBeanClass() != null && StringUtils.isNotBlank(getFactoryBeanName())) { return false; } return true; } }
實現bean定義接口BeanDefinition實現一個通用的bean定義GenericBeanDefinition
package com.study.spring.beans; import org.apache.commons.lang3.StringUtils; /** * * @Description: bean定義有了就須要實現bean定義接口BeanDefinition實現一個通用的bean * 定義GenericBeanDefinition,用來承載數據 * @author leeSmall * @date 2018年11月29日 * */ public class GenericBeanDefinition implements BeanDefinition { //bean的名稱 private Class<?> beanClass; //scope 默認單例 private String scope = BeanDefinition.SCOPE_SINGLETION; //工廠bean名 private String factoryBeanName; //工廠方法名 private String factoryMethodName; //初始化方法 private String initMethodName; //銷燬方法 private String destroyMethodName; //設置bean的名稱 public void setBeanClass(Class<?> beanClass) { this.beanClass = beanClass; } //設置scope public void setScope(String scope) { if (StringUtils.isNotBlank(scope)) { this.scope = scope; } } //設置工廠bean名 public void setFactoryBeanName(String factoryBeanName) { this.factoryBeanName = factoryBeanName; } //設置工廠方法名 public void setFactoryMethodName(String factoryMethodName) { this.factoryMethodName = factoryMethodName; } //設置初始化方法 public void setInitMethodName(String initMethodName) { this.initMethodName = initMethodName; } //設置銷燬方法 public void setDestroyMethodName(String destroyMethodName) { this.destroyMethodName = destroyMethodName; } @Override public Class<?> getBeanClass() { return this.beanClass; } @Override public String getScope() { return this.scope; } @Override public boolean isSingleton() { return BeanDefinition.SCOPE_SINGLETION.equals(this.scope); } @Override public boolean isPrototype() { return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope); } @Override public String getFactoryBeanName() { return this.factoryBeanName; } @Override public String getFactoryMethodName() { return this.factoryMethodName; } @Override public String getInitMethodName() { return this.initMethodName; } @Override public String getDestroyMethodName() { return this.destroyMethodName; } @Override public String toString() { return "GenericBeanDefinition [beanClass=" + beanClass + ", scope=" + scope + ", factoryBeanName=" + factoryBeanName + ", factoryMethodName=" + factoryMethodName + ", initMethodName=" + initMethodName + ", destroyMethodName=" + destroyMethodName + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((beanClass == null) ? 0 : beanClass.hashCode()); result = prime * result + ((destroyMethodName == null) ? 0 : destroyMethodName.hashCode()); result = prime * result + ((factoryBeanName == null) ? 0 : factoryBeanName.hashCode()); result = prime * result + ((factoryMethodName == null) ? 0 : factoryMethodName.hashCode()); result = prime * result + ((initMethodName == null) ? 0 : initMethodName.hashCode()); result = prime * result + ((scope == null) ? 0 : scope.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GenericBeanDefinition other = (GenericBeanDefinition) obj; if (beanClass == null) { if (other.beanClass != null) return false; } else if (!beanClass.equals(other.beanClass)) return false; if (destroyMethodName == null) { if (other.destroyMethodName != null) return false; } else if (!destroyMethodName.equals(other.destroyMethodName)) return false; if (factoryBeanName == null) { if (other.factoryBeanName != null) return false; } else if (!factoryBeanName.equals(other.factoryBeanName)) return false; if (factoryMethodName == null) { if (other.factoryMethodName != null) return false; } else if (!factoryMethodName.equals(other.factoryMethodName)) return false; if (initMethodName == null) { if (other.initMethodName != null) return false; } else if (!initMethodName.equals(other.initMethodName)) return false; if (scope == null) { if (other.scope != null) return false; } else if (!scope.equals(other.scope)) return false; return true; } }
咱們繼續看下面的圖:
說明:bean定義BeanDefinition經過bean定義註冊接口BeanDefinitionRegistry註冊到Bean工廠BeanFactory,Bean工廠BeanFactory負責建立bean
實現一個最基礎的默認bean工廠:DefaultBeanFactory
說明:
6.1 實現bean定義信息註冊接口
問題1:bean定義信息如何存放?
Map
問題2:bean定義是否能夠重名?重名怎麼辦?
重名拋異常
6.2 實現bean工廠
問題1:建立的bean用什麼存放,方便下次獲取?
Map,由於getBean是經過名字來取的,放在Map中更好
問題2:在getBean方法中要作哪些事?
建立bean實例,進行初始化
6.3 Bean工廠實現代碼:
package com.study.spring.beans; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * @Description: bean定義BeanDefinition有了,bean定義註冊BeanDefinitionRegistry也有了, * 就能夠實現一個默認的bean工廠DefaultBeanFactory來建立bean實例了,DefaultBeanFactory除了須要實現BeanFactory外, * 還須要實現Bean定義註冊接口BeanDefinitionRegistry,由於要把bean定義註冊到bean工廠裏面 * @author leeSmall * @date 2018年11月29日 * */ public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable { private final Log logger = LogFactory.getLog(getClass()); //用Map來存放bean定義信息 private Map<String, BeanDefinition> beanDefintionMap = new ConcurrentHashMap<>(256); //用Map來存放建立的bean實例,注意這裏只是存放單例bean,多實例每次都要建立新的,不須要存放 private Map<String, Object> beanMap = new ConcurrentHashMap<>(256); //註冊bean定義 @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException { //判斷給入的beanName和beanDefinition不能爲空 Objects.requireNonNull(beanName, "註冊bean須要給入beanName"); Objects.requireNonNull(beanDefinition, "註冊bean須要給入beanDefinition"); // 校驗給入的bean是否合法 if (!beanDefinition.validate()) { throw new BeanDefinitionRegistException("名字爲[" + beanName + "] 的bean定義不合法:" + beanDefinition); } //若是已存在bean定義就拋異常 if (this.containsBeanDefinition(beanName)) { throw new BeanDefinitionRegistException( "名字爲[" + beanName + "] 的bean定義已存在:" + this.getBeanDefinition(beanName)); } //把bean定義放到Map裏面 this.beanDefintionMap.put(beanName, beanDefinition); } //根據bean的名字從Map裏面獲取bean定義 @Override public BeanDefinition getBeanDefinition(String beanName) { return this.beanDefintionMap.get(beanName); } //根據bean的名字判斷Map裏面是否包含bean定義 @Override public boolean containsBeanDefinition(String beanName) { return this.beanDefintionMap.containsKey(beanName); } //根據bean的名字獲取bean實例,裏面主要作的工做是建立bean實例和對bean實例進行初始化 @Override public Object getBean(String name) throws Exception { return this.doGetBean(name); } //根據bean的名字獲取bean實例,裏面主要作的工做是建立bean實例和對bean實例進行初始化 protected Object doGetBean(String beanName) throws Exception { //判斷給入的bean名字不能爲空 Objects.requireNonNull(beanName, "beanName不能爲空"); //先從beanMap裏面獲取bean實例 Object instance = beanMap.get(beanName); //若是beanMap裏面已存在bean實例就直接返回,不須要走後面的流程了 if (instance != null) { return instance; } //從beanDefintionMap裏面獲取bean定義信息 BeanDefinition bd = this.getBeanDefinition(beanName); //bean定義信息不能爲空 Objects.requireNonNull(bd, "beanDefinition不能爲空"); //獲取bean的類型 Class<?> type = bd.getBeanClass(); if (type != null) { //若是bean的類型不爲空,而且工廠方法名爲空,說明是使用構造方法的方式來建立bean實例 if (StringUtils.isBlank(bd.getFactoryMethodName())) { // 構造方法來構造對象 instance = this.createInstanceByConstructor(bd); } //若是bean的類型不爲空,而且工廠方法名不爲空,說明是使用靜態工廠方法的方式來建立bean實例 else { // 靜態工廠方法 instance = this.createInstanceByStaticFactoryMethod(bd); } } //若是bean的類型爲空,說明是使用工廠bean的方式來建立bean實例 else { // 工廠bean方式來構造對象 instance = this.createInstanceByFactoryBean(bd); } // 執行初始化方法 this.doInit(bd, instance); //存放單例的bean到beanMap if (bd.isSingleton()) { beanMap.put(beanName, instance); } return instance; } // 構造方法來構造對象 反射 private Object createInstanceByConstructor(BeanDefinition bd) throws InstantiationException, IllegalAccessException { try { //拿到bean的類型,而後調用newInstance經過反射來建立bean實例 return bd.getBeanClass().newInstance(); } catch (SecurityException e1) { logger.error("建立bean的實例異常,beanDefinition:" + bd, e1); throw e1; } } // 靜態工廠方法 反射 private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception { //拿到bean的類型 Class<?> type = bd.getBeanClass(); //經過靜態工廠方法方法的名字getFactoryMethodName反射出bean的方法m建立bean實例 Method m = type.getMethod(bd.getFactoryMethodName(), null); return m.invoke(type, null); } // 工廠bean方式來構造對象 反射 private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception { //經過bean定義信息中工廠bean的名字獲取工廠bean的實例 Object factoryBean = this.doGetBean(bd.getFactoryBeanName()); //經過bean定義信息中工廠方法的名字反射出工廠bean的方法m建立bean實例 Method m = factoryBean.getClass().getMethod(bd.getFactoryMethodName(), null); return m.invoke(factoryBean, null); } //執行初始化方法 private void doInit(BeanDefinition bd, Object instance) throws Exception { // 獲取bean定義中的初始化方法,若是存在初始化方法就經過反射去執行初始化方法 if (StringUtils.isNotBlank(bd.getInitMethodName())) { Method m = instance.getClass().getMethod(bd.getInitMethodName(), null); m.invoke(instance, null); } } //銷燬 @Override public void close() throws IOException { // 執行單例實例的銷燬方法 for (Entry<String, BeanDefinition> e : this.beanDefintionMap.entrySet()) { String beanName = e.getKey(); BeanDefinition bd = e.getValue(); //獲取bean定義中的銷燬方法,若是存在銷燬方法就經過反射去執行銷燬方法 if (bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())) { Object instance = this.beanMap.get(beanName); try { Method m = instance.getClass().getMethod(bd.getDestroyMethodName(), null); m.invoke(instance, null); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) { logger.error("執行bean[" + beanName + "] " + bd + " 的 銷燬方法異常!", e1); } } } } }
6.4 擴展DefaultBeanFactory
對於單例bean,咱們能夠提早實例化,這樣作的好處是不用在須要的時候再取獲取了,能夠保證線程安全,提升性能
package com.study.spring.beans; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * @Description: 提早實例化單例bean * @author leeSmall * @date 2018年11月27日 * */ public class PreBuildBeanFactory extends DefaultBeanFactory { private final Log logger = LogFactory.getLog(getClass()); private List<String> beanNames = new ArrayList<>(); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException { super.registerBeanDefinition(beanName, beanDefinition); synchronized (beanNames) { beanNames.add(beanName); } } public void preInstantiateSingletons() throws Exception { synchronized (beanNames) { for (String name : beanNames) { BeanDefinition bd = this.getBeanDefinition(name); if (bd.isSingleton()) { this.doGetBean(name); if (logger.isDebugEnabled()) { logger.debug("preInstantiate: name=" + name + " " + bd); } } } } } }
6.5 測試IOC容器(bean工廠)建立bean實例
須要建立的bean實例ABean
package com.study.spring.samples; /** * * @Description: 須要建立的bean實例ABean * @author leeSmall * @date 2018年11月29日 * */ public class ABean { public void doSomthing() { System.out.println(System.currentTimeMillis() + " " + this); } public void init() { System.out.println("ABean.init() 執行了"); } public void destroy() { System.out.println("ABean.destroy() 執行了"); } }
建立bean實例ABean的工廠ABeanFactory
package com.study.spring.samples; /** * * @Description: 建立bean實例ABean的工廠ABeanFactory * @author leeSmall * @date 2018年11月29日 * */ public class ABeanFactory { //靜態工廠方式 public static ABean getABean() { return new ABean(); } //工廠bean的方式 public ABean getABean2() { return new ABean(); } }
測試IOC容器(bean工廠)建立bean實例ABean
package v1; import org.junit.AfterClass; import org.junit.Test; import com.dn.spring.beans.BeanDefinition; import com.dn.spring.beans.DefaultBeanFactory; import com.dn.spring.beans.GenericBeanDefinition; import com.dn.spring.samples.ABean; import com.dn.spring.samples.ABeanFactory; /** * * @Description: 測試IOC容器(bean工廠)建立bean實例ABean * @author leeSmall * @date 2018年11月29日 * */ public class DefaultBeanFactoryTest { static DefaultBeanFactory bf = new DefaultBeanFactory(); //測試構造方法方式建立bean實例 @Test public void testRegist() throws Exception { //建立bean定義 GenericBeanDefinition bd = new GenericBeanDefinition(); //設置bean的類名 bd.setBeanClass(ABean.class); //設置是否單例 bd.setScope(BeanDefinition.SCOPE_SINGLETION); // bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); //設置bean的初始化方法 bd.setInitMethodName("init"); //設置bean的銷燬方法 bd.setDestroyMethodName("destroy"); //把bean定義註冊到bean工廠DefaultBeanFactory bf bf.registerBeanDefinition("aBean", bd); } //靜態工廠方法的方式建立bean實例 @Test public void testRegistStaticFactoryMethod() throws Exception { //建立bean定義 GenericBeanDefinition bd = new GenericBeanDefinition(); //設置工廠bean的名字 bd.setBeanClass(ABeanFactory.class); //設置工廠方法名 bd.setFactoryMethodName("getABean"); //把bean定義註冊到bean工廠DefaultBeanFactory bf bf.registerBeanDefinition("staticAbean", bd); } //工廠bean的方式建立bean實例 @Test public void testRegistFactoryMethod() throws Exception { //建立工廠bean定義 GenericBeanDefinition bd = new GenericBeanDefinition(); //設置工廠bean的名字 bd.setBeanClass(ABeanFactory.class); String fbname = "factory"; //把工廠bean註冊到bean工廠DefaultBeanFactory bf bf.registerBeanDefinition(fbname, bd); //建立bean定義 bd = new GenericBeanDefinition(); //設置工廠bean的名字 bd.setFactoryBeanName(fbname); //設置工廠bean的方法名 bd.setFactoryMethodName("getABean2"); //設置是不是單列 bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); //把bean定義註冊到bean工廠DefaultBeanFactory bf bf.registerBeanDefinition("factoryAbean", bd); } //獲取bean實例並調用裏面的方法 @AfterClass public static void testGetBean() throws Exception { System.out.println("構造方法方式------------"); for (int i = 0; i < 3; i++) { ABean ab = (ABean) bf.getBean("aBean"); ab.doSomthing(); } System.out.println("靜態工廠方法方式------------"); for (int i = 0; i < 3; i++) { ABean ab = (ABean) bf.getBean("staticAbean"); ab.doSomthing(); } System.out.println("工廠方法方式------------"); for (int i = 0; i < 3; i++) { ABean ab = (ABean) bf.getBean("factoryAbean"); ab.doSomthing(); } //銷燬 bf.close(); } }
輸出結果:
構造方法方式------------ ABean.init() 執行了 1543478493599 com.dn.spring.samples.ABean@46fbb2c1 1543478493599 com.dn.spring.samples.ABean@46fbb2c1 1543478493599 com.dn.spring.samples.ABean@46fbb2c1 靜態工廠方法方式------------ 1543478493599 com.dn.spring.samples.ABean@5ef04b5 1543478493599 com.dn.spring.samples.ABean@5ef04b5 1543478493599 com.dn.spring.samples.ABean@5ef04b5 工廠方法方式------------ 1543478493599 com.dn.spring.samples.ABean@5f4da5c3 1543478493600 com.dn.spring.samples.ABean@443b7951 1543478493600 com.dn.spring.samples.ABean@14514713 ABean.destroy() 執行了
DI(Dependency Injection)依賴注入分析
問題1:哪些地方會有依賴?
構造參數依賴
屬性依賴
問題2:依賴注入的本質是什麼?
賦值,給入構造參數值,給屬性賦值
問題3:參數值、屬性值多是什麼值?
直接值、bean依賴
舉例:
public class Girl{ public Girl(String name,int age,char cup,Boy boyfriend){ } }
name,age,cup都是直接值,boyfriend是bean依賴
問題4:直接值會有哪幾種狀況?
基本數據類型、String
數組、集合
Properties
Map
本質:參數值、屬性值都是值。bean工廠在進行依賴注入時,就是給入值。
public class Girl{ public Girl(String name,int age,char cup,Boy boyfriend){ } }
問題1:咱們要建立一個Girl是如何建立的?
Boy leeSmall = new Boy("leeSmall"); Girl girl = new Girl("小青",18,'D',leeSmall);
直接把值傳入構造函數便可
問題2:咱們可不能夠這樣來定義構造參數依賴?
第一個參數值是:"小青"
第二個參數值是:18
第三個參數值是:'D'
第四個參數值是:依賴一個Boy Bean
徹底能夠!
問題1:參數能夠有多個,用什麼存儲?
集合:List
問題2:參數有順序,如何體現順序?
按參數順序放入List
問題3:參數能夠值直接值,也能夠是bean依賴,如何表示?
由於能夠有多種值,那就只能用Object
List<Object> constructorArgumentValues
問題4:若是用Object來表示值,如何區分是Bean依賴?
爲Bean依賴定義一種數據類型BeanReference,bean工廠在構造Bean實例時,遍歷判斷參數是不是BeanReference,若是是則替換爲依賴的bean實例。
問題5:若是直接值是數組、集合等,它們的元素中有的是bean依賴,怎麼處理?
元素值仍是用BeanReference,一樣bean工廠在使用時需遍歷替換。
BeanReference就是用來講明bean依賴的:依賴哪一個Bean
package com.dn.spring.beans; /** * 用於依賴注入中描述bean依賴 */ public class BeanReference { private String beanName; public BeanReference(String beanName) { super(); this.beanName = beanName; } /** * 得到引用的beanName */ public String getBeanName() { return this.beanName; } }
1.3 在BeanDefinition中增長得到構造參數值的方法
List<?> getConstructorArgumentValues();
在GenericBeanDefinition中增長對應的實現
//構造參數存放屬性 private List<?> constructorArgumentValues; //得到構造參數值 public List<?> getConstructorArgumentValues() { return constructorArgumentValues; } //設置構造參數值 public void setConstructorArgumentValues(List<?> constructorArgumentValues) { this.constructorArgumentValues = constructorArgumentValues; }
構造參數依賴有了,下面就能夠來實現構造參數依賴注入了!
代碼實現:
//把bean定義中的構造參數引用轉爲真實的值 private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception { return this.getRealValues(bd.getConstructorArgumentValues()); } //把bean定義中的構造參數引用轉爲真實的值 private Object[] getRealValues(List<?> defs) throws Exception { if (CollectionUtils.isEmpty(defs)) { return null; } Object[] values = new Object[defs.size()]; int i = 0; Object v = null; for (Object rv : defs) { if (rv == null) { v = null; } else if (rv instanceof BeanReference) { v = this.doGetBean(((BeanReference) rv).getBeanName()); } else if (rv instanceof Object[]) { // TODO 處理集合中的bean引用 } else if (rv instanceof Collection) { // TODO 處理集合中的bean引用 } else if (rv instanceof Properties) { // TODO 處理properties中的bean引用 } else if (rv instanceof Map) { // TODO 處理Map中的bean引用 } else { v = rv; } values[i++] = v; } return values; }
問題:有參數了,如何判定是哪一個構造方法、哪一個工廠方法?須要考慮下面的狀況
a)方法是能夠重載的
b)形參定義時多是接口或者父類,實參則是具體的子實現
c)能夠經過反射獲取提供的構造方法、方法,以下:
java.lang.Class
反射獲取構造方法:
//獲取全部構造方法 @CallerSensitive public Constructor<?>[] getConstructors() throws SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return copyConstructors(privateGetDeclaredConstructors(true)); } //獲取指定構造方法 @CallerSensitive public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return getConstructor0(parameterTypes, Member.PUBLIC); }
反射獲取方法:
//獲取全部方法 @CallerSensitive public Method[] getMethods() throws SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return copyMethods(privateGetPublicMethods()); } //獲取指定方法 @CallerSensitive public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); Method method = getMethod0(name, parameterTypes, true); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); } return method; }
判斷邏輯:
a)先根據參數的類型進行精確匹配查找,如未找到,則進行第二步查找;
b)得到全部的構造方法遍歷,經過參數數量過濾,再比對形參類型與實參類型
也就是說,對於原型Bean,咱們能夠緩存下這個構造方法或工廠方法。如何實現?
在BeanDefinition中增長緩存的方法:
/* 下面的四個方法是供beanFactory中使用的 */ //獲取構造方法 public Constructor<?> getConstructor(); //緩存構造方法 public void setConstructor(Constructor<?> constructor); //獲取工廠方法 public Method getFactoryMethod(); //緩存工廠方法 public void setFactoryMethod(Method factoryMethod);
在GenericBeanDefinition中增長對應的實現:
/* 下面的四個方法是供beanFactory中使用的 */ //獲取構造方法 public Constructor<?> getConstructor() { return constructor; } //緩存構造方法 public void setConstructor(Constructor<?> constructor) { this.constructor = constructor; } //獲取工廠方法 public Method getFactoryMethod() { return factoryMethod; } //緩存工廠方法 public void setFactoryMethod(Method factoryMethod) { this.factoryMethod = factoryMethod; }
1)在DefaultBeanFactory中增長查找構造方法的方法
//查找構造方法的方法 private Constructor<?> determineConstructor(BeanDefinition bd, Object[] args) throws Exception { Constructor<?> ct = null; //若是參數爲空,則返回無參的構造方法 if (args == null) { return bd.getBeanClass().getConstructor(null); } // 對於原型bean,從第二次開始獲取bean實例時,可直接得到第一次緩存的構造方法。 ct = bd.getConstructor(); if (ct != null) { return ct; } // 先根據參數類型獲取精確匹配的構造方法 Class<?>[] paramTypes = new Class[args.length]; int j = 0; for (Object p : args) { paramTypes[j++] = p.getClass(); } try { ct = bd.getBeanClass().getConstructor(paramTypes); } catch (Exception e) { // 這個異常不須要處理 } //若是根據參數類型進行精確批次查找沒有找到構造方法,就得到全部的構造方法遍歷,經過參數數量過濾,再比對形參類型與實參類型 if (ct == null) { // 沒有精確參數類型匹配的,則遍歷匹配全部的構造方法 // 判斷邏輯:先判斷參數數量,再依次比對形參類型與實參類型 outer: for (Constructor<?> ct0 : bd.getBeanClass().getConstructors()) { Class<?>[] paramterTypes = ct0.getParameterTypes(); if (paramterTypes.length == args.length) { for (int i = 0; i < paramterTypes.length; i++) { //isAssignableFrom方法表示是否能夠把args[i].getClass()賦值給paramterTypes[i] if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) { continue outer; } } ct = ct0; break outer; } } } //若是找到構造方法了,而且是原型的就緩存起來 if (ct != null) { // 對於原型bean,能夠緩存找到的構造方法,方便下次構造實例對象。在BeanDefinfition中獲取設置所用構造方法的方法。 // 同時在上面增長從beanDefinition中獲取的邏輯。 if (bd.isPrototype()) { bd.setConstructor(ct); } return ct; } else { throw new Exception("不存在對應的構造方法!" + bd); } }
2)修改DefaultBeanFactory中用構造方法建立實例的代碼調用determineConstructor
// 構造方法來構造對象 private Object createInstanceByConstructor(BeanDefinition bd) throws Exception { try { //獲取bean定義中的構造參數 Object[] args = this.getConstructorArgumentValues(bd); //若是構造參數爲空就使用無參構造函數 if (args == null) { return bd.getBeanClass().newInstance(); } //查找有參構造函數並返回 else { // 決定構造方法 return this.determineConstructor(bd, args).newInstance(args); } } catch (SecurityException e1) { logger.error("建立bean的實例異常,beanDefinition:" + bd, e1); throw e1; } }
3)按照增長查找構造方法的方式修改靜態工廠方法、工廠方法方式的參數依賴
增長查找工廠方法的方法
//查找工廠方法的方法 private Method determineFactoryMethod(BeanDefinition bd, Object[] args, Class<?> type) throws Exception { if (type == null) { type = bd.getBeanClass(); } //獲取bean定義中國工廠方法的名字 String methodName = bd.getFactoryMethodName(); //若是參數爲空就返回無參的方法 if (args == null) { return type.getMethod(methodName, null); } Method m = null; // 對於原型bean,從第二次開始獲取bean實例時,可直接得到第一次緩存的構造方法。 m = bd.getFactoryMethod(); if (m != null) { return m; } // 先根據參數類型獲取精確匹配的方法 Class[] paramTypes = new Class[args.length]; int j = 0; for (Object p : args) { paramTypes[j++] = p.getClass(); } try { m = type.getMethod(methodName, paramTypes); } catch (Exception e) { // 這個異常不須要處理 } //若是根據參數類型進行精確批次查找沒有找到工廠方法,就得到全部的構造方法遍歷,經過參數數量過濾,再比對形參類型與實參類型 if (m == null) { // 沒有精確參數類型匹配的,則遍歷匹配全部的方法 // 判斷邏輯:先判斷參數數量,再依次比對形參類型與實參類型 outer: for (Method m0 : type.getMethods()) { if (!m0.getName().equals(methodName)) { continue; } Class<?>[] paramterTypes = m.getParameterTypes(); if (paramterTypes.length == args.length) { for (int i = 0; i < paramterTypes.length; i++) { //isAssignableFrom方法表示是否能夠把args[i].getClass()賦值給paramterTypes[i] if (!paramterTypes[i].isAssignableFrom(args[i].getClass())) { continue outer; } } m = m0; break outer; } } } //若是找到構造方法了,而且是原型的就緩存起來 if (m != null) { // 對於原型bean,能夠緩存找到的方法,方便下次構造實例對象。在BeanDefinfition中獲取設置所用方法的方法。 // 同時在上面增長從beanDefinition中獲取的邏輯。 if (bd.isPrototype()) { bd.setFactoryMethod(m); } return m; } else { throw new Exception("不存在對應的構造方法!" + bd); } }
修改DefaultBeanFactory中用工廠方法建立實例的代碼調用determineFactoryMethod
// 靜態工廠方法 private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception { Class<?> type = bd.getBeanClass(); Object[] realArgs = this.getRealValues(bd.getConstructorArgumentValues()); Method m = this.determineFactoryMethod(bd, realArgs, null); return m.invoke(type, realArgs); } // 工廠bean方式來構造對象 private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception { Object factoryBean = this.doGetBean(bd.getFactoryBeanName()); Object[] realArgs = this.getRealValues(bd.getConstructorArgumentValues()); Method m = this.determineFactoryMethod(bd, realArgs, factoryBean.getClass()); return m.invoke(factoryBean, realArgs); }
參數依賴注入測試
package v2; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.dn.spring.beans.BeanReference; import com.dn.spring.beans.GenericBeanDefinition; import com.dn.spring.beans.PreBuildBeanFactory; import com.dn.spring.samples.ABean; import com.dn.spring.samples.ABeanFactory; import com.dn.spring.samples.CBean; import com.dn.spring.samples.CCBean; /** * * @Description: 參數依賴注入測試 * @author leeSmall * @date 2018年12月1日 * */ public class DItest { static PreBuildBeanFactory bf = new PreBuildBeanFactory(); //構造函數參數依賴注入 @Test public void testConstructorDI() throws Exception { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(ABean.class); List<Object> args = new ArrayList<>(); args.add("abean01"); args.add(new BeanReference("cbean")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("abean", bd); bd = new GenericBeanDefinition(); bd.setBeanClass(CBean.class); args = new ArrayList<>(); args.add("cbean01"); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("cbean", bd); bf.preInstantiateSingletons(); ABean abean = (ABean) bf.getBean("abean"); abean.doSomthing(); } //靜態工廠方法參數依賴注入 @Test public void testStaticFactoryMethodDI() throws Exception { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(ABeanFactory.class); bd.setFactoryMethodName("getABean"); List<Object> args = new ArrayList<>(); args.add("abean02"); args.add(new BeanReference("cbean02")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("abean02", bd); bd = new GenericBeanDefinition(); bd.setBeanClass(CBean.class); args = new ArrayList<>(); args.add("cbean02"); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("cbean02", bd); bf.preInstantiateSingletons(); ABean abean = (ABean) bf.getBean("abean02"); abean.doSomthing(); } //普通工廠方法的參數依賴注入 @Test public void testFactoryMethodDI() throws Exception { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setFactoryBeanName("abeanFactory"); bd.setFactoryMethodName("getABean2"); List<Object> args = new ArrayList<>(); args.add("abean03"); args.add(new BeanReference("cbean02")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("abean03", bd); bd = new GenericBeanDefinition(); bd.setBeanClass(ABeanFactory.class); bf.registerBeanDefinition("abeanFactory", bd); bf.preInstantiateSingletons(); ABean abean = (ABean) bf.getBean("abean03"); abean.doSomthing(); } }
輸出結果:
調用了含有CBean參數的構造方法 1543644406604 abean01 cb.name=cbean01 調用了含有CBean參數的構造方法 1543644406607 abean02 cb.name=cbean02 調用了含有CBean參數的構造方法 1543644406608 abean03 cb.name=cbean02
問題:構造對象時能夠循環依賴嗎/
構造實例對象時的循環依賴,會陷入僵死局面,是不容許構造實例時的循環依賴的。那麼怎麼發現循環依賴呢?
發現循環依賴的方法:加入一個正在構造的bean的記錄,每一個bean開始構造時加入該記錄中,構造完成後從記錄中移除。若是有依賴,先看依賴的bean是否在構造中,如是就構成了循環依賴,拋出異常
代碼實現:
在DefaultBeanFactory增長以下代碼:
//記錄正在構造的bean private ThreadLocal<Set<String>> buildingBeans = new ThreadLocal<>(); // 記錄正在建立的Bean Set<String> ingBeans = this.buildingBeans.get(); if (ingBeans == null) { ingBeans = new HashSet<>(); this.buildingBeans.set(ingBeans); } // 檢測循環依賴 由於若是已bean正在構造,這時發現有其餘的bean依賴它,此時它又沒有建立很久須要拋異常了 if (ingBeans.contains(beanName)) { throw new Exception(beanName + " 循環依賴!" + ingBeans); } // 記錄正在建立的Bean ingBeans.add(beanName); ..................... ...................... // 建立好實例後,移除建立中記錄 ingBeans.remove(beanName);
循環依賴測試代碼
package v2; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.dn.spring.beans.BeanReference; import com.dn.spring.beans.GenericBeanDefinition; import com.dn.spring.beans.PreBuildBeanFactory; import com.dn.spring.samples.DBean; import com.dn.spring.samples.EBean; /** * * @Description: 循環依賴測試 * @author leeSmall * @date 2018年12月1日 * */ public class CirculationDiTest { static PreBuildBeanFactory bf = new PreBuildBeanFactory(); //DBean依賴EBean,EBean又依賴DBean 拋異常 @Test public void testCirculationDI() throws Exception { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(DBean.class); List<Object> args = new ArrayList<>(); args.add(new BeanReference("ebean")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("dbean", bd); bd = new GenericBeanDefinition(); bd.setBeanClass(EBean.class); args = new ArrayList<>(); args.add(new BeanReference("dbean")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("ebean", bd); bf.preInstantiateSingletons(); } }
問題1:屬性依賴是什麼?
某個屬性依賴某個值,須要外部給入這個值
問題2:該如何來描述一個屬性依賴?
屬性名、值,定義一個來來表示這兩個值
問題3:會有多個屬性依賴,怎麼存放?
List存放
問題4:屬性值得狀況和構造參數值得狀況同樣嗎?
同樣
屬性依賴實體類類圖以下:
屬性依賴實體類代碼:
package com.study.spring.beans; /** * * @Description: 屬性值依賴實體 * @author leeSmall * @date 2018年12月1日 * */ public class PropertyValue { private String name; private Object value; public PropertyValue(String name, Object value) { super(); this.name = name; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } }
/** * 得到屬性依賴定義的方法 * */ List<PropertyValue> getPropertyValues();
//存放屬性依賴 private List<PropertyValue> propertyValues; //獲取屬性依賴 public List<PropertyValue> getPropertyValues() { return propertyValues; } //設置屬性依賴 public void setPropertyValues(List<PropertyValue> propertyValues) { this.propertyValues = propertyValues; }
//屬性依賴實現 private void setPropertyDIValues(BeanDefinition bd, Object instance) throws Exception { //bean定義中沒有屬性依賴就直接返回 if (CollectionUtils.isEmpty(bd.getPropertyValues())) { return; } //若是bean定義中有屬性依賴就設置值,設置方式和構造參數的設置同樣 for (PropertyValue pv : bd.getPropertyValues()) { //屬性依賴沒有給名字就直接跳過 if (StringUtils.isBlank(pv.getName())) { continue; } //獲取類對象 Class<?> clazz = instance.getClass(); //經過屬性名獲取類對象裏面聲明的字段 Field p = clazz.getDeclaredField(pv.getName()); //設置字段可訪問 p.setAccessible(true); //把屬性值轉化爲真正的值,和構造參數同樣 Object rv = pv.getValue(); Object v = null; if (rv == null) { v = null; } else if (rv instanceof BeanReference) { v = this.doGetBean(((BeanReference) rv).getBeanName()); } else if (rv instanceof Object[]) { // TODO 處理集合中的bean引用 } else if (rv instanceof Collection) { // TODO 處理集合中的bean引用 } else if (rv instanceof Properties) { // TODO 處理properties中的bean引用 } else if (rv instanceof Map) { // TODO 處理Map中的bean引用 } else { v = rv; } //把真正的值v設置到屬性p裏面 即屬性p依賴的值v p.set(instance, v); } }
在doGetBean(String beanName)中增長對設置屬性依賴的調用
// 給入屬性依賴 this.setPropertyDIValues(bd, instance);
注意:屬性依賴是容許循環依賴的,由於是在實例建立好以後才設置屬性依賴的值的!!!
package v2; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.dn.spring.beans.BeanReference; import com.dn.spring.beans.GenericBeanDefinition; import com.dn.spring.beans.PreBuildBeanFactory; import com.dn.spring.beans.PropertyValue; import com.dn.spring.samples.ABean; import com.dn.spring.samples.CBean; import com.dn.spring.samples.FBean; /** * * @Description: 屬性依賴測試 * @author leeSmall * @date 2018年12月1日 * */ public class PropertyDItest { static PreBuildBeanFactory bf = new PreBuildBeanFactory(); @Test public void testPropertyDI() throws Exception { //構造參數依賴 GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(ABean.class); List<Object> args = new ArrayList<>(); args.add("abean01"); args.add(new BeanReference("cbean")); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("abean", bd); //構造參數依賴 bd = new GenericBeanDefinition(); bd.setBeanClass(CBean.class); args = new ArrayList<>(); args.add("cbean01"); bd.setConstructorArgumentValues(args); bf.registerBeanDefinition("cbean", bd); //屬性依賴 bd = new GenericBeanDefinition(); bd.setBeanClass(FBean.class); List<PropertyValue> propertyValues = new ArrayList<>(); propertyValues.add(new PropertyValue("name", "FFBean01")); propertyValues.add(new PropertyValue("age", 18)); propertyValues.add(new PropertyValue("aBean", new BeanReference("abean"))); bd.setPropertyValues(propertyValues); bf.registerBeanDefinition("fbean", bd); bf.preInstantiateSingletons(); FBean fbean = (FBean) bf.getBean("fbean"); System.out.println("設置的屬性依賴值: " + fbean.getName() + " " + fbean.getAge()); fbean.getaBean().doSomthing(); } }
輸出結果:
調用了含有CBean參數的構造方法
設置的屬性依賴值: FFBean01 18
1543649718902 abean01 cb.name=cbean01
完整代碼獲取地址:
Spring IOC分析和手寫實現Spring IOC代碼:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v1
Spring DI分析和手寫實現Spring DI代碼:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v2