新闻资讯

新闻资讯 行业动态

Spring框架中的设计模式(二)——代理模式 复合模式 策略模式 模板模式

编辑:008     时间:2020-02-27

代理模式

面向对象编程(OOP)可能是编程中最流行的概念。然而,Spring引入了另一种编码规范,面向切面编程(AOP)。为了简化定义,AOP是面向系统特定点的一种编程,如:异常抛出,特定类别方法的执行等.AOP允许在执行这些特定点之前或之后执行补充动作。如何实现这种操作?它可以通过监听器(listeners)进行。但在这种情况下,我们应该在只要可能存在调用的地方都需要定义监听器来进行监听(比如在一个方法的开始的地方)。这就是为什么Spring不采用这个idea。相反,Spring实现了一种能够通过额外的方法调用完成任务的设计模式 -代理设计模式。

代理就像对象的镜像一样。也正因为如此,代理对象不仅可以覆盖真实对象,还可以扩展其功能。因此,对于只能在屏幕上打印一些文本的对象,我们可以添加另一个对象来过滤显示单词。可以通过代理来定义第二个对象的调用。代理是封装真实对象的对象。例如,如果您尝试调用Waiter bean,那么您将调用该Bean的代理,其行为方式完全相同。

代理设计模式的一个很好的例子是org.springframework.aop.framework.ProxyFactoryBean。该工厂根据Spring bean构建AOP代理。该类实现了定义getObject()方法的FactoryBean接口。此方法用于将需求Bean的实例返回给bean factory。在这种情况下,它不是返回的实例,而是AOP代理。在执行代理对象的方法之前,可以通过调用补充方法来进一步“修饰”代理对象(其实所谓的静态代理不过是在装饰模式上加了个要不要你来干动作行为而已,而不是装饰模式什么也不做就加了件衣服,其他还得由你来全权完成)。

ProxyFactory的一个例子是:

  1. publicclassTestProxyAop{


  2.  @Test

  3.  publicvoidtest(){

  4.    ProxyFactoryfactory=newProxyFactory(newHouse());

  5.    factory.addInterface(Construction.class);

  6.    factory.addAdvice(newBeforeConstructAdvice());

  7.    factory.setExposeProxy(true);


  8.    Constructionconstruction=(Construction)factory.getProxy();

  9.    construction.construct();

  10.    assertTrue("Construction is illegal. "

  11.      +"Supervisor didn't give a permission to build "

  12.      +"the house",construction.isPermitted());

  13.  }


  14. }


  15. interfaceConstruction{

  16.  publicvoidconstruct();

  17.  publicvoidgivePermission();

  18.  publicbooleanisPermitted();

  19. }


  20. classHouseimplementsConstruction{


  21.  privatebooleanpermitted=false;


  22.  @Override

  23.  publicbooleanisPermitted(){

  24.    returnthis.permitted;

  25.  }


  26.  @Override

  27.  publicvoidconstruct(){

  28.    System.out.println("I'm constructing a house");

  29.  }


  30.  @Override

  31.  publicvoidgivePermission(){

  32.    System.out.println("Permission is given to construct a simple house");

  33.    this.permitted=true;

  34.  }

  35. }


  36. classBeforeConstructAdviceimplementsMethodBeforeAdvice{


  37.  @Override

  38.  publicvoidbefore(Methodmethod,Object[]arguments,Objecttarget)throwsThrowable{

  39.    if(method.getName().equals("construct")){

  40.      ((Construction)target).givePermission();

  41.    }

  42.  }


  43. }

这个测试应该通过,因为我们不直接在House实例上操作,而是代理它。代理调用第一个BeforeConstructAdvice的before方法(指向在执行目标方法之前执行,在我们的例子中为construct())通过它,给出了一个“权限”来构造对象的字段(house)。代理层提供了一个额外新功能,因为它可以简单地分配给另一个对象。要做到这一点,我们只能在before方法之前修改过滤器。

复合模式

另一种结构模式是复合模式。在关于Spring中设计模式的第一篇文章中,我们使用构建器来构造复杂对象。另一种实现方法是使用复合模式。这种模式是基于具有共同行为的多个对象的存在,用于构建更大的对象。较大的对象仍然具有与最小对象相同的特征。那么用它来定义相同的行为。

