阿里架構師工作十年接觸過Java框架設計模式(續上文)

1-25續

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

4.1 介紹

單例模式是一種創建型模式,單例模式提供一個創建對象的接口,但是多次調用該接口返回的是同一個實例的引用,目的是爲了保證只有一個實例,並且提供一個訪問這個實例的統一接口。

4.2 Spring中單例bean的創建

Spring中默認配置的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循環依賴 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { ... } else { ... // 創建單件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); } //創建原型bean else if (mbd.isPrototype()) { ... } //創建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內部四通過一個ConcurrentMap來管理單件bean的。獲取bean時候會先看看singletonObjects中是否有,有則直接返回,沒有則創建後放入。

看個時序圖:

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

4.4 使用場景

同一個jvm應用的不同模塊需要使用同一個對象實例進行信息共享。

需要同一個實例來生成全局統一的序列號

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

5.1 介紹

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

5.2 Spring中原型bean的創建

創建原型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()) { ... } //創建原型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函數裏面則是根據bean定義創建新bean,感興趣的可以看看。

5.3 使用場景

當有業務場景使用某個bean時候需要使用自己的一個拷貝的時候使用。

特別推薦一個Java架構交流學習羣:688583154

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

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

3、如果沒有工作經驗,但基礎非常紮實,對java工作機制,常用設計思想,常用java開發框架掌握熟練的,可以加羣。

4、覺得自己很牛B,一般需求都能搞定。但是所學的知識點沒有系統化,很難在技術領域繼續突破的可以加羣。

5.阿里Java架構師,分享知識,多年工作經驗的梳理和總結,帶着大家全面、科學地建立自己的技術體系和技術認知

六、 策略模式(Strategy Pattern)

6.1 介紹

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

6.2 Spring中bean實例化策略

首先看下類圖:

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

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

6.3 Spring中Aop代理策略

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

DefaultAopProxyFactory裏面的createAopProxy的邏輯如下,可以在運行時根據參數決定用Cglib策略還是JDK動態代理策略生成代理類:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { //如果XML打開了優化開關,或者設置爲了代理目標類,或者目前類沒有接口 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動態代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } //使用cglib return new ObjenesisCglibAopProxy(config); } else { //使用jdk動態代理 return new JdkDynamicAopProxy(config); } }

另外AopProxy也是一個策略接口類,具體實現的策略爲JdkDynamicAopProxy,CglibAopProxy,ObjenesisCglibAopProxy

6.4 Tomcat中Digester解析server.xml

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

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

如圖在解析每個節點時候會先找到該節點對應的解析策略,然後循環去調用所有解析策略的方法去處理

6.5 使用場景

運行時根據條件的不同使用不同的策略處理一個事情,與責任鏈不同在於,責任鏈是一個鏈條,一個事情可以被責任鏈裏面所有節點處理,而 策略模式則是隻有有一個對象來處理。

七、 門面模式(Facade Pattern)

7.1 介紹

門面模式是一種結構性模式,它通過新增一個門面類對外暴露系統提供的一部分功能,或者屏蔽了內部系統的複雜性,對外部僅僅暴露一個簡單的接口,或者通過調用不同的服務對外提供統一的接口,讓使用者對這些內部服務透明化。

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

Velocity裏面的VelocityEngine和Velocity類都是RuntimeInstance類的門面,後者提供了模板渲染的所有功能,前兩者則是內部維護RuntimeInstance的實例,具體工作還是委託給RuntimeInstance來實現。

關於Veloctiy可以參考:https://www.atatech.org/articles/78435

如圖 RuntimeInstance提供了Velocity引擎的所用功能,VelocityEngine內部直接引用了RuntimeInstance的一個實例,VelocityEngine對外暴露的服務都是委託RuntimeInstance實現,並且每次new一個VelocityEngine內部都會有RuntimeInstance的一個實例被創建。而Velocity類調用了單例模式類RuntimeSingleton裏面的方法,RuntimeSingleton又是RuntimeInstance的一個單例模式。

7.3 使用場景

當需要對外屏蔽一個系統的複雜性時候可以考慮使用門面模式對外提供簡單可讀性高的接口類

當需要對外部暴露系統一部分權限的接口時候可以考慮使用門面模式減少系統權限。

當系統需要調用不同服務彙總後在對外提供服務時候可以考慮使用門面模式對外屏蔽細節,之暴露一個接口。

八、裝飾器模式(Decorator Pattern)

8.1 介紹

裝飾器模式是一種結構性模式,它作用是對對象已有功能進行增強,但是不改變原有對象結構。這避免了通過繼承方式進行功能擴充導致的類體系臃腫。

8.2 Spring中BeanDefinitionDecorator

先看下類圖:

如圖ScopedProxyBeanDefinitionDecorator實現了decorate方法用來對scope作用域爲request的bean定義進行包裝。

具體時序圖爲:

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)); } } // 創建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; }}

關於ScopedProxyBeanDefinitionDecorator幹啥用的那:

其實就是處理

的,具體作用是包裝lavaPvgInfo的bean定義爲ScopedProxyFactoryBean,作用是實現request作用域bean.

8.3 commons-collections包中ListUtils

如圖

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

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

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

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

其中LazyList類則當調用get方法發現list裏面不存在對象時候,自動使用factory創建對象.

8.4 使用場景

在不改變原有類結構基礎上,新增或者限制或者改造功能時候。如果你在學習Java的過程中或者在工作中遇到什麼問題都可以來羣裏提問,阿里Java高級大牛直播講解知識點,分享知識,多年工作經驗的梳理和總結,帶着大家全面、科學地建立自己的技術體系和技術認知!JAVA學習交流QQ羣:288351179可以加羣找我要課堂鏈接 注意:是免費的 沒有開發經驗誤入哦! 非喜勿入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章