阿里架構(gòu)師工作十年接觸過Java框架設(shè)計模式(續(xù)上文)

1-25續(xù)

四、單例設(shè)計模式(Singleton Pattern)

4.1 介紹

單例模式是一種創(chuàng)建型模式,單例模式提供一個創(chuàng)建對象的接口,但是多次調(diào)用該接口返回的是同一個實例的引用,目的是為了保證只有一個實例,并且提供一個訪問這個實例的統(tǒng)一接口。

4.2 Spring中單例bean的創(chuàng)建

Spring中默認(rèn)配置的bean的scope為singleton,也就是單例作用域。那么看看它是如何做到的。

在AbstractBeanFactory類里面的doGetBean方法:

protected Object doGetBean( final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean = null; // 解決set循環(huán)依賴 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { ... } else { ... // 創(chuàng)建單件bean. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory() { public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { ... throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } //創(chuàng)建原型bean else if (mbd.isPrototype()) { ... } //創(chuàng)建request作用域bean else { ... } } ... return bean;}

getSingleton代碼:

public Object getSingleton(String beanName, ObjectFactory singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { ... beforeSingletonCreation(beanName); ... try { singletonObject = singletonFactory.getObject(); } catch (BeanCreationException ex) { ... } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } addSingleton(beanName, singletonObject); } return (singletonObject != NULL_OBJECT ? singletonObject : null); }}protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); }}private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible(16);

可知Spring內(nèi)部四通過一個ConcurrentMap來管理單件bean的。獲取bean時候會先看看singletonObjects中是否有,有則直接返回,沒有則創(chuàng)建后放入。

看個時序圖:

Spring的bean工廠管理的單例模式管理的是多個bean實例的單例,是工廠模式管理所有的bean,而每個bean的創(chuàng)建又使用了單例模式。

4.4 使用場景

同一個jvm應(yīng)用的不同模塊需要使用同一個對象實例進(jìn)行信息共享。

需要同一個實例來生成全局統(tǒng)一的序列號

五、原型設(shè)計模式(Prototype Pattern)

5.1 介紹

相比單例設(shè)計模式,原型模式是每次創(chuàng)建一個對象,下面看下spring是如何使用原型模式的

5.2 Spring中原型bean的創(chuàng)建

創(chuàng)建原型bean需要在xml特別說明:

