分享我在阿里工作十年接觸過Java框架設(shè)計(jì)模式

一陋桂、前言

說起來設(shè)計(jì)模式逆趣,大家應(yīng)該都耳熟能詳,設(shè)計(jì)模式代表了軟件設(shè)計(jì)的最佳實(shí)踐嗜历,是經(jīng)過不斷總結(jié)提煉出來的代碼設(shè)計(jì)經(jīng)驗(yàn)的分類總結(jié)宣渗,這些模式或者可以簡化代碼,或者可以是代碼邏輯開起來清晰梨州,或者對(duì)功能擴(kuò)展很方便…落包。

設(shè)計(jì)模式按照使用場景可以分為三大類:創(chuàng)建型模式(Creational Patterns)、結(jié)構(gòu)型模式(Structural Patterns)摊唇、行為型模式(Behavioral Patterns)咐蝇。

  • 創(chuàng)建型模式(Creational Patterns)
    對(duì)對(duì)象的實(shí)例化過程進(jìn)行抽象,這使得一個(gè)系統(tǒng)可以不用關(guān)心這些對(duì)象是如何創(chuàng)建巷查,組合有序,呈現(xiàn)的,對(duì)于類創(chuàng)建模式來說通過使用繼承改變實(shí)例化的類岛请,對(duì)于對(duì)象創(chuàng)建模式來說通過使用代理來實(shí)例化所需要的對(duì)象旭寿。

  • 結(jié)構(gòu)型模式(Structural Patterns)
    通過對(duì)多個(gè)類和對(duì)象進(jìn)行組合得到復(fù)雜結(jié)構(gòu)的類,一般使用繼承繼承或者成員變量引用形式來實(shí)現(xiàn)崇败。

  • 行為型模式(Behavioral Patterns)
    行為模式不僅表達(dá)了對(duì)象和類盅称,還表達(dá)了他們之間的交互,涉及到了對(duì)象和算法的分配后室。

image.png

下面就帶大家看下開源框架框架中是如何應(yīng)用這些經(jīng)典設(shè)計(jì)模式的缩膝。

二、責(zé)任鏈設(shè)計(jì)模式(Chain of Responsibility Pattern)

2.1 介紹

責(zé)任鏈模式是把多個(gè)對(duì)象串聯(lián)起來形成一個(gè)鏈狀結(jié)構(gòu)岸霹,讓每個(gè)對(duì)象都有機(jī)會(huì)對(duì)事件發(fā)送者的請(qǐng)求進(jìn)行處理疾层。責(zé)任鏈模式是設(shè)計(jì)模式中的行為模式,設(shè)計(jì)意圖是為了使事件發(fā)送者和事件接受者之間解耦贡避。通常責(zé)任鏈鏈中的每個(gè)對(duì)象都有下一個(gè)對(duì)象的引入(例如tomcat 里面StandardPipeline用來管理valve)痛黎,或者有個(gè)同一個(gè)鏈管理工廠里面使用數(shù)組存放了所有的對(duì)象(例如tomcat里面ApplicationFilterChain用來關(guān)系filter)。

2.2 Tomcat中Valve鏈

Tomcat中StandardEngine,StandardHost,StandardContext里面都有自己StandardPipeline,下面以StandardEngine里面StandardPipeline為例講解

image.png

從上面類圖可知道每個(gè)Valve都要繼承ValveBase類刮吧,該類里面有一個(gè)Valve的引用湖饱,實(shí)際是鏈中下一個(gè)節(jié)點(diǎn)對(duì)象,Valve就是通過每個(gè)Valve里面的next串聯(lián)為鏈的杀捻。

image.png

每個(gè)valve的invoke方法里面調(diào)用next.invoke激活鏈中下一個(gè)節(jié)點(diǎn),并且StandardEngine,StandardHost,StandardContext都有一個(gè)basic valve這個(gè)valve在鏈的末尾用來激活子容器的valve鏈井厌。

2.3 Tomcat中Filter鏈

Tomcat中Filter鏈?zhǔn)鞘褂肁pplicationFilterChain來管理的,具體結(jié)構(gòu)如下圖:

image.png

