常用开源框架中设计模式使用分析
Posted 布衣小工
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常用开源框架中设计模式使用分析相关的知识,希望对你有一定的参考价值。
一、前言
说起来设计模式,大家应该都耳熟能详,设计模式代表了软件设计的最佳实践,是经过不断总结提炼出来的代码设计经验的分类总结,这些模式或者可以简化代码,或者可以是代码逻辑开起来清晰,或者对功能扩展很方便…。
设计模式按照使用场景可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。
-
创建型模式(Creational Patterns)
对对象的实例化过程进行抽象,这使得一个系统可以不用关心这些对象是如何创建,组合,呈现的,对于类创建模式来说通过使用继承改变实例化的类,对于对象创建模式来说通过使用代理来实例化所需要的对象。 -
结构型模式(Structural Patterns)
通过对多个类和对象进行组合得到复杂结构的类,一般使用继承继承或者成员变量引用形式来实现。 -
行为型模式(Behavioral Patterns)
行为模式不仅表达了对象和类,还表达了他们之间的交互,涉及到了对象和算法的分配。
下面就带大家看下开源框架框架中是如何应用这些经典设计模式的。
二、责任链设计模式(Chain of Responsibility Pattern)
2.1 介绍
责任链模式是把多个对象串联起来形成一个链状结构,让每个对象都有机会对事件发送者的请求进行处理。责任链模式是设计模式中的行为模式,设计意图是为了使事件发送者和事件接受者之间解耦。通常责任链链中的每个对象都有下一个对象的引入(例如tomcat 里面StandardPipeline用来管理valve),或者有个同一个链管理工厂里面使用数组存放了所有的对象(例如tomcat里面ApplicationFilterChain用来关系filter)。
2.2 Tomcat中Valve链
Tomcat中StandardEngine,StandardHost,StandardContext里面都有自己StandardPipeline,下面以StandardEngine里面StandardPipeline为例讲解
从上面类图可知道每个Valve都要继承ValveBase类,该类里面有一个Valve的引用,实际是链中下一个节点对象,Valve就是通过每个Valve里面的next串联为链的。
每个valve的invoke方法里面调用next.invoke激活链中下一个节点,并且StandardEngine,StandardHost,StandardContext都有一个basic valve这个valve在链的末尾用来激活子容器的valve链。
2.3 Tomcat中Filter链
Tomcat中Filter链是使用ApplicationFilterChain来管理的,具体结构如下图:
可知Filter链不是像Valve一样在内部维护下个节点的引用,而是在ApplicationFilterChain中搞了个数组存放所有的Filter,并通过n统计Filter总个数,pos是当前filter的下标。
ApplicationFilterChain的doFilter代码如下:
1 public void doFilter(ServletRequest request, ServletResponse response) 2 throws IOException, ServletException { 3 ... 4 internalDoFilter(request,response); 5 ... 6 } 7 8 9 private void internalDoFilter(ServletRequest request, 10 ServletResponse response) 11 throws IOException, ServletException { 12 13 // Call the next filter if there is one 14 if (pos < n) { 15 16 //获取filter链中下标为pos的filter 17 ApplicationFilterConfig filterConfig = filters[pos++]; 18 Filter filter = null; 19 try { 20 filter = filterConfig.getFilter(); 21 support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, 22 filter, request, response); 23 24 if (request.isAsyncSupported() && "false".equalsIgnoreCase( 25 filterConfig.getFilterDef().getAsyncSupported())) { 26 request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, 27 Boolean.FALSE); 28 } 29 30 ... 31 32 //调用自定义filter的dofilter方法 33 filter.doFilter(request, response, this); 34 35 36 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, 37 filter, request, response); 38 } 39 .... 40 41 } 42 ..... 43 }
注:这两种方式的区别是啥,就是说那些场景下使用2.2,什么情况下使用2.3这个目前还没有搞清楚有
知道的麻烦在本帖留言帮我解惑下^^
2.4 使用场景
-
当一个请求需要
根据请求参数的不同由不同对象来处理时候。 -
当一个请求需要固定对象顺序处理,并且可扩展性的在固定顺序里面插入新的对象进行处理时候。
三、工厂模式(Factory Pattern)
3.1 介绍
工厂模式是创建型模式,他封装了对象的创建过程,调用者使用具体的工厂方法根据参数就可以获取对应的对象。
3.2 Spring框架中BeanFactory
如图BeanFactory接口提供了getBean方法,在AbstractBeanFactory中实现了该方法,经过层层继承,实现,最后DefaultListableBeanFactory实现了BeanDefinitionRegistry接口用来保存bean定义,继承了AbstractAutowireCapableBeanFactory用来支撑autowired。
一个例子
1 @Test 2 public void testBeanFactoy() throws NamingException, SQLException, ParseException, IOException { 3 4 //创建Bean工厂 5 DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); 6 7 //给bean工厂添加bean定义,解析xml里面的bean放入bean工厂 8 loadBeanDefinitions(bf); 9 10 //根据名字从bean工厂获取bean 11 Hello hello = (Hello) bf.getBean("hello"); 12 hello.sayHello(); 13 14 Hello2 hello2 = (Hello2) bf.getBean("hello2"); 15 hello2.sayHello(); 16 17 18 } 19 20 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { 21 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 22 23 String[] configLocations = new String[] { "beans2.xml" }; 24 if (configLocations != null) { 25 beanDefinitionReader.loadBeanDefinitions(configLocations); 26 } 27 }
3.3 使用场景
-
不同条件下创建不同实例,用于统一管理bean
-
不同条件下调用不同工厂方法获取不同场景下的bean
四、单例设计模式(Singleton Pattern)
4.1 介绍
单例模式是一种创建型模式,单例模式提供一个创建对象的接口,但是多次调用该接口返回的是同一个实例的引用,目的是为了保证只有一个实例,并且提供一个访问这个实例的统一接口。
4.2 Spring中单例bean的创建
Spring中默认配置的bean的scope为singleton,也就是单例作用域。那么看看它是如何做到的。
在AbstractBeanFactory类里面的doGetBean方法:
1 protected Object doGetBean( 2 final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { 3 4 final String beanName = transformedBeanName(name); 5 Object bean = null; 6 7 // 解决set循环依赖 8 Object sharedInstance = getSingleton(beanName); 9 if (sharedInstance != null && args == null) { 10 ... 11 } 12 13 else { 14 ... 15 // 创建单件bean. 16 if (mbd.isSingleton()) { 17 sharedInstance = getSingleton(beanName, new ObjectFactory() { 18 public Object getObject() throws BeansException { 19 try { 20 return createBean(beanName, mbd, args); 21 } 22 catch (BeansException ex) { 23 ... 24 throw ex; 25 } 26 } 27 }); 28 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 29 } 30 //创建原型bean 31 else if (mbd.isPrototype()) { 32 ... 33 } 34 //创建request作用域bean 35 else { 36 ... 37 } 38 } 39 ... 40 return bean; 41 } 42 getSingleton代码: 43 44 public Object getSingleton(String beanName, ObjectFactory singletonFactory) { 45 Assert.notNull(beanName, "\'beanName\' must not be null"); 46 synchronized (this.singletonObjects) { 47 Object singletonObject = this.singletonObjects.get(beanName); 48 if (singletonObject == null) { 49 ... 50 beforeSingletonCreation(beanName); 51 ... 52 try { 53 singletonObject = singletonFactory.getObject(); 54 } 55 catch (BeanCreationException ex) { 56 ... 57 } 58 finally { 59 if (recordSuppressedExceptions) { 60 this.suppressedExceptions = null; 61 } 62 afterSingletonCreation(beanName); 63 } 64 addSingleton(beanName, singletonObject); 65 } 66 return (singletonObject != NULL_OBJECT ? singletonObject : null); 67 } 68 } 69 70 protected void addSingleton(String beanName, Object singletonObject) { 71 synchronized (this.singletonObjects) { 72 this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); 73 this.singletonFactories.remove(beanName); 74 this.earlySingletonObjects.remove(beanName); 75 this.registeredSingletons.add(beanName); 76 } 77 } 78 79 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特别说明:
<bean id="hello" class="com.zlx.demo.Hello" scope="prototype"/>
1 protected <T> T doGetBean( 2 final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) 3 throws BeansException { 4 5 final String beanName = transformedBeanName(name); 6 Object bean; 7 8 // Eagerly check singleton cache for manually registered singletons. 9 Object sharedInstance = getSingleton(beanName); 10 if (sharedInstance != null && args == null) { 11 ... 12 } 13 14 else { 15 ... 16 17 try { 18 ... 19 20 // Create bean instance. 21 if (mbd.isSingleton()) { 22 ... 23 } 24 //创建原型bean 25 else if (mbd.isPrototype()) { 26 // It\'s a prototype -> create a new instance. 27 Object prototypeInstance = null; 28 try { 29 beforePrototypeCreation(beanName); 30 prototypeInstance = createBean(beanName, mbd, args); 31 } 32 finally { 33 afterPrototypeCreation(beanName); 34 } 35 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); 36 } 37 38 else { 39 ... 40 } 41 } 42 catch (BeansException ex) { 43 cleanupAfterBeanCreationFailure(beanName); 44 throw ex; 45 } 46 } 47 ... 48 return (T) bean; 49 }
createBean函数里面则是根据bean定义创建新bean,感兴趣的可以看看。
5.3 使用场景
-
当有业务场景使用某个bean时候需要使用自己的一个拷贝的时候使用。
六、 策略模式(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动态代理策略生成代理类:
1 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { 2 3 4 //如果XML打开了优化开关,或者设置为了代理目标类,或者目前类没有接口 5 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { 6 Class<?> targetClass = config.getTargetClass(); 7 if (targetClass == null) { 8 throw new AopConfigException("TargetSource cannot determine target class: " + 9 "Either an interface or a target is required for proxy creation."); 10 } 11 12 //如果有接口,或者通过Proxy.newProxyInstance生成的,则使用jdk动态代理 13 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { 14 return new JdkDynamicAopProxy(config); 15 } 16 17 //使用cglib 18 return new ObjenesisCglibAopProxy(config); 19 } 20 else { 21 //使用jdk动态代理 22 return new JdkDynamicAopProxy(config); 23 } 24 }
另外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)
9.1 介绍
装饰器模式是一种结构性模式,它作用是对对象已有功能进行增强,但是不改变原有对象结构。这避免了通过继承方式进行功能扩充导致的类体系臃肿。
9.2 Spring中BeanDefinitionDecorator
先看下类图:
如图ScopedProxyBeanDefinitionDecorator实现了decorate方法用来对scope作用域为request的bean定义进行包装。
具体时序图为:
1 class ScopedProxyBeanDefinitionDecorator implements BeanDefinitionDecorator { 2 3 private static final String PROXY_TARGET_CLASS = "proxy-target-class"; 4 5 6 @Override 7 public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) { 8 boolean proxyTargetClass = true; 9 if (node instanceof Element) { 10 Element ele = (Element) node; 11 if (ele.hasAttribute(PROXY_TARGET_CLASS)) { 12 proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS)); 13 } 14 } 15 16 // 创建scoped的代理类,并注册到容器 17 BeanDefinitionHolder holder = 18 ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass); 19 String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName()); 20 parserContext.getReaderContext().fireComponentRegistered( 21 new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName)); 22 return holder; 23 } 24 25 }
关于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
如图
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接口被用来拦截指定的方法,对方法进行增强。
大家都知道在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。
看下DefaultAdvisorAdapterRegistry的 getInterceptors方法:
1 public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { 2 List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3); 3 4 //从advistor中获取advice 5 Advice advice = advisor.getAdvice(); 6 7 //如果实现了MethodInterceptor则直接加入,比如AspectJAroundAdvice,AspectJAfterAdvice 8 if (advice instanceof MethodInterceptor) { 9 interceptors.add((MethodInterceptor) advice); 10 } 11 12 //否者看是否有当前advice的适配器,首先检验是否支持,支持则返回对应的适配器 13 for (AdvisorAdapter adapter : this.adapters) { 14 if (adapter.supportsAdvice(advice)) { 15 interceptors.add(adapter.getInterceptor(advisor)); 16 } 17 } 18 if (interceptors.isEmpty()) { 19 throw new UnknownAdviceTypeException(advisor.getAdvice()); 20 } 21 return interceptors.toArray(new MethodInterceptor[interceptors.size()]); 22 }
以MethodBeforeAdviceAdapter为例子看下:
1 class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { 2 3 public boolean supportsAdvice(Advice advice) { 4 return (advice instanceof MethodBeforeAdvice); 5 } 6 7 public MethodInterceptor getInterceptor(Advisor advisor) { 8 MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice(); 9 return new MethodBeforeAdviceInterceptor(advice); 10 } 11 12 } 13 14 public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { 15 16 private MethodBeforeAdvice advice; 17 18 19 /** 20 * Create a new MethodBeforeAdviceInterceptor for the given advice. 21 * @param advice the MethodBeforeAdvice to wrap MJExtension框架源码分析