精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

橫刀躍馬,關于Bean對象作用域以及FactoryBean的實現和使用

開發 前端
交給 Spring 管理的 Bean 對象,一定就是我們用類創建出來的 Bean 嗎?創建出來的 Bean 就永遠是單例的嗎,沒有可能是原型模式嗎?

[[408524]]

本文轉載自微信公眾號「bugstack蟲洞棧」,作者小傅哥。轉載本文請聯系bugstack蟲洞棧公眾號。

 目錄

  • 一、前言
  • 二、目標
  • 三、方案
  • 四、實現
    • 1. 工程結構
    • 2. Bean的作用范圍定義和xml解析
    • 3. 創建和修改對象時候判斷單例和原型模式
    • 4. 定義 FactoryBean 接口
    • 5. 實現一個 FactoryBean 注冊服務
    • 6. 擴展 AbstractBeanFactory 創建對象邏輯
  • 五、測試
    • 1. 事先準備
    • 2. 定義 FactoryBean 對象
    • 3. 配置文件
    • 4. 單元測試(單例&原型)
    • 5. 單元測試(代理對象)
  • 六、總結

一、前言

老司機,你的磚怎么搬的那么快?

是有勁?是技巧?是后門?總之,那個老司機的代碼總是可以很快的完成產品每次新增的需求,就像他倆是一家似的!而你就不一樣了,不只產品經理還有運營、測試的小姐姐,都得給你買吃的,求著你趕緊把Bug修修,否則都來不及上線了。

那為啥別人的代碼總是可以很快的擴展新功能,而你的代碼從來不能被重構只能被重寫,小需求小改、大需求大改,沒需求呢?沒需求自己跑著跑著也能崩潰,半夜被運維薅起來:“你這怎么又有數據庫慢查詢,把別人業務都拖拉胯了!”

有人說30歲的人都,還和剛畢業的做一樣的活,是沒進步的! 這太扯淡了,同樣是同樣的活,但做出來的結果可不一定是一樣的,有人能用ifelse把產品功能湊出來,也有人可以把需求拆解成各個功能模塊,定義接口、抽象類、實現和繼承,運用設計模式構建出一套新增需求時候能快速實現,出現問題能準確定位的代碼邏輯。這就像有人問:“樹上有十只鳥,一槍打死一只,還有幾只?”,你會想到什么?槍聲大嗎、鳥籠了嗎、鳥被綁樹上了嗎、有鳥殘疾的嗎、鳥被打死了嗎、打鳥的人眼睛好使嗎、算肚子里懷孕的鳥嗎、打鳥犯法嗎、邊上樹還有其他鳥嗎等等,這些都是一個職業技術人在一個行業磨練出來的經驗,不是1天2天看幾本書,刷幾個洗腦文章能吸收的。

二、目標

交給 Spring 管理的 Bean 對象,一定就是我們用類創建出來的 Bean 嗎?創建出來的 Bean 就永遠是單例的嗎,沒有可能是原型模式嗎?

在集合 Spring 框架下,我們使用的 MyBatis 框架中,它的核心作用是可以滿足用戶不需要實現 Dao 接口類,就可以通過 xml 或者注解配置的方式完成對數據庫執行 CRUD 操作,那么在實現這樣的 ORM 框架中,是怎么把一個數據庫操作的 Bean 對象交給 Spring 管理的呢。

因為我們在使用 Spring、MyBatis 框架的時候都可以知道,并沒有手動的去創建任何操作數據庫的 Bean 對象,有的僅僅是一個接口定義,而這個接口定義竟然可以被注入到其他需要使用 Dao 的屬性中去了,那么這一過程最核心待解決的問題,就是需要完成把復雜且以代理方式動態變化的對象,注冊到 Spring 容器中。而為了滿足這樣的一個擴展組件開發的需求,就需要我們在現有手寫的 Spring 框架中,添加這一能力。

三、方案

關于提供一個能讓使用者定義復雜的 Bean 對象,功能點非常不錯,意義也非常大,因為這樣做了之后 Spring 的生態種子孵化箱就此提供了,誰家的框架都可以在此標準上完成自己服務的接入。