可知Filter鏈不是像Valve一樣在內(nèi)部維護(hù)下個(gè)節(jié)點(diǎn)的引用,而是在ApplicationFilterChain中搞了個(gè)數(shù)組存放所有的Filter旗笔,并通過n統(tǒng)計(jì)Filter總個(gè)數(shù)彪置,pos是當(dāng)前filter的下標(biāo)。

ApplicationFilterChain的doFilter代碼如下:

public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
        ...
        internalDoFilter(request,response);
        ...
    }

private void internalDoFilter(ServletRequest request, 
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {

            //獲取filter鏈中下標(biāo)為pos的filter
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            try {
                filter = filterConfig.getFilter();
                support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
                                          filter, request, response);

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                            Boolean.FALSE);
                }

                ...

                //調(diào)用自定義filter的dofilter方法
                filter.doFilter(request, response, this);

                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                          filter, request, response);
            } 
            ....

    }
   .....
}

注:這兩種方式的區(qū)別是啥,就是說那些場景下使用2.2蝇恶,什么情況下使用2.3這個(gè)目前還沒有搞清楚有
知道的麻煩在本帖留言幫我解惑下^^

2.4 使用場景

  • 當(dāng)一個(gè)請(qǐng)求需要
    根據(jù)請(qǐng)求參數(shù)的不同由不同對(duì)象來處理時(shí)候拳魁。
  • 當(dāng)一個(gè)請(qǐng)求需要固定對(duì)象順序處理,并且可擴(kuò)展性的在固定順序里面插入新的對(duì)象進(jìn)行處理時(shí)候撮弧。

三潘懊、工廠模式(Factory Pattern)

3.1 介紹

工廠模式是創(chuàng)建型模式,他封裝了對(duì)象的創(chuàng)建過程贿衍,調(diào)用者使用具體的工廠方法根據(jù)參數(shù)就可以獲取對(duì)應(yīng)的對(duì)象授舟。

3.2 Spring框架中BeanFactory

image.png

如圖BeanFactory接口提供了getBean方法,在AbstractBeanFactory中實(shí)現(xiàn)了該方法贸辈,經(jīng)過層層繼承释树,實(shí)現(xiàn),最后DefaultListableBeanFactory實(shí)現(xiàn)了BeanDefinitionRegistry接口用來保存bean定義擎淤,繼承了AbstractAutowireCapableBeanFactory用來支撐autowired奢啥。

一個(gè)例子

@Test
public void testBeanFactoy() throws NamingException, SQLException, ParseException, IOException {

    //創(chuàng)建Bean工廠
    DefaultListableBeanFactory bf = new DefaultListableBeanFactory();

    //給bean工廠添加bean定義,解析xml里面的bean放入bean工廠
    loadBeanDefinitions(bf);

    //根據(jù)名字從bean工廠獲取bean
    Hello hello = (Hello) bf.getBean("hello");
    hello.sayHello();

    Hello2 hello2 = (Hello2) bf.getBean("hello2");
    hello2.sayHello();

}

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    String[] configLocations = new String[] { "beans2.xml" };
    if (configLocations != null) {
        beanDefinitionReader.loadBeanDefinitions(configLocations);
    }
}

3.3 使用場景

  • 不同條件下創(chuàng)建不同實(shí)例,用于統(tǒng)一管理bean
  • 不同條件下調(diào)用不同工廠方法獲取不同場景下的bean

四嘴拢、單例設(shè)計(jì)模式(Singleton Pattern)

4.1 介紹

單例模式是一種創(chuàng)建型模式桩盲,單例模式提供一個(gè)創(chuàng)建對(duì)象的接口,但是多次調(diào)用該接口返回的是同一個(gè)實(shí)例的引用席吴,目的是為了保證只有一個(gè)實(shí)例赌结,并且提供一個(gè)訪問這個(gè)實(shí)例的統(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)部四通過一個(gè)ConcurrentMap來管理單件bean的迈倍。獲取bean時(shí)候會(huì)先看看singletonObjects中是否有伤靠,有則直接返回,沒有則創(chuàng)建后放入啼染。

看個(gè)時(shí)序圖:


image.png

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

4.4 使用場景

  • 同一個(gè)jvm應(yīng)用的不同模塊需要使用同一個(gè)對(duì)象實(shí)例進(jìn)行信息共享迹鹅。
  • 需要同一個(gè)實(shí)例來生成全局統(tǒng)一的序列號(hào)

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