复合对象的非Spring示例可以是一个写入HTML的文本对象,由包含span或em标签的段落组成:

  1. publicclassCompositeTest{


  2.  @Test

  3.  publicvoidtest(){

  4.    TextTagCompositecomposite=newPTag();

  5.    composite.addTag(newSpanTag());

  6.    composite.addTag(newEmTag());


  7.    // sample client code

  8.    composite.startWrite();

  9.    for(TextTagleaf:composite.getTags()){

  10.      leaf.startWrite();

  11.      leaf.endWrite();

  12.    }

  13.    composite.endWrite();

  14.    assertTrue("Composite should contain 2 tags but it contains "+composite.getTags().size(),composite.getTags().size()==2);

  15.  }


  16. }



  17. interfaceTextTag{

  18.  publicvoidstartWrite();

  19.  publicvoidendWrite();

  20. }


  21. interfaceTextTagCompositeextendsTextTag{

  22.  publicList<TextTag>getTags();

  23.  publicvoidaddTag(TextTagtag);

  24. }


  25. classPTagimplementsTextTagComposite{

  26.  privateList<TextTag>tags=newArrayList<TextTag>();


  27.  @Override

  28.  publicvoidstartWrite(){

  29.    System.out.println("<p>");

  30.  }


  31.  @Override

  32.  publicvoidendWrite(){

  33.    System.out.println("</p>");

  34.  }


  35.  @Override

  36.  publicList<TextTag>getTags(){

  37.    returntags;

  38.  }


  39.  @Override

  40.  publicvoidaddTag(TextTagtag){

  41.    tags.add(tag);

  42.  }

  43. }


  44. classSpanTagimplementsTextTag{


  45.  @Override

  46.  publicvoidstartWrite(){

  47.    System.out.println("<span>");

  48.  }


  49.  @Override

  50.  publicvoidendWrite(){

  51.    System.out.println("</span>");

  52.  }


  53. }


  54. classEmTagimplementsTextTag{


  55.  @Override

  56.  publicvoidstartWrite(){

  57.    System.out.println("<em>");

  58.  }


  59.  @Override

  60.  publicvoidendWrite(){

  61.    System.out.println("</em>");

  62.  }


  63. }

在这种情况下,可以看到一个复合对象。我们可以区分复合与非复合对象,因为第一个可以容纳一个或多个非复合对象(PTag类中的privateListtags字段)。非复合对象称为叶子。TextTag接口被称为组件,因为它为两个对象类型提供了共同的行为规范(有点像Linux文件管理系统的有共同点的文件放在一个文件夹下进行管理,其实就是节点管理)。

在Spring世界中,我们检索复合对象的概念是org.springframework.beans.BeanMetadataElement接口,用于配置bean对象。它是所有继承对象的基本界面。现在,在一方面,我们有一个叶子,由org.springframework.beans.factory.parsing.BeanComponentDefinition表示,另一边是复合org.springframework.beans.factory.parsing.CompositeComponentDefinition。CompositeComponentDefinition类似于组件,因为它包含addNestedComponent(ComponentDefinition component)方法,它允许将叶添加到私有final列表中nestedComponents。您可以看到,由于此列表,BeanComponentDefinition和CompositeComponentDefinition的组件是org.springframework.beans.factory.parsing.ComponentDefinition。

策略模式

本文描述的第三个概念是策略设计模式。策略定义了通过不同方式完成相同事情的几个对象。完成任务的方式取决于采用的策略。举个例子说明,我们可以去一个国家。我们可以乘公共汽车,飞机,船甚至汽车去那里。所有这些方法将把我们运送到目的地国家。但是,我们将通过检查我们的银行帐户来选择最适应的方式。如果我们有很多钱,我们将采取最快的方式(可能是私人飞行)。如果我们没有足够的话,我们会采取最慢的(公车,汽车)。该银行账户作为确定适应策略的因素。