但這樣的功能邏輯設計上并不復雜,因為整個 Spring 框架在開發的過程中就已經提供了各項擴展能力的接茬,你只需要在合適的位置提供一個接茬的處理接口調用和相應的功能邏輯實現即可,像這里的目標實現就是對外提供一個可以二次從 FactoryBean 的 getObject 方法中獲取對象的功能即可,這樣所有實現此接口的對象類,就可以擴充自己的對象功能了。MyBatis 就是實現了一個 MapperFactoryBean 類,在 getObject 方法中提供 SqlSession 對執行 CRUD 方法的操作 整體設計結構如下圖:

  • 整個的實現過程包括了兩部分,一個解決單例還是原型對象,另外一個處理 FactoryBean 類型對象創建過程中關于獲取具體調用對象的 getObject 操作。
  • SCOPE_SINGLETON、SCOPE_PROTOTYPE,對象類型的創建獲取方式,主要區分在于 AbstractAutowireCapableBeanFactory#createBean 創建完成對象后是否放入到內存中,如果不放入則每次獲取都會重新創建。
  • createBean 執行對象創建、屬性填充、依賴加載、前置后置處理、初始化等操作后,就要開始做執行判斷整個對象是否是一個 FactoryBean 對象,如果是這樣的對象,就需要再繼續執行獲取 FactoryBean 具體對象中的 getObject 對象了。整個 getBean 過程中都會新增一個單例類型的判斷factory.isSingleton(),用于決定是否使用內存存放對象信息。

四、實現

1. 工程結構

  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 功能實現類關系,如圖 10-2

圖 10-2

  • 以上整個類關系圖展示的就是添加 Bean 的實例化是單例還是原型模式以及 FactoryBean 的實現。
  • 其實整個實現的過程并不復雜,只是在現有的 AbstractAutowireCapableBeanFactory 類以及繼承的抽象類 AbstractBeanFactory 中進行擴展。
  • 不過這次我們把 AbstractBeanFactory 繼承的 DefaultSingletonBeanRegistry 類,中間加了一層 FactoryBeanRegistrySupport,這個類在 Spring 框架中主要是處理關于 FactoryBean 注冊的支撐操作。

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 類中新增加的兩個屬性信息,用于把從 spring.xml 中解析到的 Bean 對象作用范圍填充到屬性中。

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.             // 判斷對象 
  9.             if (!"bean".equals(childNodes.item(i).getNodeName())) continue
  10.  
  11.             // 解析標簽 
  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.             // 優先級 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.             // 注冊 BeanDefinition 
  40.             getRegistry().registerBeanDefinition(beanName, beanDefinition); 
  41.         } 
  42.     } 
  43.  
  • 在解析 XML 處理類 XmlBeanDefinitionReader 中,新增加了關于 Bean 對象配置中 scope 的解析,并把這個屬性信息填充到 Bean 定義中。beanDefinition.setScope(beanScope)

3. 創建和修改對象時候判斷單例和原型模式

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.             // 執行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置處理方法 
  13.             bean = initializeBean(beanName, bean, beanDefinition); 
  14.         } catch (Exception e) { 
  15.             throw new BeansException("Instantiation of bean failed", e); 
  16.         } 
  17.  
  18.         // 注冊實現了 DisposableBean 接口的 Bean 對象 
  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 不執行銷毀方法 
  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.     // ... 其他功能 
  • 單例模式和原型模式的區別就在于是否存放到內存中,如果是原型模式那么就不會存放到內存中,每次獲取都重新創建對象,另外非 Singleton 類型的 Bean 不需要執行銷毀方法。
  • 所以這里的代碼會有兩處修改,一處是 createBean 中判斷是否添加到 addSingleton(beanName, bean);,另外一處是 registerDisposableBeanIfNecessary 銷毀注冊中的判斷 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個方法,獲取對象、對象類型,以及是否是單例對象,如果是單例對象依然會被放到內存中。

5. 實現一個 FactoryBean 注冊服務

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 類主要處理的就是關于 FactoryBean 此類對象的注冊操作,之所以放到這樣一個單獨的類里,就是希望做到不同領域模塊下只負責各自需要完成的功能,避免因為擴展導致類膨脹到難以維護。
  • 同樣這里也定義了緩存操作 factoryBeanObjectCache,用于存放單例類型的對象,避免重復創建。在日常使用用,基本也都是創建的單例對象
  • doGetObjectFromFactoryBean 是具體的獲取 FactoryBean#getObject() 方法,因為既有緩存的處理也有對象的獲取,所以額外提供了 getObjectFromFactoryBean 進行邏輯包裝,這部分的操作方式是不和你日常做的業務邏輯開發非常相似。從Redis取數據,如果為空就從數據庫獲取并寫入Redis