5.1 介紹

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

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

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

    <bean id="hello" class="com.zlx.demo.Hello" scope="prototype"/>

protected <T> T doGetBean(
        final String name, final Class<T> 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ù)場景使用某個(gè)bean時(shí)候需要使用自己的一個(gè)拷貝的時(shí)候使用弟蚀。
    特別推薦一個(gè)Java架構(gòu)交流學(xué)習(xí)群:688583154
    1、具有1-5工作經(jīng)驗(yàn)的,面對(duì)目前流行的技術(shù)不知從何下手悉盆,需要突破技術(shù)瓶頸的可以加群氓英。
    2、在公司待久了捶闸,過得很安逸夜畴,但跳槽時(shí)面試碰壁。需要在短時(shí)間內(nèi)進(jìn)修删壮、跳槽拿高薪的可以加群贪绘。
    3、如果沒有工作經(jīng)驗(yàn)央碟,但基礎(chǔ)非常扎實(shí)税灌,對(duì)java工作機(jī)制,常用設(shè)計(jì)思想亿虽,常用java開發(fā)框架掌握熟練的菱涤,可以加群。
    4经柴、覺得自己很牛B狸窘,一般需求都能搞定。但是所學(xué)的知識(shí)點(diǎn)沒有系統(tǒng)化坯认,很難在技術(shù)領(lǐng)域繼續(xù)突破的可以加群翻擒。
    5.阿里Java架構(gòu)師,分享知識(shí)牛哺,多年工作經(jīng)驗(yàn)的梳理和總結(jié)陋气,帶著大家全面、科學(xué)地建立自己的技術(shù)體系和技術(shù)認(rèn)知

六引润、 策略模式(Strategy Pattern)

6.1 介紹

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

6.2 Spring中bean實(shí)例化策略

首先看下類圖:

image.png

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

在AbstractAutowireCapableBeanFactory中管理了該策略的一個(gè)對(duì)象,默認(rèn)是CglibSubclassingInstantiationStrategy策略洽糟,運(yùn)行時(shí)候可以通過setInstantiationStrategy改變實(shí)例化策略炉菲,如果你自己寫個(gè)個(gè)策略的話堕战。

6.3 Spring中Aop代理策略

image.png

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

DefaultAopProxyFactory里面的createAopProxy的邏輯如下吠裆,可以在運(yùn)行時(shí)根據(jù)參數(shù)決定用Cglib策略還是JDK動(dòng)態(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動(dòng)態(tài)代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }

            //使用cglib
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            //使用jdk動(dòng)態(tài)代理
            return new JdkDynamicAopProxy(config);
        }
    }

另外AopProxy也是一個(gè)策略接口類,具體實(shí)現(xiàn)的策略為JdkDynamicAopProxy祝旷,CglibAopProxy履澳,ObjenesisCglibAopProxy

6.4 Tomcat中Digester解析server.xml

tomcat中的Digester是為了解析server.xml的,其中每個(gè)元素都有一個(gè)解析規(guī)則就是Rule ,類圖如下:
DigestER一開始先指定不同的解析策略(Rule)怀跛,然后在具體解析Server.xml時(shí)候根據(jù)節(jié)點(diǎn)不同使用不同解析策略來解析節(jié)點(diǎn)距贷。

image.png

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

6.5 使用場景

  • 運(yùn)行時(shí)根據(jù)條件的不同使用不同的策略處理一個(gè)事情忠蝗,與責(zé)任鏈不同在于,責(zé)任鏈?zhǔn)且粋€(gè)鏈條漓拾,一個(gè)事情可以被責(zé)任鏈里面所有節(jié)點(diǎn)處理阁最,而 策略模式則是只有有一個(gè)對(duì)象來處理。

七骇两、 門面模式(Facade Pattern)

7.1 介紹

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

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

Velocity里面的VelocityEngine和Velocity類都是RuntimeInstance類的門面棋傍,后者提供了模板渲染的所有功能,前兩者則是內(nèi)部維護(hù)RuntimeInstance的實(shí)例难审,具體工作還是委托給RuntimeInstance來實(shí)現(xiàn)舍沙。
關(guān)于Veloctiy可以參考:https://www.atatech.org/articles/78435

