⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 https://muyinchen.github.io/2017/07/28/Spring%E6%A1%86%E6%9E%B6%E4%B8%AD%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F(%E4%B8%89)/ 「一叶知秋」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

在之前的两篇文章中,我们看到了一些在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.migo.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定义创建的。最初,cart1cart2对象的id值为9.它在测试结束时被修改,以证明两个引用都属于两个不同的对象。

对象池

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

Spring还使用线程池来管理其调度部分。一些示例位于org.springframework.scheduling.concurrent中。我们检索数据库(Spring JDBC)项目中的对象池的想法。数据库连接池不是由Spring直接实现的,而是适用于Spring工作方式的项目,如C3P0Jakarta 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的原型,避免重新创建巨型对象的对象池,以及将应用程序的上下文事件分派给适当的监听器的观察者。

666. 彩蛋

如果你对 Spring 感兴趣,欢迎加入我的知识星球一起交流。

知识星球

文章目录
  1. 1. 原型模式
  2. 2. 对象池
  3. 3. 观察者
  • 666. 彩蛋