6. 擴展 AbstractBeanFactory 創建對象邏輯

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,則需要調用 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。因為需要擴展出創建 FactoryBean 對象的能力,所以這就想一個鏈條服務上,截出一個段來處理額外的服務,并把鏈條再鏈接上。
  • 此處新增加的功能主要是在 doGetBean 方法中,添加了調用 (T) getObjectForBeanInstance(sharedInstance, name) 對獲取 FactoryBean 的操作。
  • 在 getObjectForBeanInstance 方法中做具體的 instanceof 判斷,另外還會從 FactoryBean 的緩存中獲取對象,如果不存在則調用 FactoryBeanRegistrySupport#getObjectFromFactoryBean,執行具體的操作。

五、測試

1. 事先準備

cn.bugstack.springframework.test.bean.IUserDao

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

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 新修改了一個原有 UserDao 屬性為 IUserDao,后面我們會給這個屬性注入代理對象。

2. 定義 FactoryBean 對象

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.  
  • 這是一個實現接口 FactoryBean 的代理類 ProxyBeanFactory 名稱,主要是模擬了 UserDao 的原有功能,類似于 MyBatis 框架中的代理操作。
  • getObject() 中提供的就是一個 InvocationHandler 的代理對象,當有方法調用的時候,則執行代理對象的功能。

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 這個代理對象,注入到 userService 的 userDao 中。與上一章節相比,去掉了 UserDao 實現類,轉而用代理類替換

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對象調用方法 
  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. 打印十六進制哈希 
  16.     System.out.println(userService01 + " 十六進制哈希:" + Integer.toHexString(userService01.hashCode())); 
  17.     System.out.println(ClassLayout.parseInstance(userService01).toPrintable()); 
  18.  
  • 在 spring.xml 配置文件中,設置了 scope="prototype" 這樣就每次獲取到的對象都應該是一個新的對象。
  • 這里判斷對象是否為一個會看到打印的類對象的哈希值,所以我們把十六進制哈希打印出來。

測試結果

  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 十六進制哈希: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 

  • 對象后面的這一小段字符串就是16進制哈希值,在對象頭哈希值存放的結果上看,也有對應的數值。只不過這個結果是倒過來的。
  • 另外可以看到 cabb984@1b0375b3、cabb984@2f7c7260,這兩個對象的結尾16進制哈希值并不一樣,所以我們的原型模式是生效的。

5. 單元測試(代理對象)

  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. 調用代理方法 
  8.     UserService userService = applicationContext.getBean("userService", UserService.class); 
  9.     System.out.println("測試結果:" + userService.queryUserInfo()); 

關于 FactoryBean 的調用并沒有太多不一樣,因為所有的不同都已經被 spring.xml 配置進去了。當然你可以直接調用 spring.xml 配置的對象 cn.bugstack.springframework.test.bean.ProxyBeanFactory

測試結果

測試結果:你被代理了 queryUserName:小傅哥,騰訊,深圳

Process finished with exit code 0

  • 從測試結果來看,我們的代理類 ProxyBeanFactory 已經完美替換掉了 UserDao 的功能。
  • 雖然看上去這一點實現并不復雜,甚至有點簡單。但就是這樣一點點核心內容的設計了,解決了所有需要和 Spring 結合的其他框架交互鏈接問題。如果對此類內容感興趣,也可以閱讀小傅哥《中間件設計和開發》

六、總結

  • 在 Spring 框架整個開發的過程中,前期的各個功能接口類擴展的像膨脹了似的,但到后期在完善功能時,就沒有那么難了,反而深入理解后會覺得功能的補充,都比較簡單。只需要再所遇領域范圍內進行擴展相應的服務實現即可。
  • 當你仔細閱讀完關于 FactoryBean 的實現以及測試過程的使用,以后再需要使用 FactoryBean 開發相應的組件時候,一定會非常清楚它是如何創建自己的復雜 Bean 對象以及在什么時候初始化和調用的。遇到問題也可以快速的排查、定位和解決。
  • 如果你在學習的過程中感覺這些類、接口、實現、繼承,穿梭的很復雜,一時半會腦子還反應不過來。那么你最好的方式是動手去畫畫這些類關系圖,梳理下實現的結構,看看每個類在干什么。

 

責任編輯:武曉燕 來源: bugstack蟲洞棧
相關推薦

2011-03-18 09:27:00

Spring

2023-06-29 08:32:41

Bean作用域