Spring在org.springframework.web.servlet.mvc.multiaction.MethodNameResolver类(过时,但不影响拿来研究)中使用策略设计模式。它是MultiActionController(同样过时)的参数化实现。在开始解释策略之前,我们需要了解MultiActionController的实用性。这个类允许同一个类处理几种类型的请求。作为Spring中的每个控制器,MultiActionController执行方法来响应提供的请求。策略用于检测应使用哪种方法。解析过程在MethodNameResolver实现中实现,例如在同一个包中的ParameterMethodNameResolver中。方法可以通过多个条件解决:属性映射,HTTP请求参数或URL路径。

  1. @Override

  2. publicStringgetHandlerMethodName(HttpServletRequestrequest)throwsNoSuchRequestHandlingMethodException{

  3.  StringmethodName=null;


  4.  // Check parameter names where the very existence of each parameter

  5.  // means that a method of the same name should be invoked, if any.

  6.  if(this.methodParamNames!=null){

  7.    for(Stringcandidate:this.methodParamNames){

  8.      if(WebUtils.hasSubmitParameter(request,candidate)){

  9.        methodName=candidate;

  10.        if(logger.isDebugEnabled()){

  11.          logger.debug("Determined handler method '"+methodName+

  12.            "' based on existence of explicit request parameter of same name");

  13.        }

  14.        break;

  15.      }

  16.    }

  17.  }


  18.  // Check parameter whose value identifies the method to invoke, if any.

  19.  if(methodName==null&&this.paramName!=null){

  20.    methodName=request.getParameter(this.paramName);

  21.    if(methodName!=null){

  22.      if(logger.isDebugEnabled()){

  23.        logger.debug("Determined handler method '"+methodName+

  24.          "' based on value of request parameter '"+this.paramName+"'");

  25.      }

  26.    }

  27.  }


  28.  if(methodName!=null&&this.logicalMappings!=null){

  29.    // Resolve logical name into real method name, if appropriate.

  30.    StringoriginalName=methodName;

  31.    methodName=this.logicalMappings.getProperty(methodName,methodName);

  32.    if(logger.isDebugEnabled()){

  33.      logger.debug("Resolved method name '"+originalName+"' to handler method '"+methodName+"'");

  34.    }

  35.  }


  36.  if(methodName!=null&&!StringUtils.hasText(methodName)){

  37.    if(logger.isDebugEnabled()){

  38.      logger.debug("Method name '"+methodName+"' is empty: treating it as no method name found");

  39.    }

  40.    methodName=null;

  41.  }


  42.  if(methodName==null){

  43.    if(this.defaultMethodName!=null){

  44.      // No specific method resolved: use default method.

  45.      methodName=this.defaultMethodName;

  46.      if(logger.isDebugEnabled()){

  47.        logger.debug("Falling back to default handler method '"+this.defaultMethodName+"'");

  48.      }

  49.    }

  50.    else{

  51.      // If resolution failed completely, throw an exception.

  52.      thrownewNoSuchRequestHandlingMethodException(request);

  53.    }

  54.  }


  55.  returnmethodName;

  56. }

正如我们在前面的代码中可以看到的,方法的名称通过提供的参数映射,URL中的预定义属性或参数存在来解决(默认情况下,该参数的名称是action)。

模板模式