image.png

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

7.3 使用場景

  • 當(dāng)需要對(duì)外屏蔽一個(gè)系統(tǒng)的復(fù)雜性時(shí)候可以考慮使用門面模式對(duì)外提供簡單可讀性高的接口類
  • 當(dāng)需要對(duì)外部暴露系統(tǒng)一部分權(quán)限的接口時(shí)候可以考慮使用門面模式減少系統(tǒng)權(quán)限。
  • 當(dāng)系統(tǒng)需要調(diào)用不同服務(wù)匯總后在對(duì)外提供服務(wù)時(shí)候可以考慮使用門面模式對(duì)外屏蔽細(xì)節(jié)地淀,之暴露一個(gè)接口失球。

八、裝飾器模式(Decorator Pattern)

8.1 介紹

裝飾器模式是一種結(jié)構(gòu)性模式帮毁,它作用是對(duì)對(duì)象已有功能進(jìn)行增強(qiáng)实苞,但是不改變?cè)袑?duì)象結(jié)構(gòu)。這避免了通過繼承方式進(jìn)行功能擴(kuò)充導(dǎo)致的類體系臃腫烈疚。

8.2 Spring中BeanDefinitionDecorator

先看下類圖:

image.png

如圖ScopedProxyBeanDefinitionDecorator實(shí)現(xiàn)了decorate方法用來對(duì)scope作用域?yàn)閞equest的bean定義進(jìn)行包裝黔牵。
具體時(shí)序圖為:

image.png
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的代理類,并注冊(cè)到容器
        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干啥用的那:


  <bean id="lavaPvgInfo" class="com.alibaba.lava.privilege.PrivilegeInfo"
        scope="request">
        <property name="aesKey" value="666" />
        <aop:scoped-proxy />
    </bean>

其實(shí)就是處理<aop:scoped-proxy />
的爷肝,具體作用是包裝lavaPvgInfo的bean定義為ScopedProxyFactoryBean猾浦,作用是實(shí)現(xiàn)request作用域bean.

8.3 commons-collections包中ListUtils

image.png

如圖

ListUtils中的四個(gè)方法分別依賴list的四種裝飾器類對(duì)List功能進(jìn)行擴(kuò)充和限制。
其中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里面不存在對(duì)象時(shí)候金赦,自動(dòng)使用factory創(chuàng)建對(duì)象.

8.4 使用場景

  • 在不改變?cè)蓄惤Y(jié)構(gòu)基礎(chǔ)上,新增或者限制或者改造功能時(shí)候对嚼。

九夹抗、適配器模式(Adapter Pattern)

9.1 介紹

適配器模式屬于結(jié)構(gòu)性模式,它為兩個(gè)不同接口之間互通提供了一種手段纵竖。

9.2 Spring中MethodInterceptor適配器

在Spring Aop框架中漠烧,MethodInterceptor接口被用來攔截指定的方法,對(duì)方法進(jìn)行增強(qiáng)磨确。

image.png

大家都知道在Aop中每個(gè)advistor 里面會(huì)有一個(gè)advice具體做切面動(dòng)作沽甥,Spring提供了AspectJAfterReturningAdvice,AspectJMethodBeforeAdvice乏奥,AspectJAroundAdvice摆舟,AspectJAfterAdvice這幾個(gè)advice,在XML 配置aop時(shí)候會(huì)指定<aop:after-returning/>,<aop:before/>,<aop:around/>,<aop:after/>,其實(shí)內(nèi)部就是創(chuàng)建上面對(duì)應(yīng)的這些advice。

從圖知道AspectJAfterReturningAdvice和AspectJMethodBeforeAdvice沒有實(shí)現(xiàn)MethodInterceptor接口邓了,其他兩者則實(shí)現(xiàn)了該接口恨诱。而Spring Aop的方法攔截器卻必須是實(shí)現(xiàn)了MethodInterceptor的,所以Spring提供了對(duì)應(yīng)的適配器來適配這個(gè)問題,分別是MethodBeforeAdviceAdapter和AfterReturningAdviceAdapter和ThrowsAdviceAdapter骗炉。

image.png