protected T doGetBean( final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { ... } else { ... try { ... // Create bean instance. if (mbd.isSingleton()) { ... } //創(chuàng)建原型bean else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { ... } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } }... return (T) bean;}

createBean函數(shù)里面則是根據(jù)bean定義創(chuàng)建新bean,感興趣的可以看看。

5.3 使用場景

當(dāng)有業(yè)務(wù)場景使用某個bean時候需要使用自己的一個拷貝的時候使用。

特別推薦一個Java架構(gòu)交流學(xué)習(xí)群:688583154

1、具有1-5工作經(jīng)驗的,面對目前流行的技術(shù)不知從何下手,需要突破技術(shù)瓶頸的可以加群。

2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內(nèi)進(jìn)修、跳槽拿高薪的可以加群。

3、如果沒有工作經(jīng)驗,但基礎(chǔ)非常扎實,對java工作機制,常用設(shè)計思想,常用java開發(fā)框架掌握熟練的,可以加群。

4、覺得自己很牛B,一般需求都能搞定。但是所學(xué)的知識點沒有系統(tǒng)化,很難在技術(shù)領(lǐng)域繼續(xù)突破的可以加群。

5.阿里Java架構(gòu)師,分享知識,多年工作經(jīng)驗的梳理和總結(jié),帶著大家全面、科學(xué)地建立自己的技術(shù)體系和技術(shù)認(rèn)知

六、 策略模式(Strategy Pattern)

6.1 介紹

策略模式屬于行為性模式,它定義一系列的算法對象,使用時候可以使它們相互替換。

6.2 Spring中bean實例化策略

首先看下類圖:

從圖知道:接口InstantiationStrategy是實例化策略接口類,它定義了三個實例化接口,然后SimpleInstantiationStrategy實現(xiàn)了該策略,它主要做一些簡單的根據(jù)構(gòu)造函數(shù)實例號bean的工作,然后CglibSubclassingInstantiationStrategy又繼承了SimpleInstantiationStrategy新增了方法注入方式根據(jù)cglib生成代理類實例化方法。

在AbstractAutowireCapableBeanFactory中管理了該策略的一個對象,默認(rèn)是CglibSubclassingInstantiationStrategy策略,運行時候可以通過setInstantiationStrategy改變實例化策略,如果你自己寫個個策略的話。

6.3 Spring中Aop代理策略

首先看AopProxyFactory接口類提供了createAopProxy接口,這個是策略模式的接口方法。然后DefaultAopProxyFactory實現(xiàn)了該接口作為策略的實現(xiàn)者。然后ProxyCreatorSupport里面引用了AopProxyFactory,并且提供了get,set方法用來運行時改變策略,這里Spring只實現(xiàn)了DefaultAopProxyFactory這一個策略,如果需要自己也可以寫個。

DefaultAopProxyFactory里面的createAopProxy的邏輯如下,可以在運行時根據(jù)參數(shù)決定用Cglib策略還是JDK動態(tài)代理策略生成代理類:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { //如果XML打開了優(yōu)化開關(guān),或者設(shè)置為了代理目標(biāo)類,或者目前類沒有接口 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } //如果有接口,或者通過Proxy.newProxyInstance生成的,則使用jdk動態(tài)代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } //使用cglib return new ObjenesisCglibAopProxy(config); } else { //使用jdk動態(tài)代理 return new JdkDynamicAopProxy(config); } }

另外AopProxy也是一個策略接口類,具體實現(xiàn)的策略為JdkDynamicAopProxy,CglibAopProxy,ObjenesisCglibAopProxy

6.4 Tomcat中Digester解析server.xml

tomcat中的Digester是為了解析server.xml的,其中每個元素都有一個解析規(guī)則就是Rule ,類圖如下:

DigestER一開始先指定不同的解析策略(Rule),然后在具體解析Server.xml時候根據(jù)節(jié)點不同使用不同解析策略來解析節(jié)點。

如圖在解析每個節(jié)點時候會先找到該節(jié)點對應(yīng)的解析策略,然后循環(huán)去調(diào)用所有解析策略的方法去處理

6.5 使用場景

運行時根據(jù)條件的不同使用不同的策略處理一個事情,與責(zé)任鏈不同在于,責(zé)任鏈?zhǔn)且粋€鏈條,一個事情可以被責(zé)任鏈里面所有節(jié)點處理,而 策略模式則是只有有一個對象來處理。

七、 門面模式(Facade Pattern)

7.1 介紹

門面模式是一種結(jié)構(gòu)性模式,它通過新增一個門面類對外暴露系統(tǒng)提供的一部分功能,或者屏蔽了內(nèi)部系統(tǒng)的復(fù)雜性,對外部僅僅暴露一個簡單的接口,或者通過調(diào)用不同的服務(wù)對外提供統(tǒng)一的接口,讓使用者對這些內(nèi)部服務(wù)透明化。

7.2 模板引擎Velocity中門面模式使用

Velocity里面的VelocityEngine和Velocity類都是RuntimeInstance類的門面,后者提供了模板渲染的所有功能,前兩者則是內(nèi)部維護RuntimeInstance的實例,具體工作還是委托給RuntimeInstance來實現(xiàn)。

關(guān)于Veloctiy可以參考:https://www.atatech.org/articles/78435

如圖 RuntimeInstance提供了Velocity引擎的所用功能,VelocityEngine內(nèi)部直接引用了RuntimeInstance的一個實例,VelocityEngine對外暴露的服務(wù)都是委托RuntimeInstance實現(xiàn),并且每次new一個VelocityEngine內(nèi)部都會有RuntimeInstance的一個實例被創(chuàng)建。而Velocity類調(diào)用了單例模式類RuntimeSingleton里面的方法,RuntimeSingleton又是RuntimeInstance的一個單例模式。

7.3 使用場景

當(dāng)需要對外屏蔽一個系統(tǒng)的復(fù)雜性時候可以考慮使用門面模式對外提供簡單可讀性高的接口類

當(dāng)需要對外部暴露系統(tǒng)一部分權(quán)限的接口時候可以考慮使用門面模式減少系統(tǒng)權(quán)限。

當(dāng)系統(tǒng)需要調(diào)用不同服務(wù)匯總后在對外提供服務(wù)時候可以考慮使用門面模式對外屏蔽細(xì)節(jié),之暴露一個接口。

八、裝飾器模式(Decorator Pattern)

8.1 介紹

裝飾器模式是一種結(jié)構(gòu)性模式,它作用是對對象已有功能進(jìn)行增強,但是不改變原有對象結(jié)構(gòu)。這避免了通過繼承方式進(jìn)行功能擴充導(dǎo)致的類體系臃腫。

8.2 Spring中BeanDefinitionDecorator

先看下類圖:

如圖ScopedProxyBeanDefinitionDecorator實現(xiàn)了decorate方法用來對scope作用域為request的bean定義進(jìn)行包裝。

具體時序圖為:

class ScopedProxyBeanDefinitionDecorator implements BeanDefinitionDecorator { private static final String PROXY_TARGET_CLASS = "proxy-target-class"; @Override public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) { boolean proxyTargetClass = true; if (node instanceof Element) { Element ele = (Element) node; if (ele.hasAttribute(PROXY_TARGET_CLASS)) { proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS)); } } // 創(chuàng)建scoped的代理類,并注冊到容器 BeanDefinitionHolder holder = ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass); String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName()); parserContext.getReaderContext().fireComponentRegistered( new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName)); return holder; }}

關(guān)于ScopedProxyBeanDefinitionDecorator干啥用的那:

其實就是處理

的,具體作用是包裝lavaPvgInfo的bean定義為ScopedProxyFactoryBean,作用是實現(xiàn)request作用域bean.

8.3 commons-collections包中ListUtils

如圖

ListUtils中的四個方法分別依賴list的四種裝飾器類對List功能進(jìn)行擴充和限制。

其中FixedSizeList類通過禁止add/remove操作保證list的大小固定,但是可以修改元素內(nèi)容

其中UnmodifiableList類通過禁用add,clear,remove,set,保證list的內(nèi)容不被修改

其中SynchronizedList類通過使用Lock 來保證add,set,get,remove等的同步安全

其中LazyList類則當(dāng)調(diào)用get方法發(fā)現(xiàn)list里面不存在對象時候,自動使用factory創(chuàng)建對象.

8.4 使用場景

在不改變原有類結(jié)構(gòu)基礎(chǔ)上,新增或者限制或者改造功能時候。如果你在學(xué)習(xí)Java的過程中或者在工作中遇到什么問題都可以來群里提問,阿里Java高級大牛直播講解知識點,分享知識,多年工作經(jīng)驗的梳理和總結(jié),帶著大家全面、科學(xué)地建立自己的技術(shù)體系和技術(shù)認(rèn)知!JAVA學(xué)習(xí)交流QQ群:288351179可以加群找我要課堂鏈接 注意:是免費的 沒有開發(fā)經(jīng)驗誤入哦! 非喜勿入

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,628評論 19 139
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,818評論 11 349
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,740評論 18 399
  • 很久前的照片,一直來不及發(fā)。 我們沈陽(才來幾個月就這么叫了,啊哈),有一條渾河,據(jù)說釣上過五十斤大魚,真假布吉島...
    木成語閱讀 274評論 0 1

友情鏈接更多精彩內(nèi)容