本文提出的最后一个设计模式是模板方法。此模式定义了类行为的骨架,并将子步骤的某些步骤的延迟执行(具体就是下面例子中一个方法放在另一个方法中,只有调用另一方方法的时候这个方法才会执行,而且还可能会在其他行为方法之后按顺序执行)。其中写了一种方法(下面例子中的construct()),注意定义为final,起着同步器的角色。它以给定的顺序执行由子类定义的方法。在现实世界中,我们可以将模板方法与房屋建设进行比较。独立于建造房屋的公司,我们需要从建立基础开始,只有在我们完成之后才能做其他的工作。这个执行逻辑将被保存在一个我们不能改变的方法中。例如基础建设或刷墙会被作为一个模板方法中的方法,具体到建筑房屋的公司。我们可以在给定的例子中看到它:

  1. publicclassTemplateMethod{


  2.  publicstaticvoidmain(String[]args){

  3.    HouseAbstracthouse=newSeaHouse();

  4.    house.construct();

  5.  }


  6. }


  7. abstractclassHouseAbstract{

  8.  protectedabstractvoidconstructFoundations();

  9.  protectedabstractvoidconstructWall();


  10.  // template method

  11.  publicfinalvoidconstruct(){

  12.    constructFoundations();

  13.    constructWall();

  14.  }

  15. }


  16. classEcologicalHouseextendsHouseAbstract{


  17.  @Override

  18.  protectedvoidconstructFoundations(){

  19.    System.out.println("Making foundations with wood");

  20.  }


  21.  @Override

  22.  protectedvoidconstructWall(){

  23.    System.out.println("Making wall with wood");

  24.  }


  25. }


  26. classSeaHouseextendsHouseAbstract{


  27.  @Override

  28.  protectedvoidconstructFoundations(){

  29.    System.out.println("Constructing very strong foundations");

  30.  }


  31.  @Override

  32.  protectedvoidconstructWall(){

  33.    System.out.println("Constructing very strong wall");

  34.  }


  35. }

该代码应该输出:

  1. Constructingvery strong foundations

  2. Constructingvery strong wall

Spring在org.springframework.context.support.AbstractApplicationContext类中使用模板方法。他们不是一个模板方法(在我们的例子中是construct ),而是多个。例如,getsFreshBeanFactory返回内部bean工厂的新版本,调用两个抽象方法:refreshBeanFactory(刷新工厂bean)和getBeanFactory(以获取更新的工厂bean)。这个方法和其他一些方法一样,用在public void refresh()中,抛出构造应用程序上下文的BeansException,IllegalStateException方法(这里会在后面Spring中与应用程序上下文分析中再次提到)。

我们可以从同一个包中的GenericApplicationContext找到一些通过模板方法所实现的抽象方法的实现的例子(说的有点拗口,多读几遍就好):

  1. /**

  2.  * Do nothing: We hold a single internal BeanFactory and rely on callers

  3.  * to register beans through our public methods (or the BeanFactory's).

  4.  * @see #registerBeanDefinition

  5.  */

  6. @Override

  7. protectedfinalvoidrefreshBeanFactory()throwsIllegalStateException{

  8.  if(this.refreshed){

  9.    thrownewIllegalStateException(

  10.      "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");

  11.  }

  12.  this.beanFactory.setSerializationId(getId());

  13.  this.refreshed=true;

  14. }


  15. @Override

  16. protectedvoidcancelRefresh(BeansExceptionex){

  17.  this.beanFactory.setSerializationId(null);

  18.  super.cancelRefresh(ex);

  19. }


  20. /**

  21.  * Not much to do: We hold a single internal BeanFactory that will never

  22.  * get released.

  23.  */

  24. @Override

  25. protectedfinalvoidcloseBeanFactory(){

  26.  this.beanFactory.setSerializationId(null);

  27. }


  28. /**

  29.  * Return the single internal BeanFactory held by this context

  30.  * (as ConfigurableListableBeanFactory).

  31.  */

  32. @Override

  33. publicfinalConfigurableListableBeanFactorygetBeanFactory(){

  34.  returnthis.beanFactory;

  35. }


  36. /**

  37.  * Return the underlying bean factory of this context,

  38.  * available for registering bean definitions.

  39.  * <p><b>NOTE:</b> You need to call {@link #refresh()} to initialize the

  40.  * bean factory and its contained beans with application context semantics

  41.  * (autodetecting BeanFactoryPostProcessors, etc).

  42.  * @return the internal bean factory (as DefaultListableBeanFactory)

  43.  */

  44. publicfinalDefaultListableBeanFactorygetDefaultListableBeanFactory(){

  45.  returnthis.beanFactory;

  46. }

经过上面这些可以让我们发现Spring如何通过使用行为和结构设计模式来更好地组织上下文(模板方法),并通过相应策略来解决执行方法。它使用两种结构设计模式,通过代理模式来简化AOP部分并通过复合模式来构造复杂对象。


原文链接:https://mp.weixin.qq.com/s/fc0af9WPlTK_LduChSfLhA

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