看下DefaultAdvisorAdapterRegistry的 getInterceptors方法:

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);

    //從advistor中獲取advice
    Advice advice = advisor.getAdvice();

    //如果實(shí)現(xiàn)了MethodInterceptor則直接加入照宝,比如AspectJAroundAdvice,AspectJAfterAdvice
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }

    //否者看是否有當(dāng)前advice的適配器句葵,首先檢驗(yàn)是否支持厕鹃,支持則返回對(duì)應(yīng)的適配器
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}

以MethodBeforeAdviceAdapter為例子看下:

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

    private MethodBeforeAdvice advice;

    /**
     * Create a new MethodBeforeAdviceInterceptor for the given advice.
     * @param advice the MethodBeforeAdvice to wrap
     */
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }

}

可知MethodBeforeAdviceInterceptor繼承了MethodInterceptor作為了一個(gè)適配器內(nèi)部委托請(qǐng)求給MethodBeforeAdvice兢仰。

9.3 使用場景

  • 兩個(gè)系統(tǒng)交互時(shí)候由于接口參數(shù)不一樣沒辦法直接對(duì)接,則可以搞個(gè)適配器接口做參數(shù)轉(zhuǎn)換剂碴。

  • 適配器模式經(jīng)常是在一個(gè)系統(tǒng)或者設(shè)計(jì)已經(jīng)定型時(shí)候用的把将,而不是在初始設(shè)計(jì)時(shí)候。一般是因?yàn)椴挥绊懍F(xiàn)在業(yè)務(wù)情況下忆矛,通過適配方式統(tǒng)一接口

十察蹲、模板設(shè)計(jì)模式(Template Pattern)

10.1 前言

模板設(shè)計(jì)模式是一種行為設(shè)計(jì)模式,它使用一個(gè)抽象類定義了一個(gè)模板催训,這個(gè)模板里面定義了一系列的接口洽议,子類則只需要繼承該抽象類并且根據(jù)需要重寫一部分接口。

10.2 ibatis2中AbstractDAOTemplate

image.png

如圖AbstractDAOTemplate是抽象模板類漫拭,里面定義了configure方法亚兄,configure方法里面定義了好多protected方法,其中就有些是抽象方法嫂侍。類SpringDAOTemplate儿捧,IbatisDAOTemplate,GenericCIDAOTemplate挑宠,GenericSIDAOTemplate則繼承了AbstractDAOTemplate類并重寫了一部分方法菲盾。

10.3 Tomcat中Digester里面的Rule

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

image.png

如圖:Rule是抽象類各淀,對(duì)于每個(gè)解析的節(jié)點(diǎn)來說Rule提供了解析所需所有的方法懒鉴,而他的子類則根據(jù)自己的特殊性重寫一部分方法來支持自己的特性。

10.4 Tomcat中Endpoint

image.png

如圖AbstractEndpoint是個(gè)抽象類碎浇,定義了Endpoint的所有接口临谱,然后JIoEndpoint繼承了該類并且重寫了一部分重要的方法實(shí)現(xiàn)了BIO方式endpoint,NioEndpoint則重寫了方法實(shí)現(xiàn)了NIO的endpoint.

10.5使用場景

  • 當(dāng)多個(gè)子類具有共同的操作流程邏輯,并且其中某些流程節(jié)點(diǎn)操作需要自己定制化時(shí)候奴璃。

十一悉默、 建造者模式(Builder Pattern)

11.1 前言

建造者模式是一種創(chuàng)建型模式,將一個(gè)復(fù)制對(duì)象的創(chuàng)建屏蔽到接口內(nèi)部苟穆,用戶使用時(shí)候只需要傳遞固定的參數(shù)抄课,內(nèi)部就會(huì)執(zhí)行復(fù)雜邏輯后返回會(huì)用戶需要的對(duì)象,用戶不需要知道創(chuàng)建的細(xì)節(jié)雳旅。

11.2 Mybatis中的SqlSessionFactoryBuilder

image.png

如圖mybaits中的SqlSessionFactoryBuilder就是典型的創(chuàng)建者模式跟磨,他內(nèi)部有多個(gè)build方法,根據(jù)參數(shù)的不同創(chuàng)建出SqlSessionFactory對(duì)象攒盈,使用者只需要傳遞具體參數(shù)而不用關(guān)系內(nèi)部是如何創(chuàng)建出需要的對(duì)象的抵拘。SqlSessionFactoryBean大家應(yīng)該很熟悉,在xml里面配置的型豁。

