IT技术专题野蛮生长@IT·互联网

常用开源框架中设计模式使用分析(全)

2017-05-30  本文已影响1589人  阿里加多

一、前言

说起来设计模式,大家应该都耳熟能详,设计模式代表了软件设计的最佳实践,是经过不断总结提炼出来的代码设计经验的分类总结,这些模式或者可以简化代码,或者可以是代码逻辑开起来清晰,或者对功能扩展很方便...。

阿里巴巴长期招聘Java研发工程师p6,p7,p8等上不封顶级别,有意向的可以发简历给我,注明想去的部门和工作地点:1064454834@qq.com_

设计模式按照使用场景可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。

image.png

下面就带大家看下开源框架框架中是如何应用这些经典设计模式的。

二、责任链设计模式(Chain of Responsibility Pattern)

2.1 介绍

责任链模式是把多个对象串联起来形成一个链状结构,让每个对象都有机会对事件发送者的请求进行处理。责任链模式是设计模式中的行为模式,设计意图是为了使事件发送者和事件接受者之间解耦。通常责任链链中的每个对象都有下一个对象的引入(例如tomcat 里面StandardPipeline用来管理valve),或者有个同一个链管理工厂里面使用数组存放了所有的对象(例如tomcat里面ApplicationFilterChain用来关系filter)。

2.2 Tomcat中Valve链

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

image.png

从上面类图可知道每个Valve都要继承ValveBase类,该类里面有一个Valve的引用,实际是链中下一个节点对象,Valve就是通过每个Valve里面的next串联为链的。

image.png

每个valve的invoke方法里面调用next.invoke激活链中下一个节点,并且StandardEngine,StandardHost,StandardContext都有一个basic valve这个valve在链的末尾用来激活子容器的valve链。

2.3 Tomcat中Filter链

Tomcat中Filter链是使用ApplicationFilterChain来管理的,具体结构如下图:

image.png

可知Filter链不是像Valve一样在内部维护下个节点的引用,而是在ApplicationFilterChain中搞了个数组存放所有的Filter,并通过n统计Filter总个数,pos是当前filter的下标。

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链中下标为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);
                }
                
                ...

                //调用自定义filter的dofilter方法
                filter.doFilter(request, response, this);
                

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

    }
   .....
}

注:这两种方式的区别是啥,就是说那些场景下使用2.2,什么情况下使用2.3这个目前还没有搞清楚有
知道的麻烦在本帖留言帮我解惑下^^

2.4 使用场景

三、工厂模式(Factory Pattern)

3.1 介绍

工厂模式是创建型模式,他封装了对象的创建过程,调用者使用具体的工厂方法根据参数就可以获取对应的对象。

3.2 Spring框架中BeanFactory

image.png

如图BeanFactory接口提供了getBean方法,在AbstractBeanFactory中实现了该方法,经过层层继承,实现,最后DefaultListableBeanFactory实现了BeanDefinitionRegistry接口用来保存bean定义,继承了AbstractAutowireCapableBeanFactory用来支撑autowired。

一个例子

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

    //创建Bean工厂
    DefaultListableBeanFactory bf = new DefaultListableBeanFactory();

    //给bean工厂添加bean定义,解析xml里面的bean放入bean工厂
    loadBeanDefinitions(bf);

    //根据名字从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 使用场景

四、单例设计模式(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中是否有,有则直接返回,没有则创建后放入。

看个时序图:


image.png

Spring的bean工厂管理的单例模式管理的是多个bean实例的单例,是工厂模式管理所有的bean,而每个bean的创建又使用了单例模式。

4.4 使用场景

五、原型设计模式(Prototype Pattern)

5.1 介绍

相比单例设计模式,原型模式是每次创建一个对象,下面看下spring是如何使用原型模式的

5.2 Spring中原型bean的创建

创建原型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()) {
                ...
            }
            //创建原型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 使用场景

六、 策略模式(Strategy Pattern)

6.1 介绍

策略模式属于行为性模式,它定义一系列的算法对象,使用时候可以使它们相互替换。

6.2 Spring中bean实例化策略

首先看下类图:

image.png

从图知道:接口InstantiationStrategy是实例化策略接口类,它定义了三个实例化接口,然后SimpleInstantiationStrategy实现了该策略,它主要做一些简单的根据构造函数实例号bean的工作,然后CglibSubclassingInstantiationStrategy又继承了SimpleInstantiationStrategy新增了方法注入方式根据cglib生成代理类实例化方法。

