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)驗誤入哦! 非喜勿入