手写简版spring --3--对象实例化策略
生活随笔
收集整理的這篇文章主要介紹了
手写简版spring --3--对象实例化策略
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、目標
這一章節的目標主要是為了解決上一章節我們埋下的坑,那是什么坑呢?其實就是一個關于 Bean 對象在含有構造函數進行實例化的坑。在上一章節我們擴充了 Bean 容器的功能,把實例化對象交給容器來統一處理,但在我們實例化對象的代碼里并沒有考慮對象類是否含構造函數,也就是說如果我們去實例化一個含有構造函數的對象那么就要拋異常了。怎么驗證?其實就是把 UserService 添加一個含入參信息的構造函數就可以。
- 驗證
發生這一現象的主要原因就是因為 beanDefinition.getBeanClass().newInstance(); 實例化方式并沒有考慮構造函數的入參,所以就這個坑就在這等著你了!那么我們的目標就很明顯了,來把這個坑填平!
二、設計
填平這個坑的技術設計主要考慮兩部分
- 一個是串流程從哪合理的把構造函數的入參信息傳遞到實例化操作里
- 另外一個是怎么去實例化含有構造函數的對象。
- 參考 Spring Bean 容器源碼的實現方式,在 BeanFactory 中添加 Object getBean(String name, Object... args) 接口,這樣就可以在獲取 Bean 時把構造函數的入參信息傳遞進去了。
- 另外一個核心的內容是使用什么方式來創建含有構造函數的 Bean 對象呢?這里有兩種方式可以選擇,一個是基于 Java 本身自帶的方法DeclaredConstructor,另外一個是使用 Cglib 來動態創建 Bean 對象。 Cglib 是基于字節碼框架 ASM 實現,所以你也可以直接通過 ASM 操作指令碼來 創建對象
關于代理的文章
三、實現
相比于第二章的類依賴圖,主要變化:
- 在現有工程中添加 InstantiationStrategy 實例化策略接口
- 以及在BeanFactory接口中補充相應的 getBean 入參信息,讓外部調用時可以傳遞構造函數的入參并順利實例化。
- BeanFactory
- 定義實例化策略接口
- 實例化(使用上面提到的兩種方式)
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 信息,這個 Class 信息是在 Bean 定義的時候傳遞進去的。
- 接下來判斷 ctor 是否為空,如果為空則是無構造函數實例化,否則就是需要有構造函數的實例化。
- 這里我們重點關注有構造函數的實例化,實例化方式為clazz.getDeclaredConstructor(ctor.getParameterTypes()).ne wInstance(args);把入參信息傳遞給 newInstance 進行實例化。
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 抽象類中定義了一個創建對象的實例化策略屬性類 InstantiationStrategy instantiationStrategy,這里我們選擇了 Cglib 的實現類。
- 接下來抽取 createBeanInstance 方法,在這個方法中需要注意 Constructor代表了你有多少個構造函數,通過 beanClass.getDeclaredConstructors() 方式可以獲取到你所有的構造函數,是一個集合。
- 接下來就需要循環比對出構造函數集合與入參信息 args 的匹配情況,這里我們對比的方式比較簡單,只是一個數量對比,而實際 Spring 源碼中還需要比對入參類型,否則相同數量不同入參類型的情況,就會拋異常了。
四、總結
- 本章節的主要以完善實例化操作,增加 InstantiationStrategy 實例化策略接口,并新增了兩個實例化類。這部分類的名稱與實現方式基本是 Spring 框架的一個縮小版,大家在學習過程中也可以從 Spring 源碼找到對應的代碼。
- 從我們不斷的完善增加需求可以看到的,當你的代碼結構設計的較為合理的時候,就可以非常容易且方便的進行擴展不同屬性的類職責,而不會因為需求的增加導致類結構混亂。所以在我們自己業務需求實現的過程中,也要盡可能的去考慮一個良 好的擴展性以及拆分好類的職責。
總結
以上是生活随笔為你收集整理的手写简版spring --3--对象实例化策略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用这些心理学效应,轻松拿到满意offe
- 下一篇: 手写简版spring --4--注入属性