11.3 使用場景

  • 當(dāng)一個(gè)對(duì)象比較復(fù)雜并且容易出錯(cuò)時(shí)候僵蛛,可以考慮這種模式去屏蔽創(chuàng)造細(xì)節(jié)尚蝌。

十二、 觀察者模式(Observer Pattern)

12.1 前言

觀察者模式是一種行為模式墩瞳,它定義了當(dāng)一個(gè)對(duì)象的狀態(tài)或者屬性發(fā)生變化時(shí)候驼壶,通通知其他對(duì)這些狀態(tài)感興趣的對(duì)象。觀察者模式也叫發(fā)布-訂閱模式喉酌,就是說當(dāng)你訂閱了摸一個(gè)主體時(shí)候,如果發(fā)布者改變了主題內(nèi)容泵喘,那么所有訂閱這個(gè)主體者都會(huì)受到通知泪电。
特別推薦一個(gè)Java架構(gòu)交流學(xué)習(xí)群:688583154
1、具有1-5工作經(jīng)驗(yàn)的纪铺,面對(duì)目前流行的技術(shù)不知從何下手相速,需要突破技術(shù)瓶頸的可以加群。
2鲜锚、在公司待久了突诬,過得很安逸,但跳槽時(shí)面試碰壁芜繁。需要在短時(shí)間內(nèi)進(jìn)修旺隙、跳槽拿高薪的可以加群。
3骏令、如果沒有工作經(jīng)驗(yàn)蔬捷,但基礎(chǔ)非常扎實(shí),對(duì)java工作機(jī)制榔袋,常用設(shè)計(jì)思想周拐,常用java開發(fā)框架掌握熟練的,可以加群凰兑。
4妥粟、覺得自己很牛B,一般需求都能搞定吏够。但是所學(xué)的知識(shí)點(diǎn)沒有系統(tǒng)化勾给,很難在技術(shù)領(lǐng)域繼續(xù)突破的可以加群。
5.阿里Java架構(gòu)師稿饰,分享知識(shí)锦秒,多年工作經(jīng)驗(yàn)的梳理和總結(jié),帶著大家全面喉镰、科學(xué)地建立自己的技術(shù)體系和技術(shù)認(rèn)知

12.2 Spring中ApplicationListener

image.png

如圖 黃色部分的listener們可以認(rèn)為是訂閱者旅择,紅色的context是發(fā)布者,context在IOC不同狀態(tài)會(huì)給這些訂閱者發(fā)布不同的消息通知訂閱者容器狀態(tài)侣姆。藍(lán)色的為具體的事件(這里為容器不同狀態(tài))生真,其中ContextRefreshedEvent是IOC刷新完成(也就是bean解析完成沉噩,創(chuàng)建完畢并且autowired完成)后的事件這個(gè)經(jīng)常用。
柱蟀。這里context并不是直接來管理黃色的listener訂閱者的川蒙,而是委托給了綠色的部分,該部分是可以增加刪除訂閱者长已,并且發(fā)布事件給訂閱者畜眨。

其實(shí)Tomact中的Lifecycle也是這種機(jī)制,這里不再贅述术瓮。

12.3 使用場景

  • 滿足發(fā)布-訂閱條件的康聂,當(dāng)一個(gè)對(duì)象狀態(tài)或者屬性變化,需要把這種變化通知到訂閱者時(shí)候胞四。

十三恬汁、命令模式(Command Pattern)

13.1 介紹

命令模式是一種行為模式,通過把命令封裝為一個(gè)對(duì)象辜伟,命令發(fā)送者把命令對(duì)象發(fā)出后氓侧,就不去管是誰來接受處理這個(gè)命令,命令接受者接受到命令對(duì)象后進(jìn)行處理导狡,也不用管命令是誰發(fā)出的约巷,所以命令模式實(shí)現(xiàn)了發(fā)送者與接受者之間的解耦,而具體把命令發(fā)送給誰還需要一個(gè)控制器烘豌。

13.2 Tomcat中命令模式

