新聞中心
本文轉(zhuǎn)載自微信公眾號(hào)「bugstack蟲洞?!梗髡咝「蹈?。轉(zhuǎn)載本文請(qǐng)聯(lián)系bugstack蟲洞棧公眾號(hào)。

創(chuàng)新互聯(lián):自2013年起為各行業(yè)開拓出企業(yè)自己的“網(wǎng)站建設(shè)”服務(wù),為上千公司企業(yè)提供了專業(yè)的成都做網(wǎng)站、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)和網(wǎng)站推廣服務(wù), 按需開發(fā)網(wǎng)站由設(shè)計(jì)師親自精心設(shè)計(jì),設(shè)計(jì)的效果完全按照客戶的要求,并適當(dāng)?shù)奶岢龊侠淼慕ㄗh,擁有的視覺效果,策劃師分析客戶的同行競爭對(duì)手,根據(jù)客戶的實(shí)際情況給出合理的網(wǎng)站構(gòu)架,制作客戶同行業(yè)具有領(lǐng)先地位的。
目錄
- 一、前言
- 二、目標(biāo)
- 三、設(shè)計(jì)
- 四、實(shí)現(xiàn)
- 1. 工程結(jié)構(gòu)
- 2. 新增 getBean 接口
- 3. 定義實(shí)例化策略接口
- 4. JDK 實(shí)例化
- 5. Cglib 實(shí)例化
- 6. 創(chuàng)建策略調(diào)用
- 五、測試
- 1. 事先準(zhǔn)備
- 2. 測試用例
- 3. 測試結(jié)果
- 4. 操作案例
- 六、總結(jié)
- 七、系列推薦
一、前言
技術(shù)成長,是對(duì)場景設(shè)計(jì)細(xì)節(jié)不斷的雕刻!
你覺得自己的技術(shù)什么時(shí)候得到了快速的提高,是CRUD寫的多了以后嗎?想都不要想,絕對(duì)不可能!CRUD寫的再多也只是能滿足你作為一個(gè)搬磚工具人,敲擊少邏輯流水代碼的速度而已,而編程能力這一塊,除了最開始的從不熟練到熟練以外,就很少再有其他提升了。
那你可能會(huì)想什么才是編程能力提升?其實(shí)更多的編程能力的提升是你對(duì)復(fù)雜場景的架構(gòu)把控以及對(duì)每一個(gè)技術(shù)實(shí)現(xiàn)細(xì)節(jié)點(diǎn)的不斷用具有規(guī)模體量的流量沖擊驗(yàn)證時(shí),是否能保證系統(tǒng)穩(wěn)定運(yùn)行從而決定你見識(shí)了多少、學(xué)到了多少、提升了多少!
最終當(dāng)你在接一個(gè)產(chǎn)品需求時(shí),開始思考程序數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)、核心功能的算法邏輯實(shí)現(xiàn)、整體服務(wù)的設(shè)計(jì)模式使用、系統(tǒng)架構(gòu)的搭建方式、應(yīng)用集群的部署結(jié)構(gòu),那么也就是的編程能力真正提升的時(shí)候!
二、目標(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ù)就可以,如下:
- public class UserService {
- private String name;
- public UserService(String name) {
- this.name = name;
- }
- // ...
- }
報(bào)錯(cuò)如下:
- java.lang.InstantiationException: cn.bugstack.springframework.test.bean.UserService
- at java.lang.Class.newInstance(Class.java:427)
- at cn.bugstack.springframework.test.ApiTest.test_newInstance(ApiTest.java:51)
- ...
發(fā)生這一現(xiàn)象的主要原因就是因?yàn)?beanDefinition.getBeanClass().newInstance(); 實(shí)例化方式并沒有考慮構(gòu)造函數(shù)的入?yún)?,所以就這個(gè)坑就在這等著你了!那么我們的目標(biāo)就很明顯了,來把這個(gè)坑填平!
三、設(shè)計(jì)
填平這個(gè)坑的技術(shù)設(shè)計(jì)主要考慮兩部分,一個(gè)是串流程從哪合理的把構(gòu)造函數(shù)的入?yún)⑿畔鬟f到實(shí)例化操作里,另外一個(gè)是怎么去實(shí)例化含有構(gòu)造函數(shù)的對(duì)象。
圖 4-1
- 參考 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ì)象
四、實(shí)現(xiàn)
1. 工程結(jié)構(gòu)
- small-spring-step-03
- └── src
- ├── main
- │ └── java
- │ └── cn.bugstack.springframework.beans
- │ ├── factory
- │ │ ├── factory
- │ │ │ ├── BeanDefinition.java
- │ │ │ └── SingletonBeanRegistry.java
- │ │ ├── support
- │ │ │ ├── AbstractAutowireCapableBeanFactory.java
- │ │ │ ├── AbstractBeanFactory.java
- │ │ │ ├── BeanDefinitionRegistry.java
- │ │ │ ├── CglibSubclassingInstantiationStrategy.java
- │ │ │ ├── DefaultListableBeanFactory.java
- │ │ │ ├── DefaultSingletonBeanRegistry.java
- │ │ │ ├── InstantiationStrategy.java
- │ │ │ └── SimpleInstantiationStrategy.java
- │ │ └── BeanFactory.java
- │ └── BeansException.java
- └── test
- └── java
- └── cn.bugstack.springframework.test
- ├── bean
- │ └── UserService.java
- └── ApiTest.java
工程源碼:公眾號(hào)「bugstack蟲洞棧」,回復(fù):Spring 專欄,獲取完整源碼
Spring Bean 容器類關(guān)系,如圖 4-2
圖 4-2
本章節(jié)“填坑”主要是在現(xiàn)有工程中添加 InstantiationStrategy 實(shí)例化策略接口,以及補(bǔ)充相應(yīng)的 getBean 入?yún)⑿畔?,讓外部調(diào)用時(shí)可以傳遞構(gòu)造函數(shù)的入?yún)⒉㈨樌麑?shí)例化。
2. 新增 getBean 接口
- public interface BeanFactory {
- Object getBean(String name) throws BeansException;
- Object getBean(String name, Object... args) throws BeansException;
- }
BeanFactory 中我們重載了一個(gè)含有入?yún)⑿畔?args 的 getBean 方法,這樣就可以方便的傳遞入?yún)⒔o構(gòu)造函數(shù)實(shí)例化了。
3. 定義實(shí)例化策略接口
cn.bugstack.springframework.beans.factory.support.InstantiationStrategy
- public interface InstantiationStrategy {
- Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;
- }
- 在實(shí)例化接口 instantiate 方法中添加必要的入?yún)⑿畔?,包括:beanDefinition、 beanName、ctor、args
- 其中 Constructor 你可能會(huì)有一點(diǎn)陌生,它是 java.lang.reflect 包下的 Constructor 類,里面包含了一些必要的類信息,有這個(gè)參數(shù)的目的就是為了拿到符合入?yún)⑿畔⑾鄬?duì)應(yīng)的構(gòu)造函數(shù)。
- 而 args 就是一個(gè)具體的入?yún)⑿畔⒘?,最終實(shí)例化時(shí)候會(huì)用到。
4. JDK 實(shí)例化
cn.bugstack.springframework.beans.factory.support.SimpleInstantiationStrategy
- public class SimpleInstantiationStrategy implements InstantiationStrategy {
- @Override
- public 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()).newInstance(args);,把入?yún)⑿畔鬟f給 newInstance 進(jìn)行實(shí)例化。
5. Cglib 實(shí)例化
cn.bugstack.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy
- public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
- @Override
- public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(beanDefinition.getBeanClass());
- enhancer.setCallback(new NoOp() {
- @Override
- public int hashCode() {
- return super.hashCode();
- }
- });
- if (null == ctor) return enhancer.create();
- return enhancer.create(ctor.getParameterTypes(), args);
- }
- }
其實(shí) Cglib 創(chuàng)建有構(gòu)造函數(shù)的 Bean 也非常方便,在這里我們更加簡化的處理了,如果你閱讀 Spring 源碼還會(huì)看到 CallbackFilter 等實(shí)現(xiàn),不過我們目前的方式并不會(huì)影響創(chuàng)建。
6. 創(chuàng)建策略調(diào)用
cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
- public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
- private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
- @Override
- protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
- Object bean = null;
- try {
- bean = createBeanInstance(beanDefinition, beanName, args);
- } catch (Exception e) {
- throw new BeansException("Instantiation of bean failed", e);
- }
- addSingleton(beanName, bean);
- return bean;
- }
- protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
- Constructor constructorToUse = null;
- Class> beanClass = beanDefinition.getBeanClass();
- Constructor>[] declaredConstructors = beanClass.getDeclaredConstructors();
- for (Constructor ctor : declaredConstructors) {
- if (null != args && ctor.getParameterTypes().length == args.length) {
- constructorToUse = ctor;
- break;
- }
- }
- return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, 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ì)比的方式比較簡單,只是一個(gè)數(shù)量對(duì)比,而實(shí)際 Spring 源碼中還需要比對(duì)入?yún)㈩愋?,否則相同數(shù)量不同入?yún)㈩愋偷那闆r,就會(huì)拋異常了。
五、測試
1. 事先準(zhǔn)備
cn.bugstack.springframework.test.bean.UserService
- public class UserService {
- private String name;
- public UserService(String name) {
- this.name = name;
- }
- public void queryUserInfo() {
- System.out.println("查詢用戶信息:" + name);
- }
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("");
- sb.append("").append(name);
- return sb.toString();
- }
- }
這里唯一多在 UserService 中添加的就是一個(gè)有 name 入?yún)⒌臉?gòu)造函數(shù),方便我們驗(yàn)證這樣的對(duì)象是否能被實(shí)例化。
2. 測試用例
cn.bugstack.springframework.test.ApiTest
- @Test
- public void test_BeanFactory() {
- // 1.初始化 BeanFactory
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- // 2. 注入bean
- BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
- beanFactory.registerBeanDefinition("userService", beanDefinition);
- // 3.獲取bean
- UserService userService = (UserService) beanFactory.getBean("userService", "小傅哥");
- userService.queryUserInfo();
- }
在此次的單元測試中除了包括;Bean 工廠、注冊(cè) Bean、獲取 Bean,三個(gè)步驟,還額外增加了一次對(duì)象的獲取和調(diào)用。這里主要測試驗(yàn)證單例對(duì)象的是否正確的存放到了緩存中。
此外與上一章節(jié)測試過程中不同的是,我們把 UserService.class 傳遞給了 BeanDefinition 而不是像上一章節(jié)那樣直接 new UserService() 操作。
3. 測試結(jié)果
查詢用戶信息:小傅哥
Process finished with exit code 0
- 從測試結(jié)果來看,最大的變化就是可以滿足帶有構(gòu)造函數(shù)的對(duì)象,可以被實(shí)例化了。
- 你可以嘗試分別使用兩種不同的實(shí)例化策略,來進(jìn)行實(shí)例化。SimpleInstantiationStrategy、CglibSubclassingInstantiationStrategy
4. 操作案例
這里我們?cè)侔褞追N不同方式的實(shí)例化操作,放到單元測試中,方便大家比對(duì)學(xué)習(xí)。
4.1 無構(gòu)造函數(shù)
- @Test
- public void test_newInstance() throws IllegalAccessException, InstantiationException {
- UserService userService = UserService.class.newInstance();
- System.out.println(userService);
- }
這種方式的實(shí)例化也是我們?cè)谏弦徽鹿?jié)實(shí)現(xiàn) Spring Bean 容器時(shí)直接使用的方式
4.2 驗(yàn)證有構(gòu)造函數(shù)實(shí)例化
- @Test
- public void test_constructor() throws Exception {
- Class
userServiceClass = UserService.class; - Constructor
declaredConstructor = userServiceClass.getDeclaredConstructor(String.class); - UserService userService = declaredConstructor.newInstance("小傅哥");
- System.out.println(userService);
- }
從最簡單的操作來看,如果有構(gòu)造函數(shù)的類需要實(shí)例化時(shí),則需要使用 getDeclaredConstructor 獲取構(gòu)造函數(shù),之后在通過傳遞參數(shù)進(jìn)行實(shí)例化。
4.3 獲取構(gòu)造函數(shù)信息
- @Test
- public void test_parameterTypes() throws Exception {
- Class
beanClass = UserService.class; - Constructor>[] declaredConstructors = beanClass.getDeclaredConstructors();
- Constructor> constructor = declaredConstructors[0];
- Constructor
declaredConstructor = beanClass.getDeclaredConstructor(constructor.getParameterTypes()); - UserService userService = declaredConstructor.newInstance("小傅哥");
- System.out.println(userService);
這個(gè)案例中其實(shí)最核心的點(diǎn)在于獲取一個(gè)類中所有的構(gòu)造函數(shù),其實(shí)也就是這個(gè)方法的使用 beanClass.getDeclaredConstructors()
4.4 Cglib 實(shí)例化
- @Test
- public void test_cglib() {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(UserService.class);
- enhancer.setCallback(new NoOp() {
- @Override
- public int hashCode() {
- return super.hashCode();
- }
- });
- Object obj = enhancer.create(new Class[]{String.class}, new Object[]{"小傅哥"});
- System.out.println(obj);
- }
此案例演示使用非常簡單,但關(guān)于 Cglib 在 Spring 容器中的使用非常多,也可以深入的學(xué)習(xí)一下 Cglib 的擴(kuò)展知識(shí)。
六、總結(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é)。
動(dòng)手是學(xué)習(xí)起來最快的方式,不要讓眼睛是感覺看會(huì)了,但上手操作就廢了。也希望有需要的讀者可以親手操作一下,把你的想法也融入到可落地實(shí)現(xiàn)的代碼里,看看想的和做的是否一致。
名稱欄目:嶄露頭角,基于Cglib實(shí)現(xiàn)含構(gòu)造函數(shù)的類實(shí)例化策略
當(dāng)前鏈接:http://m.5511xx.com/article/cocissc.html


咨詢
建站咨詢
