成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

橫刀躍馬,關(guān)于Bean對(duì)象作用域以及FactoryBean的實(shí)現(xiàn)和使用

開發(fā) 前端
交給 Spring 管理的 Bean 對(duì)象,一定就是我們用類創(chuàng)建出來的 Bean 嗎?創(chuàng)建出來的 Bean 就永遠(yuǎn)是單例的嗎,沒有可能是原型模式嗎?

[[408524]]

本文轉(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)

  1. small-spring-step-09 
  2. └── src 
  3.     ├── main 
  4.     │   └── java 
  5.     │       └── cn.bugstack.springframework 
  6.     │           ├── beans 
  7.     │           │   ├── factory 
  8.     │           │   │   ├── config 
  9.     │           │   │   │   ├── AutowireCapableBeanFactory.java 
  10.     │           │   │   │   ├── BeanDefinition.java 
  11.     │           │   │   │   ├── BeanFactoryPostProcessor.java 
  12.     │           │   │   │   ├── BeanPostProcessor.java 
  13.     │           │   │   │   ├── BeanReference.java 
  14.     │           │   │   │   ├── ConfigurableBeanFactory.java 
  15.     │           │   │   │   └── SingletonBeanRegistry.java 
  16.     │           │   │   ├── support 
  17.     │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java 
  18.     │           │   │   │   ├── AbstractBeanDefinitionReader.java 
  19.     │           │   │   │   ├── AbstractBeanFactory.java 
  20.     │           │   │   │   ├── BeanDefinitionReader.java 
  21.     │           │   │   │   ├── BeanDefinitionRegistry.java 
  22.     │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java 
  23.     │           │   │   │   ├── DefaultListableBeanFactory.java 
  24.     │           │   │   │   ├── DefaultSingletonBeanRegistry.java 
  25.     │           │   │   │   ├── DisposableBeanAdapter.java 
  26.     │           │   │   │   ├── FactoryBeanRegistrySupport.java 
  27.     │           │   │   │   ├── InstantiationStrategy.java 
  28.     │           │   │   │   └── SimpleInstantiationStrategy.java   
  29.     │           │   │   ├── support 
  30.     │           │   │   │   └── XmlBeanDefinitionReader.java 
  31.     │           │   │   ├── Aware.java 
  32.     │           │   │   ├── BeanClassLoaderAware.java 
  33.     │           │   │   ├── BeanFactory.java 
  34.     │           │   │   ├── BeanFactoryAware.java 
  35.     │           │   │   ├── BeanNameAware.java 
  36.     │           │   │   ├── ConfigurableListableBeanFactory.java 
  37.     │           │   │   ├── DisposableBean.java 
  38.     │           │   │   ├── FactoryBean.java 
  39.     │           │   │   ├── HierarchicalBeanFactory.java 
  40.     │           │   │   ├── InitializingBean.java 
  41.     │           │   │   └── ListableBeanFactory.java 
  42.     │           │   ├── BeansException.java 
  43.     │           │   ├── PropertyValue.java 
  44.     │           │   └── PropertyValues.java  
  45.     │           ├── context 
  46.     │           │   ├── support 
  47.     │           │   │   ├── AbstractApplicationContext.java  
  48.     │           │   │   ├── AbstractRefreshableApplicationContext.java  
  49.     │           │   │   ├── AbstractXmlApplicationContext.java  
  50.     │           │   │   ├── ApplicationContextAwareProcessor.java  
  51.     │           │   │   └── ClassPathXmlApplicationContext.java  
  52.     │           │   ├── ApplicationContext.java  
  53.     │           │   ├── ApplicationContextAware.java  
  54.     │           │   └── ConfigurableApplicationContext.java 
  55.     │           ├── core.io 
  56.     │           │   ├── ClassPathResource.java  
  57.     │           │   ├── DefaultResourceLoader.java  
  58.     │           │   ├── FileSystemResource.java  
  59.     │           │   ├── Resource.java  
  60.     │           │   ├── ResourceLoader.java  
  61.     │           │   └── UrlResource.java 
  62.     │           └── utils 
  63.     │               └── ClassUtils.java 
  64.     └── test 
  65.         └── java 
  66.             └── cn.bugstack.springframework.test 
  67.                 ├── bean 
  68.                 │   ├── UserDao.java 
  69.                 │   └── UserService.java 
  70.                 └── 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
  1. public class BeanDefinition { 
  2.  
  3.     String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; 
  4.  
  5.     String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; 
  6.  
  7.     private Class beanClass; 
  8.  
  9.     private PropertyValues propertyValues; 
  10.  
  11.     private String initMethodName; 
  12.  
  13.     private String destroyMethodName; 
  14.  
  15.     private String scope = SCOPE_SINGLETON; 
  16.  
  17.     private boolean singleton = true
  18.  
  19.     private boolean prototype = false
  20.      
  21.     // ...get/set 
  • singleton、prototype,是本次在 BeanDefinition 類中新增加的兩個(gè)屬性信息,用于把從 spring.xml 中解析到的 Bean 對(duì)象作用范圍填充到屬性中。

cn.bugstack.springframework.beans.factory.xml.XmlBeanDefinitionReader

  1. public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { 
  2.  
  3.     protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException { 
  4.        
  5.         for (int i = 0; i < childNodes.getLength(); i++) { 
  6.             // 判斷元素 
  7.             if (!(childNodes.item(i) instanceof Element)) continue
  8.             // 判斷對(duì)象 
  9.             if (!"bean".equals(childNodes.item(i).getNodeName())) continue
  10.  
  11.             // 解析標(biāo)簽 
  12.             Element bean = (Element) childNodes.item(i); 
  13.             String id = bean.getAttribute("id"); 
  14.             String name = bean.getAttribute("name"); 
  15.             String className = bean.getAttribute("class"); 
  16.             String initMethod = bean.getAttribute("init-method"); 
  17.             String destroyMethodName = bean.getAttribute("destroy-method"); 
  18.             String beanScope = bean.getAttribute("scope"); 
  19.  
  20.             // 獲取 Class,方便獲取類中的名稱 
  21.             Class<?> clazz = Class.forName(className); 
  22.             // 優(yōu)先級(jí) id > name 
  23.             String beanName = StrUtil.isNotEmpty(id) ? id : name
  24.             if (StrUtil.isEmpty(beanName)) { 
  25.                 beanName = StrUtil.lowerFirst(clazz.getSimpleName()); 
  26.             } 
  27.  
  28.             // 定義Bean 
  29.             BeanDefinition beanDefinition = new BeanDefinition(clazz); 
  30.             beanDefinition.setInitMethodName(initMethod); 
  31.             beanDefinition.setDestroyMethodName(destroyMethodName); 
  32.  
  33.             if (StrUtil.isNotEmpty(beanScope)) { 
  34.                 beanDefinition.setScope(beanScope); 
  35.             } 
  36.              
  37.             // ... 
  38.              
  39.             // 注冊(cè) BeanDefinition 
  40.             getRegistry().registerBeanDefinition(beanName, beanDefinition); 
  41.         } 
  42.     } 
  43.  
  • 在解析 XML 處理類 XmlBeanDefinitionReader 中,新增加了關(guān)于 Bean 對(duì)象配置中 scope 的解析,并把這個(gè)屬性信息填充到 Bean 定義中。beanDefinition.setScope(beanScope)

3. 創(chuàng)建和修改對(duì)象時(shí)候判斷單例和原型模式

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

  1. public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { 
  2.  
  3.     private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); 
  4.  
  5.     @Override 
  6.     protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException { 
  7.         Object bean = null
  8.         try { 
  9.             bean = createBeanInstance(beanDefinition, beanName, args); 
  10.             // 給 Bean 填充屬性 
  11.             applyPropertyValues(beanName, bean, beanDefinition); 
  12.             // 執(zhí)行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置處理方法 
  13.             bean = initializeBean(beanName, bean, beanDefinition); 
  14.         } catch (Exception e) { 
  15.             throw new BeansException("Instantiation of bean failed", e); 
  16.         } 
  17.  
  18.         // 注冊(cè)實(shí)現(xiàn)了 DisposableBean 接口的 Bean 對(duì)象 
  19.         registerDisposableBeanIfNecessary(beanName, bean, beanDefinition); 
  20.  
  21.         // 判斷 SCOPE_SINGLETON、SCOPE_PROTOTYPE 
  22.         if (beanDefinition.isSingleton()) { 
  23.             addSingleton(beanName, bean); 
  24.         } 
  25.         return bean; 
  26.     } 
  27.  
  28.     protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) { 
  29.         // 非 Singleton 類型的 Bean 不執(zhí)行銷毀方法 
  30.         if (!beanDefinition.isSingleton()) return
  31.  
  32.         if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) { 
  33.             registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition)); 
  34.         } 
  35.     } 
  36.      
  37.     // ... 其他功能 
  • 單例模式和原型模式的區(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

  1. public interface FactoryBean<T> { 
  2.  
  3.     T getObject() throws Exception; 
  4.  
  5.     Class<?> getObjectType(); 
  6.  
  7.     boolean isSingleton(); 
  8.  

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

  1. public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry { 
  2.  
  3.     /** 
  4.      * Cache of singleton objects created by FactoryBeans: FactoryBean name --> object 
  5.      */ 
  6.     private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>(); 
  7.  
  8.     protected Object getCachedObjectForFactoryBean(String beanName) { 
  9.         Object object = this.factoryBeanObjectCache.get(beanName); 
  10.         return (object != NULL_OBJECT ? object : null); 
  11.     } 
  12.  
  13.     protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) { 
  14.         if (factory.isSingleton()) { 
  15.             Object object = this.factoryBeanObjectCache.get(beanName); 
  16.             if (object == null) { 
  17.                 object = doGetObjectFromFactoryBean(factory, beanName); 
  18.                 this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT)); 
  19.             } 
  20.             return (object != NULL_OBJECT ? object : null); 
  21.         } else { 
  22.             return doGetObjectFromFactoryBean(factory, beanName); 
  23.         } 
  24.     } 
  25.  
  26.     private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName){ 
  27.         try { 
  28.             return factory.getObject(); 
  29.         } catch (Exception e) { 
  30.             throw new BeansException("FactoryBean threw exception on object[" + beanName + "] creation", e); 
  31.         } 
  32.     } 
  33.  
  • 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

  1. public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { 
  2.  
  3.     protected <T> T doGetBean(final String name, final Object[] args) { 
  4.         Object sharedInstance = getSingleton(name); 
  5.         if (sharedInstance != null) { 
  6.             // 如果是 FactoryBean,則需要調(diào)用 FactoryBean#getObject 
  7.             return (T) getObjectForBeanInstance(sharedInstance, name); 
  8.         } 
  9.  
  10.         BeanDefinition beanDefinition = getBeanDefinition(name); 
  11.         Object bean = createBean(name, beanDefinition, args); 
  12.         return (T) getObjectForBeanInstance(bean, name); 
  13.     }   
  14.     
  15.     private Object getObjectForBeanInstance(Object beanInstance, String beanName) { 
  16.         if (!(beanInstance instanceof FactoryBean)) { 
  17.             return beanInstance; 
  18.         } 
  19.  
  20.         Object object = getCachedObjectForFactoryBean(beanName); 
  21.  
  22.         if (object == null) { 
  23.             FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance; 
  24.             object = getObjectFromFactoryBean(factoryBean, beanName); 
  25.         } 
  26.  
  27.         return object; 
  28.     } 
  29.          
  30.     // ... 
  • 首先這里把 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

  1. public interface IUserDao { 
  2.  
  3.     String queryUserName(String uId); 
  4.  
  • 這個(gè)章節(jié)我們刪掉 UserDao,定義一個(gè) IUserDao 接口,之所這樣做是為了通過 FactoryBean 做一個(gè)自定義對(duì)象的代理操作。

cn.bugstack.springframework.test.bean.UserService

  1. public class UserService { 
  2.  
  3.     private String uId; 
  4.     private String company; 
  5.     private String location; 
  6.     private IUserDao userDao; 
  7.  
  8.     public String queryUserInfo() { 
  9.         return userDao.queryUserName(uId) + "," + company + "," + location; 
  10.     } 
  11.  
  12.     // ...get/set 

在 UserService 新修改了一個(gè)原有 UserDao 屬性為 IUserDao,后面我們會(huì)給這個(gè)屬性注入代理對(duì)象。

2. 定義 FactoryBean 對(duì)象

cn.bugstack.springframework.test.bean.ProxyBeanFactory

  1. public class ProxyBeanFactory implements FactoryBean<IUserDao> { 
  2.  
  3.     @Override 
  4.     public IUserDao getObject() throws Exception { 
  5.         InvocationHandler handler = (proxy, method, args) -> { 
  6.  
  7.             Map<String, String> hashMap = new HashMap<>(); 
  8.             hashMap.put("10001""小傅哥"); 
  9.             hashMap.put("10002""八杯水"); 
  10.             hashMap.put("10003""阿毛"); 
  11.              
  12.             return "你被代理了 " + method.getName() + ":" + hashMap.get(args[0].toString()); 
  13.         }; 
  14.         return (IUserDao) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserDao.class}, handler); 
  15.     } 
  16.  
  17.     @Override 
  18.     public Class<?> getObjectType() { 
  19.         return IUserDao.class; 
  20.     } 
  21.  
  22.     @Override 
  23.     public boolean isSingleton() { 
  24.         return true
  25.     } 
  26.  
  • 這是一個(gè)實(shí)現(xiàn)接口 FactoryBean 的代理類 ProxyBeanFactory 名稱,主要是模擬了 UserDao 的原有功能,類似于 MyBatis 框架中的代理操作。
  • getObject() 中提供的就是一個(gè) InvocationHandler 的代理對(duì)象,當(dāng)有方法調(diào)用的時(shí)候,則執(zhí)行代理對(duì)象的功能。

3. 配置文件

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans> 
  3.  
  4.     <bean id="userService" class="cn.bugstack.springframework.test.bean.UserService" scope="prototype"
  5.         <property name="uId" value="10001"/> 
  6.         <property name="company" value="騰訊"/> 
  7.         <property name="location" value="深圳"/> 
  8.         <property name="userDao" ref="proxyUserDao"/> 
  9.     </bean> 
  10.  
  11.     <bean id="proxyUserDao" class="cn.bugstack.springframework.test.bean.ProxyBeanFactory"/> 
  12.  
  13. </beans> 
  • 在配置文件中,我們把 proxyUserDao 這個(gè)代理對(duì)象,注入到 userService 的 userDao 中。與上一章節(jié)相比,去掉了 UserDao 實(shí)現(xiàn)類,轉(zhuǎn)而用代理類替換

4. 單元測試(單例&原型)

  1. @Test 
  2. public void test_prototype() { 
  3.     // 1.初始化 BeanFactory 
  4.     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml"); 
  5.     applicationContext.registerShutdownHook();    
  6.  
  7.     // 2. 獲取Bean對(duì)象調(diào)用方法 
  8.     UserService userService01 = applicationContext.getBean("userService", UserService.class); 
  9.     UserService userService02 = applicationContext.getBean("userService", UserService.class); 
  10.      
  11.     // 3. 配置 scope="prototype/singleton" 
  12.     System.out.println(userService01); 
  13.     System.out.println(userService02);     
  14.  
  15.     // 4. 打印十六進(jìn)制哈希 
  16.     System.out.println(userService01 + " 十六進(jìn)制哈希:" + Integer.toHexString(userService01.hashCode())); 
  17.     System.out.println(ClassLayout.parseInstance(userService01).toPrintable()); 
  18.  
  • 在 spring.xml 配置文件中,設(shè)置了 scope="prototype" 這樣就每次獲取到的對(duì)象都應(yīng)該是一個(gè)新的對(duì)象。
  • 這里判斷對(duì)象是否為一個(gè)會(huì)看到打印的類對(duì)象的哈希值,所以我們把十六進(jìn)制哈希打印出來。

測試結(jié)果

  1. cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@1b0375b3 
  2. cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@2f7c7260 
  3. cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@1b0375b3 十六進(jìn)制哈希:1b0375b3 
  4. cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984 object internals: 
  5.  OFFSET  SIZE                                             TYPE DESCRIPTION                                               VALUE 
  6.       0     4                                                  (object header)                                           01 b3 75 03 (00000001 10110011 01110101 00000011) (58045185) 
  7.       4     4                                                  (object header)                                           1b 00 00 00 (00011011 00000000 00000000 00000000) (27) 
  8.       8     4                                                  (object header)                                           9f e1 01 f8 (10011111 11100001 00000001 11111000) (-134094433) 
  9.      12     4                                 java.lang.String UserService.uId                                           (object) 
  10.      16     4                                 java.lang.String UserService.company                                       (object) 
  11.      20     4                                 java.lang.String UserService.location                                      (object) 
  12.      24     4   cn.bugstack.springframework.test.bean.IUserDao UserService.userDao                                       (object) 
  13.      28     1                                          boolean UserService$$EnhancerByCGLIB$$4cabb984.CGLIB$BOUND        true 
  14.      29     3                                                  (alignment/padding gap)                                   
  15.      32     4                          net.sf.cglib.proxy.NoOp UserService$$EnhancerByCGLIB$$4cabb984.CGLIB$CALLBACK_0   (object) 
  16.      36     4                                                  (loss due to the next object alignment) 
  17. Instance size: 40 bytes 
  18. Space losses: 3 bytes internal + 4 bytes external = 7 bytes total 
  19.  
  20.  
  21. 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ì)象)

  1. @Test 
  2. public void test_factory_bean() { 
  3.     // 1.初始化 BeanFactory 
  4.     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml"); 
  5.     applicationContext.registerShutdownHook();  
  6.  
  7.     // 2. 調(diào)用代理方法 
  8.     UserService userService = applicationContext.getBean("userService", UserService.class); 
  9.     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è)類在干什么。

 

責(zé)任編輯:武曉燕 來源: bugstack蟲洞棧
相關(guān)推薦

2011-03-18 09:27:00

Spring

2021-07-05 08:43:46

Spring Beanscope作用域

2023-06-29 08:32:41

Bean作用域

2010-09-01 09:10:30

DHCP作用域

2010-09-27 13:21:02

DHCP作用域

2023-09-05 08:23:56

SpringScope方法

2019-03-13 08:00:00

JavaScript作用域前端

2021-03-09 08:35:51

JSS作用域前端

2011-09-06 09:56:24

JavaScript

2021-06-03 07:55:12

技術(shù)

2021-07-01 11:56:51

JavaScript開發(fā)代碼

2024-01-05 08:38:20

SpringBeanScope

2017-09-14 13:55:57

JavaScript

2022-08-31 07:04:50

Bean作用域

2009-01-04 09:08:30

面向?qū)ο?/a>繼承接口