在AbstractAutowireCapableBeanFactory中管理了该策略的一个对象,默认是CglibSubclassingInstantiationStrategy策略,运行时候可以通过setInstantiationStrategy改变实例化策略,如果你自己写个个策略的话。

6.3 Spring中Aop代理策略

image.png

首先看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时候根据节点不同使用不同解析策略来解析节点。

image.png

如图在解析每个节点时候会先找到该节点对应的解析策略,然后循环去调用所有解析策略的方法去处理。

6.5 使用场景

七、 门面模式(Facade Pattern)

7.1 介绍

门面模式是一种结构性模式,它通过新增一个门面类对外暴露系统提供的一部分功能,或者屏蔽了内部系统的复杂性,对外部仅仅暴露一个简单的接口,或者通过调用不同的服务对外提供统一的接口,让使用者对这些内部服务透明化。

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

Velocity里面的VelocityEngine和Velocity类都是RuntimeInstance类的门面,后者提供了模板渲染的所有功能,前两者则是内部维护RuntimeInstance的实例,具体工作还是委托给RuntimeInstance来实现。
关于Veloctiy可以参考:https://www.atatech.org/articles/78435

image.png

如图 RuntimeInstance提供了Velocity引擎的所用功能,VelocityEngine内部直接引用了RuntimeInstance的一个实例,VelocityEngine对外暴露的服务都是委托RuntimeInstance实现,并且每次new一个VelocityEngine内部都会有RuntimeInstance的一个实例被创建。而Velocity类调用了单例模式类RuntimeSingleton里面的方法,RuntimeSingleton又是RuntimeInstance的一个单例模式。

7.3 使用场景

九、装饰器模式(Decorator Pattern)

9.1 介绍

装饰器模式是一种结构性模式,它作用是对对象已有功能进行增强,但是不改变原有对象结构。这避免了通过继承方式进行功能扩充导致的类体系臃肿。

9.2 Spring中BeanDefinitionDecorator

先看下类图:

image.png

如图ScopedProxyBeanDefinitionDecorator实现了decorate方法用来对scope作用域为request的bean定义进行包装。
具体时序图为:

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));
            }
        }

        // 创建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干啥用的那:


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

其实就是处理<aop:scoped-proxy />
的,具体作用是包装lavaPvgInfo的bean定义为ScopedProxyFactoryBean,作用是实现request作用域bean.

9.3 commons-collections包中ListUtils

image.png

如图

ListUtils中的四个方法分别依赖list的四种装饰器类对List功能进行扩充和限制。
其中FixedSizeList类通过禁止add/remove操作保证list的大小固定,但是可以修改元素内容
其中UnmodifiableList类通过禁用add,clear,remove,set,保证list的内容不被修改
其中SynchronizedList类通过使用Lock 来保证add,set,get,remove等的同步安全
其中LazyList类则当调用get方法发现list里面不存在对象时候,自动使用factory创建对象.

9.4 使用场景

十、适配器模式(Adapter Pattern)

10.1 介绍

适配器模式属于结构性模式,它为两个不同接口之间互通提供了一种手段。

10.2 Spring中MethodInterceptor适配器

在Spring Aop框架中,MethodInterceptor接口被用来拦截指定的方法,对方法进行增强。

image.png

大家都知道在Aop中每个advistor 里面会有一个advice具体做切面动作,Spring提供了AspectJAfterReturningAdvice,AspectJMethodBeforeAdvice,AspectJAroundAdvice,AspectJAfterAdvice这几个advice,在XML 配置aop时候会指定<aop:after-returning/>,<aop:before/>,<aop:around/>,<aop:after/>,其实内部就是创建上面对应的这些advice。

从图知道AspectJAfterReturningAdvice和AspectJMethodBeforeAdvice没有实现MethodInterceptor接口,其他两者则实现了该接口。而Spring Aop的方法拦截器却必须是实现了MethodInterceptor的,所以Spring提供了对应的适配器来适配这个问题,分别是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();

    //如果实现了MethodInterceptor则直接加入,比如AspectJAroundAdvice,AspectJAfterAdvice
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }

    //否者看是否有当前advice的适配器,首先检验是否支持,支持则返回对应的适配器
    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作为了一个适配器内部委托请求给MethodBeforeAdvice。

10.3 使用场景

十一、模板设计模式(Template Pattern)

11.1 前言

模板设计模式是一种行为设计模式,它使用一个抽象类定义了一个模板,这个模板里面定义了一系列的接口,子类则只需要继承该抽象类并且根据需要重写一部分接口。

