手写简版spring --3--对象实例化策略
一、目標(biāo)
這一章節(jié)的目標(biāo)主要是為了解決上一章節(jié)我們埋下的坑,那是什么坑呢?其實(shí)就是一個(gè)關(guān)于 Bean 對(duì)象在含有構(gòu)造函數(shù)進(jìn)行實(shí)例化的坑。在上一章節(jié)我們擴(kuò)充了 Bean 容器的功能,把實(shí)例化對(duì)象交給容器來統(tǒng)一處理,但在我們實(shí)例化對(duì)象的代碼里并沒有考慮對(duì)象類是否含構(gòu)造函數(shù),也就是說如果我們?nèi)?shí)例化一個(gè)含有構(gòu)造函數(shù)的對(duì)象那么就要拋異常了。怎么驗(yàn)證?其實(shí)就是把 UserService 添加一個(gè)含入?yún)⑿畔⒌臉?gòu)造函數(shù)就可以。
- 驗(yàn)證
發(fā)生這一現(xiàn)象的主要原因就是因?yàn)?beanDefinition.getBeanClass().newInstance(); 實(shí)例化方式并沒有考慮構(gòu)造函數(shù)的入?yún)?#xff0c;所以就這個(gè)坑就在這等著你了!那么我們的目標(biāo)就很明顯了,來把這個(gè)坑填平!
二、設(shè)計(jì)
填平這個(gè)坑的技術(shù)設(shè)計(jì)主要考慮兩部分
- 一個(gè)是串流程從哪合理的把構(gòu)造函數(shù)的入?yún)⑿畔鬟f到實(shí)例化操作里
- 另外一個(gè)是怎么去實(shí)例化含有構(gòu)造函數(shù)的對(duì)象。
- 參考 Spring Bean 容器源碼的實(shí)現(xiàn)方式,在 BeanFactory 中添加 Object getBean(String name, Object... args) 接口,這樣就可以在獲取 Bean 時(shí)把構(gòu)造函數(shù)的入?yún)⑿畔鬟f進(jìn)去了。
- 另外一個(gè)核心的內(nèi)容是使用什么方式來創(chuàng)建含有構(gòu)造函數(shù)的 Bean 對(duì)象呢?這里有兩種方式可以選擇,一個(gè)是基于 Java 本身自帶的方法DeclaredConstructor,另外一個(gè)是使用 Cglib 來動(dòng)態(tài)創(chuàng)建 Bean 對(duì)象。 Cglib 是基于字節(jié)碼框架 ASM 實(shí)現(xiàn),所以你也可以直接通過 ASM 操作指令碼來 創(chuàng)建對(duì)象
關(guān)于代理的文章
三、實(shí)現(xiàn)
相比于第二章的類依賴圖,主要變化:
- 在現(xiàn)有工程中添加 InstantiationStrategy 實(shí)例化策略接口
- 以及在BeanFactory接口中補(bǔ)充相應(yīng)的 getBean 入?yún)⑿畔?#xff0c;讓外部調(diào)用時(shí)可以傳遞構(gòu)造函數(shù)的入?yún)⒉㈨樌麑?shí)例化。
- BeanFactory
- 定義實(shí)例化策略接口
- 實(shí)例化(使用上面提到的兩種方式)
JDK方式:
public class SimpleInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {Class clazz = beanDefinition.getBeanClass();try {if (null != ctor) {return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);} else {return clazz.getDeclaredConstructor().newInstance();}} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);}} }- 首先通過 beanDefinition 獲取 Class 信息,這個(gè) Class 信息是在 Bean 定義的時(shí)候傳遞進(jìn)去的。
- 接下來判斷 ctor 是否為空,如果為空則是無構(gòu)造函數(shù)實(shí)例化,否則就是需要有構(gòu)造函數(shù)的實(shí)例化。
- 這里我們重點(diǎn)關(guān)注有構(gòu)造函數(shù)的實(shí)例化,實(shí)例化方式為clazz.getDeclaredConstructor(ctor.getParameterTypes()).ne wInstance(args);把入?yún)⑿畔鬟f給 newInstance 進(jìn)行實(shí)例化。
CGLIB方式:
public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(beanDefinition.getBeanClass());enhancer.setCallback(new NoOp() {@Overridepublic int hashCode() {return super.hashCode();}});if (null == ctor) return enhancer.create();return enhancer.create(ctor.getParameterTypes(), args);} }- 首先在 AbstractAutowireCapableBeanFactory 抽象類中定義了一個(gè)創(chuàng)建對(duì)象的實(shí)例化策略屬性類 InstantiationStrategy instantiationStrategy,這里我們選擇了 Cglib 的實(shí)現(xiàn)類。
- 接下來抽取 createBeanInstance 方法,在這個(gè)方法中需要注意 Constructor代表了你有多少個(gè)構(gòu)造函數(shù),通過 beanClass.getDeclaredConstructors() 方式可以獲取到你所有的構(gòu)造函數(shù),是一個(gè)集合。
- 接下來就需要循環(huán)比對(duì)出構(gòu)造函數(shù)集合與入?yún)⑿畔?args 的匹配情況,這里我們對(duì)比的方式比較簡(jiǎn)單,只是一個(gè)數(shù)量對(duì)比,而實(shí)際 Spring 源碼中還需要比對(duì)入?yún)㈩愋?#xff0c;否則相同數(shù)量不同入?yún)㈩愋偷那闆r,就會(huì)拋異常了。
四、總結(jié)
- 本章節(jié)的主要以完善實(shí)例化操作,增加 InstantiationStrategy 實(shí)例化策略接口,并新增了兩個(gè)實(shí)例化類。這部分類的名稱與實(shí)現(xiàn)方式基本是 Spring 框架的一個(gè)縮小版,大家在學(xué)習(xí)過程中也可以從 Spring 源碼找到對(duì)應(yīng)的代碼。
- 從我們不斷的完善增加需求可以看到的,當(dāng)你的代碼結(jié)構(gòu)設(shè)計(jì)的較為合理的時(shí)候,就可以非常容易且方便的進(jìn)行擴(kuò)展不同屬性的類職責(zé),而不會(huì)因?yàn)樾枨蟮脑黾訉?dǎo)致類結(jié)構(gòu)混亂。所以在我們自己業(yè)務(wù)需求實(shí)現(xiàn)的過程中,也要盡可能的去考慮一個(gè)良 好的擴(kuò)展性以及拆分好類的職責(zé)。
總結(jié)
以上是生活随笔為你收集整理的手写简版spring --3--对象实例化策略的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用这些心理学效应,轻松拿到满意offe
- 下一篇: 手写简版spring --4--注入属性