新闻资讯
Spring框架中的设计模式(三)——原型模式 对象池 观察者
原型模式
这篇文章的第一个设计模式是原型。可以通过官方文档查找有关Spring作用域中的bean作用域的文章中介绍了类似的概念(prototype)。原型设计模式与有用相同名称的(prototype)作用域有点相似。此设计模式允许通过复制已存在的对象来创建一个对象的实例。副本应该是真正的副本。这意味着新对象的所有属性应与复制对象的属性相同。如果不清楚,比一个简单的JUnit案例更好的说明:
-
publicclassPrototypeTest{
-
-
@Test
-
publicvoidtest(){
-
RobotfirstRobot=newRobot("Droid#1");
-
RobotsecondRobot=(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()));
-
}
-
-
}
-
-
-
classRobotimplementsCloneable{
-
privateStringname;
-
-
publicRobot(Stringname){
-
this.name=name;
-
}
-
-
publicStringgetName(){
-
returnthis.name;
-
}
-
-
protectedObjectclone()throwsCloneNotSupportedException{
-
returnsuper.clone();
-
}
-
}
在Spring中,在org.springframework.beans.factory.support.AbstractBeanFactory中使用一种特定的原型设计模式,它将初始化bean原型作用域。新对象基于配置文件中的bean定义。我们可以看到,在给定的例子中:
-
<beanid="shoppingCart"class="com.waitingforcode.data.ShoppingCart"scope="prototype">
-
<propertyname="id"value="9"></property>
-
</bean>
-
@RunWith(SpringJUnit4ClassRunner.class)
-
@ContextConfiguration(locations={"applicationContext-test.xml"})
-
publicclassSpringPrototypeTest{
-
-
@Autowired
-
privateBeanFactorybeanFactory;
-
-
@Test
-
publicvoidtest(){
-
ShoppingCartcart1=(ShoppingCart)beanFactory.getBean("shoppingCart");
-
assertTrue("Id of cart1 should be 9 but was "+cart1.getId(),
-
cart1.getId()==9);
-
cart1.setId(100);
-
ShoppingCartcart2=(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中。我们检索数据库(SpringJDBC)项目中的对象池的想法。数据库连接池不是由Spring直接实现的,而是适用于Spring工作方式的项目,如C3P0或JakartaCommonsDBCP连接池。
观察者
这里呈现的最后一个设计模式是观察者。当一个或几个课程正在等待具体事件时可以使用它。观察者模式由一个科目和观察员名单组成。一个很好的例子就是GUI界面,其中点击按钮(按钮是主题)会引起听众(观察者)启动的一些操作(再说的直白点就是电影院一场电影这个subject,需要观众(也就是观察者咯),电影产生的一些画面产生的事件,比如恐怖 电影给男人女人带来的不同的感官的感受,传播到观察者也就是观众的眼里所带来的不一样的反应,这个中间一般会添加一个事件传播者,在后面解释Spring的例子的时候会说到),例如:打开一个新页面这个动作。可以参考下面的例子:
-
publicclassObserverTest{
-
-
@Test
-
publicvoidtest(){
-
ObserverpageOpener=newPageOpener();
-
Observerregister=newRegister();
-
Buttonbtn=newButton();
-
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());
-
}
-
-
}
-
-
classButton{
-
-
privatebooleanclicked;
-
privateList<observer>listeners;
-
-
publicList<observer>getListeners(){
-
if(this.listeners==null){
-
this.listeners=newArrayList<observer>();
-
}
-
returnthis.listeners;
-
}
-
-
publicvoidaddListener(Observerobserver){
-
getListeners().add(observer);
-
}
-
-
publicbooleanwasClicked(){
-
returnthis.clicked;
-
}
-
-
publicvoidclickOn(){
-
this.clicked=true;
-
informAll();
-
}
-
-
privatevoidinformAll(){
-
for(Observerobserver:getListeners()){
-
observer.informAboutEvent();
-
}
-
}
-
-
}
-
-
abstractclassObserver{
-
protectedbooleaninformed;
-
-
publicvoidinformAboutEvent(){
-
this.informed=true;
-
}
-
-
publicbooleanwasInformed(){
-
returnthis.informed;
-
}
-
}
-
-
classPageOpenerextendsObserver{
-
-
@Override
-
publicvoidinformAboutEvent(){
-
System.out.println("Preparing download of new page");
-
super.informAboutEvent();
-
}
-
-
}
-
-
classRegisterextendsObserver{
-
-
@Override
-
publicvoidinformAboutEvent(){
-
System.out.println("Adding the action to register");
-
super.informAboutEvent();
-
}
-
}
可以看到,关于我们的Button实例点击的事件被发送到所有的观察者对象。从这些对象开始下载页面内容,第二个将在事件的信息保存在注册表中。在Spring中,观察者设计模式用于将与应用程序上下文相关的事件传输到org.springframework.context.ApplicationListener的实现。要了解它们的实现方法,我们来看一下AbstractApplicationContext类(老版本的代码,新版本的请自行对照):
-
publicabstractclassAbstractApplicationContextextendsDefaultResourceLoader
-
implementsConfigurableApplicationContext,DisposableBean{
-
/** Statically specified listeners */
-
privateSet<applicationlistener<?>>applicationListeners=newLinkedHashSet<applicationlistener<?>>();
-
-
// some other fields and methods
-
@Override
-
publicvoidaddApplicationListener(ApplicationListener<?>listener){
-
if(this.applicationEventMulticaster!=null){
-
this.applicationEventMulticaster.addApplicationListener(listener);
-
}
-
else{//新版本这里直接咔嚓掉,上面的applicationEventMulticaster一旦为空,就会报错的
-
this.applicationListeners.add(listener);
-
}
-
}
-
-
/**
-
* Return the list of statically specified ApplicationListeners.
-
*/
-
publicCollection<applicationlistener<?>>getApplicationListeners(){
-
returnthis.applicationListeners;
-
}
-
-
/**
-
* Add beans that implement ApplicationListener as listeners.
-
* Doesn't affect other listeners, which can be added without being beans.
-
*/
-
protectedvoidregisterListeners(){
-
// 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(StringlisName:listenerBeanNames){
-
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
-
}
-
}
-
}
在提供的代码中,监听器在内部添加到应用程序上下文类中,并且在registerListeners()方法之后,它们被注册到由接口org.springframework.context.event.ApplicationEventMulticaster表示的适当的事件多路广播器(因为有很多listeners)。EventMulticaster负责管理不同的listener和向他们发布事件。
-
publicclassSimpleApplicationEventMulticasterextendsAbstractApplicationEventMulticaster{
-
privateExecutortaskExecutor;
-
privateErrorHandlererrorHandler;
-
-
publicSimpleApplicationEventMulticaster(){
-
}
-
-
publicSimpleApplicationEventMulticaster(BeanFactorybeanFactory){
-
this.setBeanFactory(beanFactory);
-
}
-
-
publicvoidsetTaskExecutor(ExecutortaskExecutor){
-
this.taskExecutor=taskExecutor;
-
}
-
-
protectedExecutorgetTaskExecutor(){
-
returnthis.taskExecutor;
-
}
-
-
publicvoidsetErrorHandler(ErrorHandlererrorHandler){
-
this.errorHandler=errorHandler;
-
}
-
-
protectedErrorHandlergetErrorHandler(){
-
returnthis.errorHandler;
-
}
-
-
publicvoidmulticastEvent(ApplicationEventevent){
-
this.multicastEvent(event,this.resolveDefaultEventType(event));
-
}
-
//发布事件:通过池执行任务的方式来做并发处理,这样就把之前的对象池模式给利用上了
-
publicvoidmulticastEvent(finalApplicationEventevent,ResolvableTypeeventType){
-
ResolvableTypetype=eventType!=null?eventType:this.resolveDefaultEventType(event);
-
Iteratorvar4=this.getApplicationListeners(event,type).iterator();
-
-
while(var4.hasNext()){
-
finalApplicationListener<?>listener=(ApplicationListener)var4.next();
-
Executorexecutor=this.getTaskExecutor();
-
if(executor!=null){
-
executor.execute(newRunnable(){
-
publicvoidrun(){
-
SimpleApplicationEventMulticaster.this.invokeListener(listener,event);
-
}
-
});
-
}else{
-
this.invokeListener(listener,event);
-
}
-
}
-
-
}
-
...
-
}
这次我们讲3种设计模式:用于在同一个调用作用域内创建bean的原型,避免重新创建巨型对象的对象池,以及将应用程序的上下文事件分派给适当的监听器的观察者。
原文链接:https://mp.weixin.qq.com/s/wsTqmXSSM0ixMlk_2af0FA
回复列表