Spring框架中的设计模式(三)

作者: 极乐君 | 来源:发表于2017-08-14 15:54 被阅读111次

    在之前的两篇文章中,我们看到了一些在Spring框架中实现的设计模式。这一次我们会发现这个流行框架使用的3种新模式。本文将从描述两个创意设计模式开始:原型和对象池。最后我们将重点关注行为模式—>观察者。

    原型模式

    这篇文章的第一个设计模式是原型。可以通过官方文档查找有关Spring作用域中的bean作用域的文章中介绍了类似的概念(prototype)。原型设计模式与有用相同名称的(prototype)作用域有点相似。此设计模式允许通过复制已存在的对象来创建一个对象的实例。副本应该是真正的副本。这意味着新对象的所有属性应与复制对象的属性相同。如果不清楚,比一个简单的JUnit案例更好的说明:

    public class PrototypeTest {
    
      @Test
      public void test() {
        Robot firstRobot = new Robot("Droid#1");
        Robot secondRobot = (Robot) firstRobot.clone();
        assertTrue("Cloned robot's instance can't be the same as the"
          +" source robot instance", 
          firstRobot != secondRobot);
        assertTrue("Cloned robot's name should be '"+firstRobot.getName()+"'"
          +" but was '"+secondRobot.getName()+"'", 
          secondRobot.getName().equals(firstRobot.getName()));
      }
    
    }
    
    
    class Robot implements Cloneable {
      private String name;
    
      public Robot(String name) {
        this.name = name;
      }
    
      public String getName() {
        return this.name;
      }
    
      protected Object clone() throws CloneNotSupportedException {
        return super.clone();
      }
    }
    
    

    在Spring中,在org.springframework.beans.factory.support.AbstractBeanFactory中使用一种特定的原型设计模式,它将初始化bean原型作用域。新对象基于配置文件中的bean定义。我们可以看到,在给定的例子中:

    <bean id="shoppingCart" class="com.waitingforcode.data.ShoppingCart" scope="prototype">
      <property name="id" value="9"></property>
    </bean>
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations={"applicationContext-test.xml"})
    public class SpringPrototypeTest {
    
      @Autowired
      private BeanFactory beanFactory;
    
      @Test
      public void test() {
        ShoppingCart cart1 = (ShoppingCart) beanFactory.getBean("shoppingCart");
        assertTrue("Id of cart1 should be 9 but was "+cart1.getId(), 
          cart1.getId() == 9);
        cart1.setId(100);
        ShoppingCart cart2 = (ShoppingCart) beanFactory.getBean("shoppingCart");
        assertTrue("Id of cart2 should be 9 but was "+cart2.getId(), 
          cart2.getId() == 9);
        assertTrue("Id of second cart ("+cart2.getId()+") shouldn't be the same as the first one: "+cart1.getId(), 
          cart1.getId() != cart2.getId());
        cart2.setId(cart1.getId());
        assertTrue("Now (after cart2.setId(cart1.getId())), the id of second cart ("+cart2.getId()+") should be the same as the first one: "
          +cart1.getId(), cart1.getId() == cart2.getId());
        assertTrue("Both instance shouldn't be the same", cart1 != cart2);
      }
    
    }
    

    从前面的例子可以看出,ShoppingCart实例是直接从bean定义创建的。最初,cart1和cart2对象的id值为9.它在测试结束时被修改,以证明两个引用都属于两个不同的对象。

    对象池

    Spring中使用的另一个模型是对象池设计模式。其主要目的在于在一个池中保存特定数量的对象,并根据需要重新使用。通过它,我们可以改善我们想要使用巨型对象的响应时间。巨型意味着这些对象的构造需要很多时间(例如:持有数据库连接的对象),最好重用已经存在的和未获取的对象,而不是创建新对象。

    Spring还使用线程池来管理其调度部分。一些示例位于org.springframework.scheduling.concurrent中。我们检索数据库(Spring JDBC)项目中的对象池的想法。数据库连接池不是由Spring直接实现的,而是适用于Spring工作方式的项目,如C3P0或Jakarta Commons DBCP连接池。

    观察者

    这里呈现的最后一个设计模式是观察者。当一个或几个课程正在等待具体事件时可以使用它。观察者模式由一个科目和观察员名单组成。一个很好的例子就是GUI界面,其中点击按钮(按钮是主题)会引起听众(观察者)启动的一些操作(再说的直白点就是电影院一场电影这个subject,需要观众(也就是观察者咯),电影产生的一些画面产生的事件,比如恐怖 电影给男人女人带来的不同的感官的感受,传播到观察者也就是观众的眼里所带来的不一样的反应,这个中间一般会添加一个事件传播者,在后面解释Spring的例子的时候会说到),例如:打开一个新页面这个动作。可以参考下面的例子:

    public class ObserverTest {
    
      @Test
      public void test() {
        Observer pageOpener = new PageOpener();
        Observer register = new Register();
        Button btn = new Button();
        btn.addListener(pageOpener);
        btn.addListener(register);
        btn.clickOn();
        assertTrue("Button should be clicked but it wasn't", 
          btn.wasClicked());
        assertTrue("Page opener should be informed about click but it wasn't", 
          pageOpener.wasInformed());
        assertTrue("Register should be informed about click but it wasn't", 
          register.wasInformed());
      }
    
    }
    
    class Button {
    
      private boolean clicked;
      private List<observer> listeners;
    
      public List<observer> getListeners() {
        if (this.listeners == null) {
          this.listeners = new ArrayList<observer>();
        }
        return this.listeners;
      }
    
      public void addListener(Observer observer) {
        getListeners().add(observer);
      }
    
      public boolean wasClicked() {
        return this.clicked;
      }
    
      public void clickOn() {
        this.clicked = true;
        informAll();
      }
    
      private void informAll() {
        for (Observer observer : getListeners()) {
          observer.informAboutEvent();
        }
      }
    
    }
    
    abstract class Observer {
      protected boolean informed;
    
      public void informAboutEvent() {
        this.informed = true;
      }
    
      public boolean wasInformed() {
        return this.informed;
      }
    }
    
    class PageOpener extends Observer {
    
      @Override
      public void informAboutEvent() {
        System.out.println("Preparing download of new page");
        super.informAboutEvent();
      }
    
    }
    
    class Register extends Observer {
    
      @Override
      public void informAboutEvent() {
        System.out.println("Adding the action to register");
        super.informAboutEvent();
      }
    }
    

    可以看到,关于我们的Button实例点击的事件被发送到所有的观察者对象。从这些对象开始下载页面内容,第二个将在事件的信息保存在注册表中。在Spring中,观察者设计模式用于将与应用程序上下文相关的事件传输到org.springframework.context.ApplicationListener的实现。要了解它们的实现方法,我们来看一下AbstractApplicationContext类(老版本的代码,新版本的请自行对照):

    public abstract class AbstractApplicationContext extends DefaultResourceLoader
      implements ConfigurableApplicationContext, DisposableBean {
      /** Statically specified listeners */
      private Set<applicationlistener<?>> applicationListeners = new LinkedHashSet<applicationlistener<?>>();
    
      // some other fields and methods
      @Override
      public void addApplicationListener(ApplicationListener<?> listener) {
        if (this.applicationEventMulticaster != null) {
          this.applicationEventMulticaster.addApplicationListener(listener);
        }
        else {//新版本这里直接咔嚓掉,上面的applicationEventMulticaster一旦为空,就会报错的
          this.applicationListeners.add(listener);
        }
      }
    
      /**
        * Return the list of statically specified ApplicationListeners.
        */
      public Collection<applicationlistener<?>> getApplicationListeners() {
        return this.applicationListeners;
      }
    
      /**
        * Add beans that implement ApplicationListener as listeners.
        * Doesn't affect other listeners, which can be added without being beans.
        */
      protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
          getApplicationEventMulticaster().addApplicationListener(listener);
        }
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String lisName : listenerBeanNames) {
          getApplicationEventMulticaster().addApplicationListenerBean(lisName);
        }
      }
    }
    

    在提供的代码中,监听器在内部添加到应用程序上下文类中,并且在registerListeners()方法之后,它们被注册到由接口org.springframework.context.event.ApplicationEventMulticaster表示的适当的事件多路广播器(因为有很多listeners)。EventMulticaster负责管理不同的listener和向他们发布事件。

    public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
        private Executor taskExecutor;
        private ErrorHandler errorHandler;
        public SimpleApplicationEventMulticaster() {
        }
        public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
            this.setBeanFactory(beanFactory);
        }
        public void setTaskExecutor(Executor taskExecutor) {
            this.taskExecutor = taskExecutor;
        }
        protected Executor getTaskExecutor() {
            return this.taskExecutor;
        }
        public void setErrorHandler(ErrorHandler errorHandler) {
            this.errorHandler = errorHandler;
        }
        protected ErrorHandler getErrorHandler() {
            return this.errorHandler;
        }
        public void multicastEvent(ApplicationEvent event) {
            this.multicastEvent(event, this.resolveDefaultEventType(event));
        }
        //发布事件:通过池执行任务的方式来做并发处理,这样就把之前的对象池模式给利用上了
        public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
            ResolvableType type = eventType != null?eventType:this.resolveDefaultEventType(event);
            Iterator var4 = this.getApplicationListeners(event, type).iterator();
            while(var4.hasNext()) {
                final ApplicationListener<?> listener = (ApplicationListener)var4.next();
                Executor executor = this.getTaskExecutor();
                if(executor != null) {
                    executor.execute(new Runnable() {
                        public void run() {
                            SimpleApplicationEventMulticaster.this.invokeListener(listener, event);
                        }
                    });
                } else {
                    this.invokeListener(listener, event);
                }
            }
        }
    ...
    }
    

    这次我们讲3种设计模式:用于在同一个调用作用域内创建bean的原型,避免重新创建巨型对象的对象池,以及将应用程序的上下文事件分派给适当的监听器的观察者。

    原文:Spring框架中的设计模式(三)
    转载:极乐科技

    相关文章

      网友评论

        本文标题:Spring框架中的设计模式(三)

        本文链接:https://www.haomeiwen.com/subject/dmpurxtx.html