2021-07-05 08:43:46

Spring Beanscope作用域

2010-09-27 13:21:02

DHCP作用域

2010-09-01 09:10:30

DHCP作用域

2023-09-05 08:23:56

SpringScope方法

2011-09-06 09:56:24

JavaScript

2021-03-09 08:35:51

JSS作用域前端

2019-03-13 08:00:00

JavaScript作用域前端

2021-06-03 07:55:12

技術

2021-07-01 11:56:51

JavaScript開發代碼

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

面向對象繼承接口

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事務屬性EJB事物

2021-04-14 07:52:00

Vue 作用域插槽
點贊
收藏

51CTO技術棧公眾號

久久久精品免费免费| 91久久视频| 91.com视频| 国产精品视频一二三四区| 你懂的网站在线| 国产麻豆综合| 日韩在线免费视频观看| 欧美人与性动交α欧美精品| av影院在线| 久久久久久久久久电影| 亚洲free性xxxx护士hd| 毛片视频网站在线观看| 99久久夜色精品国产亚洲狼| 亚洲精品一区二区三区蜜桃下载| 黄色高清无遮挡| av在线网址观看| 久久精品亚洲国产奇米99| 91视频国产高清| 中文字幕一区二区人妻电影| 99九九热只有国产精品| 日韩电影大片中文字幕 | 一本久久综合| 色妞欧美日韩在线| 国产福利短视频| 日韩欧洲国产| 欧美军同video69gay| 又粗又黑又大的吊av| 哥也色在线视频| 国产亚洲综合性久久久影院| 国产福利不卡| 国产乱码久久久| 久久久久中文| 韩国精品久久久999| 国产盗摄一区二区三区在线| 国产探花在线精品| 亚洲精品久久久久久下一站| 波多野结衣中文字幕在线播放| 日韩三区免费| 色一情一乱一乱一91av| 精品无码国模私拍视频| 福利在线视频网站| 国产精品女同一区二区三区| 日韩免费毛片| 免费在线国产| 久久久亚洲高清| 蜜桃狠狠色伊人亚洲综合网站| 手机av在线免费观看| 国产寡妇亲子伦一区二区| 国产日韩在线播放| 中文字幕乱码视频| 免费欧美在线视频| 国产精品成人一区| www.五月婷婷.com| 日韩和欧美的一区| 国产成人一区二区三区小说| 麻豆成人免费视频| 老牛影视一区二区三区| 清纯唯美亚洲综合| 天天干,天天干| 日韩av中文字幕一区二区三区| 欧美最猛性xxxx| 69视频免费在线观看| 亚洲欧美日韩精品一区二区| 91国产精品电影| 一区二区三区福利视频| 亚洲一区欧美二区| 国产精品成人免费电影| 成人黄色免费网| 奇米精品一区二区三区四区| 国产精品自拍小视频| 中文字幕在线视频免费| 国产在线观看一区二区| 亚洲精品免费av| 亚洲男女视频在线观看| www.欧美精品一二区| 久久艳妇乳肉豪妇荡乳av| 免费国产在线视频| 国产精品久久久久久久久晋中| 一级二级三级欧美| 午夜激情在线| 动漫精品一区二区| 天天影视综合色| 96视频在线观看欧美| 欧美电影精品一区二区| 久久国产精品无码一级毛片| 不卡日本视频| 久久69精品久久久久久国产越南| 国产一级特黄毛片| 久久久久久久欧美精品| 成人一区二区电影| 人妻无码中文字幕免费视频蜜桃| 久久久久久99久久久精品网站| 正在播放久久| 1234区中文字幕在线观看| 色哟哟国产精品免费观看| 一区二区免费av| 成人性生交大片免费看96| 亚洲性夜色噜噜噜7777| 免费成年人视频在线观看| 日韩午夜在线| 成人精品久久一区二区三区| 日本免费网站在线观看| 国产精品免费免费| 日韩中文字幕在线视频观看| 成人毛片免费| 亚洲精品久久7777777| 精品手机在线视频| 99re国产精品| 91在线观看免费网站| 日韩资源在线| 依依成人精品视频| 女人另类性混交zo| 2020最新国产精品| 最近2019年中文视频免费在线观看 | 欧美1区2区3区| 性视频1819p久久| 91丨porny丨在线中文| jizz一区二区| 999久久欧美人妻一区二区| 香蕉成人影院| 日韩精品在线视频美女| 欧美日韩免费一区二区| 日本在线播放一区二区三区| 国内成+人亚洲| 中中文字幕av在线| 欧美日韩一级二级三级| 三上悠亚ssⅰn939无码播放| 午夜av一区| 国产精品电影在线观看| 少妇荡乳情欲办公室456视频| 亚洲色图一区二区三区| 精品久久久久久久无码| 无码少妇一区二区三区| 午夜精品福利电影| 亚洲精品国产手机| 亚洲欧美另类图片小说| 日韩一级理论片| 九九综合九九| 欧美亚洲国产日本| 婷婷在线观看视频| 亚洲一区视频在线| 少妇伦子伦精品无吗| 最新精品国产| 亚洲中国色老太| 91福利国产在线观看菠萝蜜| 制服.丝袜.亚洲.另类.中文 | 污污的视频网站在线观看| 亚洲最新视频在线播放| 欧美日韩一区二区区| 欧美黄色aaaa| 99久久伊人精品影院| 18视频在线观看网站| 日韩一区二区电影网| 欧美黄片一区二区三区| 高清在线成人网| 女人帮男人橹视频播放| 凹凸成人在线| 亚洲18私人小影院| 人操人视频在线观看| 色哟哟欧美精品| 国产亚洲精品精品精品| 六月丁香综合在线视频| 一区二区成人国产精品 | 亚洲区欧洲区| 精品伦理精品一区| 黄色小说在线观看视频| 97久久精品人人做人人爽50路| 日韩中字在线观看| 亚洲精品456| 国产精品∨欧美精品v日韩精品| 国产福利免费在线观看| 欧美日韩在线电影| 欧美大片xxxx| 99久久精品免费精品国产| 男人操女人逼免费视频| 久久不见久久见免费视频7| 国产精品免费一区| 91三级在线| 日韩黄色高清视频| 中文字幕日韩第一页| 亚洲另类中文字| 亚洲av无码一区二区三区网址| 久久久久国产精品午夜一区| 亚洲一区美女| 国语一区二区三区| 日本a级片电影一区二区| 尤物网址在线观看| 精品久久久久99| 亚洲成熟少妇视频在线观看| 中文字幕一区视频| 成人一区二区三区仙踪林| 亚洲一区二区三区高清| 亚洲最大色综合成人av| 国内精品国产成人国产三级粉色| 日本成人免费在线| a级片国产精品自在拍在线播放| 亚洲精品久久久久久久久| 在线视频 中文字幕| 亚洲丰满少妇videoshd| 成人免费视频入口| 波波电影院一区二区三区| 午夜两性免费视频| 亚洲精品日本| 一本二本三本亚洲码| 亚洲综合福利| av电影成人| 看片一区二区| 5566日本婷婷色中文字幕97| 黄视频网站在线看| 亚洲欧美国产精品| 成人精品在线播放| 6080yy午夜一二三区久久| 久久艹免费视频| 亚洲综合无码一区二区| 天天摸日日摸狠狠添| 91色婷婷久久久久合中文| 男插女视频网站| 日本美女视频一区二区| 日本韩国欧美在线观看| 欧美影院一区| 日本福利视频导航| av影片在线一区| 久久国产精品久久| 高清精品xnxxcom| 亚洲自拍偷拍区| 免费日韩成人| 国产成人综合精品在线| 新版的欧美在线视频| 欧美日本中文字幕| 老司机在线视频二区| 一区二区三区黄色| 免费福利在线视频| 日韩高清欧美高清| 人妻一区二区三区四区| 日韩欧美二区三区| 国产情侣av在线| 欧美年轻男男videosbes| 欧美成人一区二区视频| 色欲综合视频天天天| 免费在线不卡视频| 天天色天天爱天天射综合| 日本熟妇乱子伦xxxx| 亚洲一线二线三线视频| 放荡的美妇在线播放| 综合av第一页| 美女视频久久久| 中文字幕一区二区三区四区| 国产精品www爽爽爽| 中文字幕精品三区| 国产精品麻豆免费版现看视频| 国产色产综合产在线视频| 国产美女免费网站| 国产日产精品一区| 亚洲欧美日韩第一页| 国产精品不卡在线| 一区二区三区影视| 亚洲精品国产视频| 久久亚洲国产成人精品性色| 亚洲韩国精品一区| 日本三级黄色大片| 欧美日韩一区二区免费在线观看| 黄色在线观看国产| 欧美在线一区二区| 91亚洲欧美激情| 日韩一卡二卡三卡| 少妇高潮一区二区三区69| 日韩精品亚洲元码| 国产视频二区在线观看| 深夜精品寂寞黄网站在线观看| 日韩欧美小视频| 久久在线精品视频| av影院在线免费观看| 欧美在线亚洲在线| 欧洲亚洲精品| av一区二区三区免费| 色橹橹欧美在线观看视频高清 | 69久久精品无码一区二区| 国产成人av电影| 精品人妻一区二区三区香蕉| 欧美国产精品一区| 天天干中文字幕| 色综合天天在线| 91精品国产综合久| 亚洲大胆人体在线| 二区在线视频| 欧美成人午夜免费视在线看片 | 粗大的内捧猛烈进出视频| av亚洲精华国产精华| 大吊一区二区三区| 亚洲大型综合色站| 中文字幕av资源| 欧美大片日本大片免费观看| 欧美女优在线观看| 九九热99久久久国产盗摄| 爱情电影社保片一区| 亚洲综合成人婷婷小说| 天海翼精品一区二区三区| 亚洲看片网站| 亚洲视频二区| 蜜桃福利午夜精品一区| 久久综合久久综合久久综合| 中国一级片在线观看| 色综合天天综合| 亚洲va欧美va| 日韩中文字幕网| 色在线视频观看| 91成人在线看| 日韩精品欧美| 大肉大捧一进一出好爽视频| 狠狠色狠狠色综合系列| 最近中文字幕在线mv视频在线| 一区二区三区中文字幕在线观看| 波多野结衣电车| 亚洲精品一区在线观看| 超碰caoporn久久| 国产精品久久久久久久久久三级| 97久久综合精品久久久综合| 亚洲人久久久| 久久激情久久| 免费日本黄色网址| 亚洲欧美国产毛片在线| 亚洲精品一区二区二区| 亚洲精品自拍偷拍| 黄色羞羞视频在线观看| 91性高湖久久久久久久久_久久99| 久久99高清| 3d动漫一区二区三区| 国产成人啪免费观看软件| 欧美爱爱免费视频| 欧美日韩色综合| 国产1区2区3区在线| 91av国产在线| 精品三级av| 岛国大片在线播放| 国产久卡久卡久卡久卡视频精品| 色www亚洲国产阿娇yao| 在线观看av一区| 欧美色视频免费| 日本一本a高清免费不卡| 欧美午夜寂寞| 成人在线观看你懂的| 成人黄色网址在线观看| 免看一级a毛片一片成人不卡| 日韩一级完整毛片| 日本孕妇大胆孕交无码| 91免费国产网站| 真实国产乱子伦精品一区二区三区| xx欧美撒尿嘘撒尿xx| 欧美激情一区三区| 中文字幕精品一区二| 自拍视频国产精品| 欧美视频第一| 99精品视频网站| 国产麻豆精品视频| 久久精品www| 亚洲国产一区二区三区在线观看 | 国产精品无码专区在线观看| 精品一区二区三区的国产在线观看| aaa毛片在线观看| 国产喷白浆一区二区三区| 最新中文字幕第一页| 久久精品国产69国产精品亚洲 | 成人无遮挡免费网站视频在线观看| 91久久精品久久国产性色也91| 亚洲国产日韩欧美在线| 日本少妇一区二区三区| 亚洲国产精品尤物yw在线观看| 天天爽夜夜爽夜夜爽| 欧美孕妇性xx| 色喇叭免费久久综合| 亚洲精品国产久| 亚洲国产欧美一区二区三区丁香婷| 天堂av2024| 国产精品第10页| 国产精品久久久久久麻豆一区软件| 伊人免费视频二| 亚洲国产毛片aaaaa无费看| 亚洲av激情无码专区在线播放| 国产精品白嫩美女在线观看| 中文字幕乱码亚洲无线精品一区 | 亚洲资源在线播放| 欧美另类交人妖| 天天操综合520| 成人性生交免费看| 亚洲综合视频在线| 国产中文字幕在线观看| 成人免费观看网址| 亚洲精品国产日韩| 女人裸体性做爰全过| 精品国产一区二区三区不卡 | 成人免费自拍视频| 日韩一级精品| 亚洲熟女少妇一区二区| 亚洲аv电影天堂网| 四虎4545www精品视频| 国风产精品一区二区| 久久久综合精品| 亚洲精品网站在线| 国产精品中文在线| 一本色道久久综合亚洲精品不卡|