tomcat作為一個(gè)服務(wù)器本身會(huì)接受外部大量請(qǐng)求载庭,當(dāng)一個(gè)請(qǐng)求過來后tomcat根據(jù)域名去找對(duì)應(yīng)的host,找到host后會(huì)根據(jù)應(yīng)用名去找具體的context(應(yīng)用),然后具體應(yīng)用處理請(qǐng)求廊佩。對(duì)于具體host來說他不關(guān)心這個(gè)請(qǐng)求是誰給的囚聚,對(duì)應(yīng)請(qǐng)求來說他不必關(guān)心誰來處理,但是兩者是通過request封裝請(qǐng)求對(duì)象進(jìn)行關(guān)聯(lián)起來标锄。

image.png

tomcat中Connector作為命令發(fā)出者顽铸,Connector接受到請(qǐng)求后把請(qǐng)求內(nèi)容封裝為request對(duì)象(命令對(duì)象),然后使用CoyoteAdapter作為分發(fā)器把請(qǐng)求具體發(fā)配到具體的host,host在根據(jù)request對(duì)象找到具體的context,至此找到了具體的應(yīng)用料皇,交給具體應(yīng)用處理谓松。

另外對(duì)于使用springmvc的應(yīng)用來說,上面找到具體應(yīng)用践剂,但是具體交給那個(gè)controller來處理那鬼譬,這是不是也是命令模式的使用那。

13.3 使用場景

  • 當(dāng)事件發(fā)送者和接受者直接需要完全解耦(直接并不存在引用關(guān)系)時(shí)候逊脯。
    十四优质、總結(jié)
    設(shè)計(jì)模式中每一個(gè)模式都描述了在我們工作中不斷重復(fù)發(fā)生的問題,以及問題的解決方案,所以真正掌握設(shè)計(jì)模式可以避免我們做不必要的重復(fù)勞動(dòng)巩螃。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末演怎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子避乏,更是在濱河造成了極大的恐慌爷耀,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拍皮,死亡現(xiàn)場離奇詭異歹叮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)春缕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門盗胀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锄贼,你說我怎么就攤上這事∨В” “怎么了宅荤?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長浸策。 經(jīng)常有香客問我冯键,道長,這世上最難降的妖魔是什么庸汗? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任惫确,我火速辦了婚禮,結(jié)果婚禮上蚯舱,老公的妹妹穿的比我還像新娘改化。我一直安慰自己,他們只是感情好枉昏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布陈肛。 她就那樣靜靜地躺著,像睡著了一般兄裂。 火紅的嫁衣襯著肌膚如雪句旱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天晰奖,我揣著相機(jī)與錄音谈撒,去河邊找鬼。 笑死匾南,一個(gè)胖子當(dāng)著我的面吹牛啃匿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播午衰,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼立宜,長吁一口氣:“原來是場噩夢啊……” “哼冒萄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起橙数,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤尊流,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后灯帮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崖技,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年钟哥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迎献。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腻贰,死狀恐怖吁恍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情播演,我是刑警寧澤冀瓦,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站写烤,受9級(jí)特大地震影響翼闽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜洲炊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一感局、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暂衡,春花似錦询微、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至隧膘,卻和暖如春代态,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疹吃。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工蹦疑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萨驶。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓歉摧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叁温,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理再悼,服務(wù)發(fā)現(xiàn),斷路器膝但,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,810評(píng)論 6 342
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法冲九,類相關(guān)的語法,內(nèi)部類的語法跟束,繼承相關(guān)的語法莺奸,異常的語法,線程的語...
    子非魚_t_閱讀 31,631評(píng)論 18 399
  • 光陰荏苒冀宴,倏忽已到而立之年灭贷。回首前塵略贮,歷歷在目甚疟。展望未來,壯志未酬逃延。 背上的行囊不知不覺沉重了許多古拴,酸甜苦...
    四合道人閱讀 1,065評(píng)論 4 4
  • 西漢著名將領(lǐng)韓信,從小勤奮讀書紧帕,學(xué)習(xí)武藝盔然。秦朝末年,他聽說劉邦起義了是嗜,就腰懸佩劍愈案,準(zhǔn)備離開故鄉(xiāng)去投靠劉...
    朱莉葉6666閱讀 229評(píng)論 0 1