做者:小傅哥
博客:https://bugstack.cnhtml
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
技術成長,是對場景設計細節不斷的雕刻!
java
你以爲本身的技術何時獲得了快速的提升,是CRUD寫的多了之後嗎?想都不要想,絕對不可能!CRUD寫的再多也只是能知足你做爲一個搬磚工具人,敲擊少邏輯流水代碼的速度而已,而編程能力這一塊,除了最開始的從不熟練到熟練之外,就不多再有其餘提高了。面試
那你可能會想什麼纔是編程能力提高?其實更多的編程能力的提高是你對複雜場景的架構把控以及對每個技術實現細節點的不斷用具備規模體量的流量衝擊驗證時,是否能保證系統穩定運行從而決定你見識了多少、學到了多少、提高了多少!算法
最終當你在接一個產品需求時,開始思考程序數據結構的設計
、核心功能的算法邏輯實現
、總體服務的設計模式使用
、系統架構的搭建方式
、應用集羣的部署結構
,那麼也就是的編程能力真正提高的時候!spring
這一章節的目標主要是爲了解決上一章節咱們埋下的坑
,那是什麼坑呢?其實就是一個關於 Bean 對象在含有構造函數進行實例化的坑。編程
在上一章節咱們擴充了 Bean 容器的功能,把實例化對象交給容器來統一處理,但在咱們實例化對象的代碼裏並無考慮對象類是否含構造函數,也就是說若是咱們去實例化一個含有構造函數的對象那麼就要拋異常了。設計模式
怎麼驗證?其實就是把 UserService 添加一個含入參信息的構造函數就能夠,以下:緩存
public class UserService { private String name; public UserService(String name) { this.name = name; } // ... }
報錯以下:數據結構
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) ...
發生這一現象的主要緣由就是由於 beanDefinition.getBeanClass().newInstance();
實例化方式並無考慮構造函數的入參,因此就這個坑就在這等着你了!那麼咱們的目標就很明顯了,來把這個坑填平!架構
填平這個坑的技術設計主要考慮兩部分,一個是串流程從哪合理的把構造函數的入參信息傳遞到實例化操做裏,另一個是怎麼去實例化含有構造函數的對象。
Object getBean(String name, Object... args)
接口,這樣就能夠在獲取 Bean 時把構造函數的入參信息傳遞進去了。DeclaredConstructor
,另一個是使用 Cglib 來動態建立 Bean 對象。Cglib 是基於字節碼框架 ASM 實現,因此你也能夠直接經過 ASM 操做指令碼來建立對象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
工程源碼:公衆號「bugstack蟲洞棧」,回覆:Spring 專欄,獲取完整源碼
Spring Bean 容器類關係,如圖 4-2
本章節「填坑」
主要是在現有工程中添加 InstantiationStrategy 實例化策略接口,以及補充相應的 getBean 入參信息,讓外部調用時能夠傳遞構造函數的入參並順利實例化。
cn.bugstack.springframework.beans.factory.BeanFactory
public interface BeanFactory { Object getBean(String name) throws BeansException; Object getBean(String name, Object... args) throws BeansException; }
cn.bugstack.springframework.beans.factory.support.InstantiationStrategy
public interface InstantiationStrategy { Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException; }
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); } } }
clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
,把入參信息傳遞給 newInstance 進行實例化。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); } }
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); } }
InstantiationStrategy instantiationStrategy
,這裏咱們選擇了 Cglib 的實現類。createBeanInstance
方法,在這個方法中須要注意 Constructor 表明了你有多少個構造函數,經過 beanClass.getDeclaredConstructors() 方式能夠獲取到你全部的構造函數,是一個集合。args
的匹配狀況,這裏咱們對比的方式比較簡單,只是一個數量對比,而實際 Springcn.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(); } }
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(); }
查詢用戶信息:小傅哥 Process finished with exit code 0
SimpleInstantiationStrategy
、CglibSubclassingInstantiationStrategy
這裏咱們再把幾種不一樣方式的實例化操做,放到單元測試中,方便你們比對學習。
@Test public void test_newInstance() throws IllegalAccessException, InstantiationException { UserService userService = UserService.class.newInstance(); System.out.println(userService); }
@Test public void test_constructor() throws Exception { Class<UserService> userServiceClass = UserService.class; Constructor<UserService> declaredConstructor = userServiceClass.getDeclaredConstructor(String.class); UserService userService = declaredConstructor.newInstance("小傅哥"); System.out.println(userService); }
getDeclaredConstructor
獲取構造函數,以後在經過傳遞參數進行實例化。@Test public void test_parameterTypes() throws Exception { Class<UserService> beanClass = UserService.class; Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors(); Constructor<?> constructor = declaredConstructors[0]; Constructor<UserService> declaredConstructor = beanClass.getDeclaredConstructor(constructor.getParameterTypes()); UserService userService = declaredConstructor.newInstance("小傅哥"); System.out.println(userService);
beanClass.getDeclaredConstructors()
@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); }