11.2 ibatis2中AbstractDAOTemplate

image.png

如图AbstractDAOTemplate是抽象模板类,里面定义了configure方法,configure方法里面定义了好多protected方法,其中就有些是抽象方法。类SpringDAOTemplate,IbatisDAOTemplate,GenericCIDAOTemplate,GenericSIDAOTemplate则继承了AbstractDAOTemplate类并重写了一部分方法。

11.3 Tomcat中Digester里面的Rule

tomcat中的Digester是为了解析server.xml的,其中每个元素都有一个解析规则就是Rule ,类图如下:

image.png

如图:Rule是抽象类,对于每个解析的节点来说Rule提供了解析所需所有的方法,而他的子类则根据自己的特殊性重写一部分方法来支持自己的特性。

11.4 Tomcat中Endpoint

image.png

如图AbstractEndpoint是个抽象类,定义了Endpoint的所有接口,然后JIoEndpoint继承了该类并且重写了一部分重要的方法实现了BIO方式endpoint,NioEndpoint则重写了方法实现了NIO的endpoint.

11.5使用场景

十二、 建造者模式(Builder Pattern)

12.1 前言

建造者模式是一种创建型模式,将一个复制对象的创建屏蔽到接口内部,用户使用时候只需要传递固定的参数,内部就会执行复杂逻辑后返回会用户需要的对象,用户不需要知道创建的细节。

12.2 Mybatis中的SqlSessionFactoryBuilder

image.png

如图mybaits中的SqlSessionFactoryBuilder就是典型的创建者模式,他内部有多个build方法,根据参数的不同创建出SqlSessionFactory对象,使用者只需要传递具体参数而不用关系内部是如何创建出需要的对象的。SqlSessionFactoryBean大家应该很熟悉,在xml里面配置的。

12.3 使用场景

十三、 观察者模式(Observer Pattern)

13.1 前言

观察者模式是一种行为模式,它定义了当一个对象的状态或者属性发生变化时候,通通知其他对这些状态感兴趣的对象。观察者模式也叫发布-订阅模式,就是说当你订阅了摸一个主体时候,如果发布者改变了主题内容,那么所有订阅这个主体者都会受到通知。

13.2 Spring中ApplicationListener

image.png

如图 黄色部分的listener们可以认为是订阅者,红色的context是发布者,context在IOC不同状态会给这些订阅者发布不同的消息通知订阅者容器状态。蓝色的为具体的事件(这里为容器不同状态),其中ContextRefreshedEvent是IOC刷新完成(也就是bean解析完成,创建完毕并且autowired完成)后的事件这个经常用。
。这里context并不是直接来管理黄色的listener订阅者的,而是委托给了绿色的部分,该部分是可以增加删除订阅者,并且发布事件给订阅者。

其实Tomact中的Lifecycle也是这种机制,这里不再赘述。

13.3 使用场景

十四、命令模式(Command Pattern)

14.1 介绍

命令模式是一种行为模式,通过把命令封装为一个对象,命令发送者把命令对象发出后,就不去管是谁来接受处理这个命令,命令接受者接受到命令对象后进行处理,也不用管命令是谁发出的,所以命令模式实现了发送者与接受者之间的解耦,而具体把命令发送给谁还需要一个控制器。

14.2 Tomcat中命令模式

tomcat作为一个服务器本身会接受外部大量请求,当一个请求过来后tomcat根据域名去找对应的host,找到host后会根据应用名去找具体的context(应用),然后具体应用处理请求。对于具体host来说他不关心这个请求是谁给的,对应请求来说他不必关心谁来处理,但是两者是通过request封装请求对象进行关联起来。

image.png

tomcat中Connector作为命令发出者,Connector接受到请求后把请求内容封装为request对象(命令对象),然后使用CoyoteAdapter作为分发器把请求具体发配到具体的host,host在根据request对象找到具体的context,至此找到了具体的应用,交给具体应用处理。

另外对于使用springmvc的应用来说,上面找到具体应用,但是具体交给那个controller来处理那,这是不是也是命令模式的使用那。

14.3 使用场景

十五、参考

十六、总结

设计模式中每一个模式都描述了在我们工作中不断重复发生的问题,以及问题的解决方案,所以真正掌握设计模式可以避免我们做不必要的重复劳动。

欢迎关注微信公众号:技术原始积累 获取更多技术干货_

image.png
上一篇下一篇

猜你喜欢

热点阅读