2022-08-17 08:10:34

語言VisitorListener

2023-07-06 14:24:23

Spring接口自定義

2012-06-21 10:18:43

索引搜索Java

2009-06-12 09:49:25

EJB事務(wù)屬性EJB事物

2021-04-14 07:52:00

Vue 作用域插槽
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 龙珠z国语版在线观看 | 黄网站在线播放 | 欧美视频在线免费 | 日韩精品中文字幕在线 | 国产精品一区三区 | 久久高清免费视频 | 国产999精品久久久久久绿帽 | 国产高清久久久 | 久草免费在线视频 | 国产高清在线精品一区二区三区 | 国产福利91精品 | 日韩欧美国产综合 | 一区二区手机在线 | 一级黄色片网站 | 日韩在线国产 | 亚洲在线看 | 午夜视频在线免费观看 | 欧洲精品在线观看 | 久久99精品久久久久蜜桃tv | 久久99精品久久久久蜜桃tv | 亚洲成在线观看 | 99国产精品视频免费观看一公开 | 亚洲自拍偷拍欧美 | 嫩草视频在线 | 国产一级在线观看 | 精品国产不卡一区二区三区 | 久久精品中文 | 日韩欧美亚洲 | 亚洲国产成人精品女人久久久野战 | 亚洲国产成人av好男人在线观看 | 国产精品区一区二 | 在线免费观看a级片 | 久久性色| 99久久免费精品国产男女高不卡 | 中文字幕在线视频网站 | 国产二区视频 | 亚洲精品在线免费 | 狠狠干狠狠操 | 超碰97人人人人人蜜桃 | 欧美成人手机在线 | 蜜月va乱码一区二区三区 |