橫刀躍馬,關(guān)于Bean對(duì)象作用域以及FactoryBean的實(shí)現(xiàn)和使用
本文轉(zhuǎn)載自微信公眾號(hào)「bugstack蟲洞棧」,作者小傅哥。轉(zhuǎn)載本文請(qǐng)聯(lián)系bugstack蟲洞棧公眾號(hào)。
目錄
- 一、前言
- 二、目標(biāo)
- 三、方案
- 四、實(shí)現(xiàn)
- 1. 工程結(jié)構(gòu)
- 2. Bean的作用范圍定義和xml解析
- 3. 創(chuàng)建和修改對(duì)象時(shí)候判斷單例和原型模式
- 4. 定義 FactoryBean 接口
- 5. 實(shí)現(xiàn)一個(gè) FactoryBean 注冊(cè)服務(wù)
- 6. 擴(kuò)展 AbstractBeanFactory 創(chuàng)建對(duì)象邏輯
- 五、測試
- 1. 事先準(zhǔn)備
- 2. 定義 FactoryBean 對(duì)象
- 3. 配置文件
- 4. 單元測試(單例&原型)
- 5. 單元測試(代理對(duì)象)
- 六、總結(jié)
一、前言
老司機(jī),你的磚怎么搬的那么快?
是有勁?是技巧?是后門?總之,那個(gè)老司機(jī)的代碼總是可以很快的完成產(chǎn)品每次新增的需求,就像他倆是一家似的!而你就不一樣了,不只產(chǎn)品經(jīng)理還有運(yùn)營、測試的小姐姐,都得給你買吃的,求著你趕緊把Bug修修,否則都來不及上線了。
那為啥別人的代碼總是可以很快的擴(kuò)展新功能,而你的代碼從來不能被重構(gòu)只能被重寫,小需求小改、大需求大改,沒需求呢?沒需求自己跑著跑著也能崩潰,半夜被運(yùn)維薅起來:“你這怎么又有數(shù)據(jù)庫慢查詢,把別人業(yè)務(wù)都拖拉胯了!”
有人說30歲的人都,還和剛畢業(yè)的做一樣的活,是沒進(jìn)步的! 這太扯淡了,同樣是同樣的活,但做出來的結(jié)果可不一定是一樣的,有人能用ifelse把產(chǎn)品功能湊出來,也有人可以把需求拆解成各個(gè)功能模塊,定義接口、抽象類、實(shí)現(xiàn)和繼承,運(yùn)用設(shè)計(jì)模式構(gòu)建出一套新增需求時(shí)候能快速實(shí)現(xiàn),出現(xiàn)問題能準(zhǔn)確定位的代碼邏輯。這就像有人問:“樹上有十只鳥,一槍打死一只,還有幾只?”,你會(huì)想到什么?槍聲大嗎、鳥籠了嗎、鳥被綁樹上了嗎、有鳥殘疾的嗎、鳥被打死了嗎、打鳥的人眼睛好使嗎、算肚子里懷孕的鳥嗎、打鳥犯法嗎、邊上樹還有其他鳥嗎等等,這些都是一個(gè)職業(yè)技術(shù)人在一個(gè)行業(yè)磨練出來的經(jīng)驗(yàn),不是1天2天看幾本書,刷幾個(gè)洗腦文章能吸收的。
二、目標(biāo)
交給 Spring 管理的 Bean 對(duì)象,一定就是我們用類創(chuàng)建出來的 Bean 嗎?創(chuàng)建出來的 Bean 就永遠(yuǎn)是單例的嗎,沒有可能是原型模式嗎?
在集合 Spring 框架下,我們使用的 MyBatis 框架中,它的核心作用是可以滿足用戶不需要實(shí)現(xiàn) Dao 接口類,就可以通過 xml 或者注解配置的方式完成對(duì)數(shù)據(jù)庫執(zhí)行 CRUD 操作,那么在實(shí)現(xiàn)這樣的 ORM 框架中,是怎么把一個(gè)數(shù)據(jù)庫操作的 Bean 對(duì)象交給 Spring 管理的呢。
因?yàn)槲覀冊(cè)谑褂?Spring、MyBatis 框架的時(shí)候都可以知道,并沒有手動(dòng)的去創(chuàng)建任何操作數(shù)據(jù)庫的 Bean 對(duì)象,有的僅僅是一個(gè)接口定義,而這個(gè)接口定義竟然可以被注入到其他需要使用 Dao 的屬性中去了,那么這一過程最核心待解決的問題,就是需要完成把復(fù)雜且以代理方式動(dòng)態(tài)變化的對(duì)象,注冊(cè)到 Spring 容器中。而為了滿足這樣的一個(gè)擴(kuò)展組件開發(fā)的需求,就需要我們?cè)诂F(xiàn)有手寫的 Spring 框架中,添加這一能力。
三、方案
關(guān)于提供一個(gè)能讓使用者定義復(fù)雜的 Bean 對(duì)象,功能點(diǎn)非常不錯(cuò),意義也非常大,因?yàn)檫@樣做了之后 Spring 的生態(tài)種子孵化箱就此提供了,誰家的框架都可以在此標(biāo)準(zhǔn)上完成自己服務(wù)的接入。
但這樣的功能邏輯設(shè)計(jì)上并不復(fù)雜,因?yàn)檎麄€(gè) Spring 框架在開發(fā)的過程中就已經(jīng)提供了各項(xiàng)擴(kuò)展能力的接茬,你只需要在合適的位置提供一個(gè)接茬的處理接口調(diào)用和相應(yīng)的功能邏輯實(shí)現(xiàn)即可,像這里的目標(biāo)實(shí)現(xiàn)就是對(duì)外提供一個(gè)可以二次從 FactoryBean 的 getObject 方法中獲取對(duì)象的功能即可,這樣所有實(shí)現(xiàn)此接口的對(duì)象類,就可以擴(kuò)充自己的對(duì)象功能了。MyBatis 就是實(shí)現(xiàn)了一個(gè) MapperFactoryBean 類,在 getObject 方法中提供 SqlSession 對(duì)執(zhí)行 CRUD 方法的操作 整體設(shè)計(jì)結(jié)構(gòu)如下圖:
- 整個(gè)的實(shí)現(xiàn)過程包括了兩部分,一個(gè)解決單例還是原型對(duì)象,另外一個(gè)處理 FactoryBean 類型對(duì)象創(chuàng)建過程中關(guān)于獲取具體調(diào)用對(duì)象的 getObject 操作。
- SCOPE_SINGLETON、SCOPE_PROTOTYPE,對(duì)象類型的創(chuàng)建獲取方式,主要區(qū)分在于 AbstractAutowireCapableBeanFactory#createBean 創(chuàng)建完成對(duì)象后是否放入到內(nèi)存中,如果不放入則每次獲取都會(huì)重新創(chuàng)建。
- createBean 執(zhí)行對(duì)象創(chuàng)建、屬性填充、依賴加載、前置后置處理、初始化等操作后,就要開始做執(zhí)行判斷整個(gè)對(duì)象是否是一個(gè) FactoryBean 對(duì)象,如果是這樣的對(duì)象,就需要再繼續(xù)執(zhí)行獲取 FactoryBean 具體對(duì)象中的 getObject 對(duì)象了。整個(gè) getBean 過程中都會(huì)新增一個(gè)單例類型的判斷factory.isSingleton(),用于決定是否使用內(nèi)存存放對(duì)象信息。
四、實(shí)現(xiàn)
1. 工程結(jié)構(gòu)
- small-spring-step-09
- └── src
- ├── main
- │ └── java
- │ └── cn.bugstack.springframework
- │ ├── beans
- │ │ ├── factory
- │ │ │ ├── config
- │ │ │ │ ├── AutowireCapableBeanFactory.java
- │ │ │ │ ├── BeanDefinition.java
- │ │ │ │ ├── BeanFactoryPostProcessor.java
- │ │ │ │ ├── BeanPostProcessor.java
- │ │ │ │ ├── BeanReference.java
- │ │ │ │ ├── ConfigurableBeanFactory.java
- │ │ │ │ └── SingletonBeanRegistry.java
- │ │ │ ├── support
- │ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
- │ │ │ │ ├── AbstractBeanDefinitionReader.java
- │ │ │ │ ├── AbstractBeanFactory.java
- │ │ │ │ ├── BeanDefinitionReader.java
- │ │ │ │ ├── BeanDefinitionRegistry.java
- │ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
- │ │ │ │ ├── DefaultListableBeanFactory.java
- │ │ │ │ ├── DefaultSingletonBeanRegistry.java
- │ │ │ │ ├── DisposableBeanAdapter.java
- │ │ │ │ ├── FactoryBeanRegistrySupport.java
- │ │ │ │ ├── InstantiationStrategy.java
- │ │ │ │ └── SimpleInstantiationStrategy.java
- │ │ │ ├── support
- │ │ │ │ └── XmlBeanDefinitionReader.java
- │ │ │ ├── Aware.java
- │ │ │ ├── BeanClassLoaderAware.java
- │ │ │ ├── BeanFactory.java
- │ │ │ ├── BeanFactoryAware.java
- │ │ │ ├── BeanNameAware.java
- │ │ │ ├── ConfigurableListableBeanFactory.java
- │ │ │ ├── DisposableBean.java
- │ │ │ ├── FactoryBean.java
- │ │ │ ├── HierarchicalBeanFactory.java
- │ │ │ ├── InitializingBean.java
- │ │ │ └── ListableBeanFactory.java
- │ │ ├── BeansException.java
- │ │ ├── PropertyValue.java
- │ │ └── PropertyValues.java
- │ ├── context
- │ │ ├── support
- │ │ │ ├── AbstractApplicationContext.java
- │ │ │ ├── AbstractRefreshableApplicationContext.java
- │ │ │ ├── AbstractXmlApplicationContext.java
- │ │ │ ├── ApplicationContextAwareProcessor.java
- │ │ │ └── ClassPathXmlApplicationContext.java
- │ │ ├── ApplicationContext.java
- │ │ ├── ApplicationContextAware.java
- │ │ └── ConfigurableApplicationContext.java
- │ ├── core.io
- │ │ ├── ClassPathResource.java
- │ │ ├── DefaultResourceLoader.java
- │ │ ├── FileSystemResource.java
- │ │ ├── Resource.java
- │ │ ├── ResourceLoader.java
- │ │ └── UrlResource.java
- │ └── utils
- │ └── ClassUtils.java
- └── test
- └── java
- └── cn.bugstack.springframework.test
- ├── bean
- │ ├── UserDao.java
- │ └── UserService.java
- └── ApiTest.java
Spring 單例、原型以及 FactoryBean 功能實(shí)現(xiàn)類關(guān)系,如圖 10-2
圖 10-2
- 以上整個(gè)類關(guān)系圖展示的就是添加 Bean 的實(shí)例化是單例還是原型模式以及 FactoryBean 的實(shí)現(xiàn)。
- 其實(shí)整個(gè)實(shí)現(xiàn)的過程并不復(fù)雜,只是在現(xiàn)有的 AbstractAutowireCapableBeanFactory 類以及繼承的抽象類 AbstractBeanFactory 中進(jìn)行擴(kuò)展。
- 不過這次我們把 AbstractBeanFactory 繼承的 DefaultSingletonBeanRegistry 類,中間加了一層 FactoryBeanRegistrySupport,這個(gè)類在 Spring 框架中主要是處理關(guān)于 FactoryBean 注冊(cè)的支撐操作。
2. Bean的作用范圍定義和xml解析
- cn.bugstack.springframework.beans.factory.config.BeanDefinition
- public class BeanDefinition {
- String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
- String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
- private Class beanClass;
- private PropertyValues propertyValues;
- private String initMethodName;
- private String destroyMethodName;
- private String scope = SCOPE_SINGLETON;
- private boolean singleton = true;
- private boolean prototype = false;
- // ...get/set
- }
- singleton、prototype,是本次在 BeanDefinition 類中新增加的兩個(gè)屬性信息,用于把從 spring.xml 中解析到的 Bean 對(duì)象作用范圍填充到屬性中。
cn.bugstack.springframework.beans.factory.xml.XmlBeanDefinitionReader
- public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
- protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
- for (int i = 0; i < childNodes.getLength(); i++) {
- // 判斷元素
- if (!(childNodes.item(i) instanceof Element)) continue;
- // 判斷對(duì)象
- if (!"bean".equals(childNodes.item(i).getNodeName())) continue;
- // 解析標(biāo)簽
- Element bean = (Element) childNodes.item(i);
- String id = bean.getAttribute("id");
- String name = bean.getAttribute("name");
- String className = bean.getAttribute("class");
- String initMethod = bean.getAttribute("init-method");
- String destroyMethodName = bean.getAttribute("destroy-method");
- String beanScope = bean.getAttribute("scope");
- // 獲取 Class,方便獲取類中的名稱
- Class<?> clazz = Class.forName(className);
- // 優(yōu)先級(jí) id > name
- String beanName = StrUtil.isNotEmpty(id) ? id : name;
- if (StrUtil.isEmpty(beanName)) {
- beanName = StrUtil.lowerFirst(clazz.getSimpleName());
- }
- // 定義Bean
- BeanDefinition beanDefinition = new BeanDefinition(clazz);
- beanDefinition.setInitMethodName(initMethod);
- beanDefinition.setDestroyMethodName(destroyMethodName);
- if (StrUtil.isNotEmpty(beanScope)) {
- beanDefinition.setScope(beanScope);
- }
- // ...
- // 注冊(cè) BeanDefinition
- getRegistry().registerBeanDefinition(beanName, beanDefinition);
- }
- }
- }
- 在解析 XML 處理類 XmlBeanDefinitionReader 中,新增加了關(guān)于 Bean 對(duì)象配置中 scope 的解析,并把這個(gè)屬性信息填充到 Bean 定義中。beanDefinition.setScope(beanScope)
3. 創(chuàng)建和修改對(duì)象時(shí)候判斷單例和原型模式
cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
- public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
- 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);
- // 給 Bean 填充屬性
- applyPropertyValues(beanName, bean, beanDefinition);
- // 執(zhí)行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置處理方法
- bean = initializeBean(beanName, bean, beanDefinition);
- } catch (Exception e) {
- throw new BeansException("Instantiation of bean failed", e);
- }
- // 注冊(cè)實(shí)現(xiàn)了 DisposableBean 接口的 Bean 對(duì)象
- registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
- // 判斷 SCOPE_SINGLETON、SCOPE_PROTOTYPE
- if (beanDefinition.isSingleton()) {
- addSingleton(beanName, bean);
- }
- return bean;
- }
- protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
- // 非 Singleton 類型的 Bean 不執(zhí)行銷毀方法
- if (!beanDefinition.isSingleton()) return;
- if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
- registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
- }
- }
- // ... 其他功能
- }
- 單例模式和原型模式的區(qū)別就在于是否存放到內(nèi)存中,如果是原型模式那么就不會(huì)存放到內(nèi)存中,每次獲取都重新創(chuàng)建對(duì)象,另外非 Singleton 類型的 Bean 不需要執(zhí)行銷毀方法。
- 所以這里的代碼會(huì)有兩處修改,一處是 createBean 中判斷是否添加到 addSingleton(beanName, bean);,另外一處是 registerDisposableBeanIfNecessary 銷毀注冊(cè)中的判斷 if (!beanDefinition.isSingleton()) return;。
4. 定義 FactoryBean 接口
cn.bugstack.springframework.beans.factory.FactoryBean
- public interface FactoryBean<T> {
- T getObject() throws Exception;
- Class<?> getObjectType();
- boolean isSingleton();
- }
FactoryBean 中需要提供3個(gè)方法,獲取對(duì)象、對(duì)象類型,以及是否是單例對(duì)象,如果是單例對(duì)象依然會(huì)被放到內(nèi)存中。
5. 實(shí)現(xiàn)一個(gè) FactoryBean 注冊(cè)服務(wù)
cn.bugstack.springframework.beans.factory.support.FactoryBeanRegistrySupport
- public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
- /**
- * Cache of singleton objects created by FactoryBeans: FactoryBean name --> object
- */
- private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();
- protected Object getCachedObjectForFactoryBean(String beanName) {
- Object object = this.factoryBeanObjectCache.get(beanName);
- return (object != NULL_OBJECT ? object : null);
- }
- protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {
- if (factory.isSingleton()) {
- Object object = this.factoryBeanObjectCache.get(beanName);
- if (object == null) {
- object = doGetObjectFromFactoryBean(factory, beanName);
- this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
- }
- return (object != NULL_OBJECT ? object : null);
- } else {
- return doGetObjectFromFactoryBean(factory, beanName);
- }
- }
- private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName){
- try {
- return factory.getObject();
- } catch (Exception e) {
- throw new BeansException("FactoryBean threw exception on object[" + beanName + "] creation", e);
- }
- }
- }
- FactoryBeanRegistrySupport 類主要處理的就是關(guān)于 FactoryBean 此類對(duì)象的注冊(cè)操作,之所以放到這樣一個(gè)單獨(dú)的類里,就是希望做到不同領(lǐng)域模塊下只負(fù)責(zé)各自需要完成的功能,避免因?yàn)閿U(kuò)展導(dǎo)致類膨脹到難以維護(hù)。
- 同樣這里也定義了緩存操作 factoryBeanObjectCache,用于存放單例類型的對(duì)象,避免重復(fù)創(chuàng)建。在日常使用用,基本也都是創(chuàng)建的單例對(duì)象
- doGetObjectFromFactoryBean 是具體的獲取 FactoryBean#getObject() 方法,因?yàn)榧扔芯彺娴奶幚硪灿袑?duì)象的獲取,所以額外提供了 getObjectFromFactoryBean 進(jìn)行邏輯包裝,這部分的操作方式是不和你日常做的業(yè)務(wù)邏輯開發(fā)非常相似。從Redis取數(shù)據(jù),如果為空就從數(shù)據(jù)庫獲取并寫入Redis
6. 擴(kuò)展 AbstractBeanFactory 創(chuàng)建對(duì)象邏輯
cn.bugstack.springframework.beans.factory.support.AbstractBeanFactory
- public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
- protected <T> T doGetBean(final String name, final Object[] args) {
- Object sharedInstance = getSingleton(name);
- if (sharedInstance != null) {
- // 如果是 FactoryBean,則需要調(diào)用 FactoryBean#getObject
- return (T) getObjectForBeanInstance(sharedInstance, name);
- }
- BeanDefinition beanDefinition = getBeanDefinition(name);
- Object bean = createBean(name, beanDefinition, args);
- return (T) getObjectForBeanInstance(bean, name);
- }
- private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
- if (!(beanInstance instanceof FactoryBean)) {
- return beanInstance;
- }
- Object object = getCachedObjectForFactoryBean(beanName);
- if (object == null) {
- FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
- object = getObjectFromFactoryBean(factoryBean, beanName);
- }
- return object;
- }
- // ...
- }
- 首先這里把 AbstractBeanFactory 原來繼承的 DefaultSingletonBeanRegistry,修改為繼承 FactoryBeanRegistrySupport。因?yàn)樾枰獢U(kuò)展出創(chuàng)建 FactoryBean 對(duì)象的能力,所以這就想一個(gè)鏈條服務(wù)上,截出一個(gè)段來處理額外的服務(wù),并把鏈條再鏈接上。
- 此處新增加的功能主要是在 doGetBean 方法中,添加了調(diào)用 (T) getObjectForBeanInstance(sharedInstance, name) 對(duì)獲取 FactoryBean 的操作。
- 在 getObjectForBeanInstance 方法中做具體的 instanceof 判斷,另外還會(huì)從 FactoryBean 的緩存中獲取對(duì)象,如果不存在則調(diào)用 FactoryBeanRegistrySupport#getObjectFromFactoryBean,執(zhí)行具體的操作。
五、測試
1. 事先準(zhǔn)備
cn.bugstack.springframework.test.bean.IUserDao
- public interface IUserDao {
- String queryUserName(String uId);
- }
- 這個(gè)章節(jié)我們刪掉 UserDao,定義一個(gè) IUserDao 接口,之所這樣做是為了通過 FactoryBean 做一個(gè)自定義對(duì)象的代理操作。
cn.bugstack.springframework.test.bean.UserService
- public class UserService {
- private String uId;
- private String company;
- private String location;
- private IUserDao userDao;
- public String queryUserInfo() {
- return userDao.queryUserName(uId) + "," + company + "," + location;
- }
- // ...get/set
- }
在 UserService 新修改了一個(gè)原有 UserDao 屬性為 IUserDao,后面我們會(huì)給這個(gè)屬性注入代理對(duì)象。
2. 定義 FactoryBean 對(duì)象
cn.bugstack.springframework.test.bean.ProxyBeanFactory
- public class ProxyBeanFactory implements FactoryBean<IUserDao> {
- @Override
- public IUserDao getObject() throws Exception {
- InvocationHandler handler = (proxy, method, args) -> {
- Map<String, String> hashMap = new HashMap<>();
- hashMap.put("10001", "小傅哥");
- hashMap.put("10002", "八杯水");
- hashMap.put("10003", "阿毛");
- return "你被代理了 " + method.getName() + ":" + hashMap.get(args[0].toString());
- };
- return (IUserDao) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserDao.class}, handler);
- }
- @Override
- public Class<?> getObjectType() {
- return IUserDao.class;
- }
- @Override
- public boolean isSingleton() {
- return true;
- }
- }
- 這是一個(gè)實(shí)現(xiàn)接口 FactoryBean 的代理類 ProxyBeanFactory 名稱,主要是模擬了 UserDao 的原有功能,類似于 MyBatis 框架中的代理操作。
- getObject() 中提供的就是一個(gè) InvocationHandler 的代理對(duì)象,當(dāng)有方法調(diào)用的時(shí)候,則執(zhí)行代理對(duì)象的功能。
3. 配置文件
- <?xml version="1.0" encoding="UTF-8"?>
- <beans>
- <bean id="userService" class="cn.bugstack.springframework.test.bean.UserService" scope="prototype">
- <property name="uId" value="10001"/>
- <property name="company" value="騰訊"/>
- <property name="location" value="深圳"/>
- <property name="userDao" ref="proxyUserDao"/>
- </bean>
- <bean id="proxyUserDao" class="cn.bugstack.springframework.test.bean.ProxyBeanFactory"/>
- </beans>
- 在配置文件中,我們把 proxyUserDao 這個(gè)代理對(duì)象,注入到 userService 的 userDao 中。與上一章節(jié)相比,去掉了 UserDao 實(shí)現(xiàn)類,轉(zhuǎn)而用代理類替換
4. 單元測試(單例&原型)
- @Test
- public void test_prototype() {
- // 1.初始化 BeanFactory
- ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
- applicationContext.registerShutdownHook();
- // 2. 獲取Bean對(duì)象調(diào)用方法
- UserService userService01 = applicationContext.getBean("userService", UserService.class);
- UserService userService02 = applicationContext.getBean("userService", UserService.class);
- // 3. 配置 scope="prototype/singleton"
- System.out.println(userService01);
- System.out.println(userService02);
- // 4. 打印十六進(jìn)制哈希
- System.out.println(userService01 + " 十六進(jìn)制哈希:" + Integer.toHexString(userService01.hashCode()));
- System.out.println(ClassLayout.parseInstance(userService01).toPrintable());
- }
- 在 spring.xml 配置文件中,設(shè)置了 scope="prototype" 這樣就每次獲取到的對(duì)象都應(yīng)該是一個(gè)新的對(duì)象。
- 這里判斷對(duì)象是否為一個(gè)會(huì)看到打印的類對(duì)象的哈希值,所以我們把十六進(jìn)制哈希打印出來。
測試結(jié)果
- cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@1b0375b3
- cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@2f7c7260
- cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@1b0375b3 十六進(jìn)制哈希:1b0375b3
- cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984 object internals:
- OFFSET SIZE TYPE DESCRIPTION VALUE
- 0 4 (object header) 01 b3 75 03 (00000001 10110011 01110101 00000011) (58045185)
- 4 4 (object header) 1b 00 00 00 (00011011 00000000 00000000 00000000) (27)
- 8 4 (object header) 9f e1 01 f8 (10011111 11100001 00000001 11111000) (-134094433)
- 12 4 java.lang.String UserService.uId (object)
- 16 4 java.lang.String UserService.company (object)
- 20 4 java.lang.String UserService.location (object)
- 24 4 cn.bugstack.springframework.test.bean.IUserDao UserService.userDao (object)
- 28 1 boolean UserService$$EnhancerByCGLIB$$4cabb984.CGLIB$BOUND true
- 29 3 (alignment/padding gap)
- 32 4 net.sf.cglib.proxy.NoOp UserService$$EnhancerByCGLIB$$4cabb984.CGLIB$CALLBACK_0 (object)
- 36 4 (loss due to the next object alignment)
- Instance size: 40 bytes
- Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
- Process finished with exit code 0
- 對(duì)象后面的這一小段字符串就是16進(jìn)制哈希值,在對(duì)象頭哈希值存放的結(jié)果上看,也有對(duì)應(yīng)的數(shù)值。只不過這個(gè)結(jié)果是倒過來的。
- 另外可以看到 cabb984@1b0375b3、cabb984@2f7c7260,這兩個(gè)對(duì)象的結(jié)尾16進(jìn)制哈希值并不一樣,所以我們的原型模式是生效的。
5. 單元測試(代理對(duì)象)
- @Test
- public void test_factory_bean() {
- // 1.初始化 BeanFactory
- ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
- applicationContext.registerShutdownHook();
- // 2. 調(diào)用代理方法
- UserService userService = applicationContext.getBean("userService", UserService.class);
- System.out.println("測試結(jié)果:" + userService.queryUserInfo());
- }
關(guān)于 FactoryBean 的調(diào)用并沒有太多不一樣,因?yàn)樗械牟煌家呀?jīng)被 spring.xml 配置進(jìn)去了。當(dāng)然你可以直接調(diào)用 spring.xml 配置的對(duì)象 cn.bugstack.springframework.test.bean.ProxyBeanFactory
測試結(jié)果
測試結(jié)果:你被代理了 queryUserName:小傅哥,騰訊,深圳
Process finished with exit code 0
- 從測試結(jié)果來看,我們的代理類 ProxyBeanFactory 已經(jīng)完美替換掉了 UserDao 的功能。
- 雖然看上去這一點(diǎn)實(shí)現(xiàn)并不復(fù)雜,甚至有點(diǎn)簡單。但就是這樣一點(diǎn)點(diǎn)核心內(nèi)容的設(shè)計(jì)了,解決了所有需要和 Spring 結(jié)合的其他框架交互鏈接問題。如果對(duì)此類內(nèi)容感興趣,也可以閱讀小傅哥《中間件設(shè)計(jì)和開發(fā)》
六、總結(jié)
- 在 Spring 框架整個(gè)開發(fā)的過程中,前期的各個(gè)功能接口類擴(kuò)展的像膨脹了似的,但到后期在完善功能時(shí),就沒有那么難了,反而深入理解后會(huì)覺得功能的補(bǔ)充,都比較簡單。只需要再所遇領(lǐng)域范圍內(nèi)進(jìn)行擴(kuò)展相應(yīng)的服務(wù)實(shí)現(xiàn)即可。
- 當(dāng)你仔細(xì)閱讀完關(guān)于 FactoryBean 的實(shí)現(xiàn)以及測試過程的使用,以后再需要使用 FactoryBean 開發(fā)相應(yīng)的組件時(shí)候,一定會(huì)非常清楚它是如何創(chuàng)建自己的復(fù)雜 Bean 對(duì)象以及在什么時(shí)候初始化和調(diào)用的。遇到問題也可以快速的排查、定位和解決。
- 如果你在學(xué)習(xí)的過程中感覺這些類、接口、實(shí)現(xiàn)、繼承,穿梭的很復(fù)雜,一時(shí)半會(huì)腦子還反應(yīng)不過來。那么你最好的方式是動(dòng)手去畫畫這些類關(guān)系圖,梳理下實(shí)現(xiàn)的結(jié)構(gòu),看看每個(gè)類在干什么。