9、原型模式
可以通过官方文档查找有关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
.它在测试结束时被修改,以证明两个引用都属于两个不同的对象。
10、对象池
Spring
中使用的另一个模型是对象池设计模式。其主要目的在于在一个池中保存特定数量的对象,并根据需要重新使用。通过它,我们可以改善我们想要使用巨型对象
的响应时间。巨型
意味着这些对象的构造需要很多时间(例如:持有数据库连接的对象),最好重用已经存在的和未获取的对象,而不是创建新对象。
Spring还使用线程池来管理其调度部分。一些示例位于org.springframework.scheduling.concurrent中。我们检索数据库(Spring JDBC
)项目中的对象池的想法。数据库连接池不是由Spring
直接实现的,而是适用于Spring
工作方式的项目,如C3P0
或Jakarta Commons DBCP
连接池。
11、观察者
当一个或几个课程正在等待具体事件时可以使用它。观察者模式由一个科目和观察员名单组成。一个很好的例子就是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);
}
}
}
...
}
12、适配器
当我们需要在给定场景下(也就是给定接口)想要不改变自身行为而又想做到一些事情的情况下(就是我给电也就是接口了,你来做事也就是各种电器),使用适配器设计模式(这里再说一点,就相当于我们再一个规章制度的环境下,如何去适应并达到我们期待的效果,放在架构设计这里,可以拿一个php系统和一个Java系统来说,假如两者要互相调用对方的功能,我们可以设计一套对外的api来适配)。这意味着在调用此对象之前,我们将更改使用对象而不改变机制。拿一个现实中的例子进行说明,想象一下你想要用电钻来钻一个洞。要钻一个小洞,你会使用小钻头,钻一个大的需要用大钻头。可以看下面的代码:
public class AdapterTest {
public static void main(String[] args) {
HoleMaker maker = new HoleMakerImpl();
maker.makeHole(1);
maker.makeHole(2);
maker.makeHole(30);
maker.makeHole(40);
}
}
interface HoleMaker {
void makeHole(int diameter);
}
interface DrillBit {
void makeSmallHole();
void makeBigHole();
}
// Two adaptee objects
class BigDrillBit implements DrillBit {
@Override
public void makeSmallHole() {
// do nothing
}
@Override
public void makeBigHole() {
System.out.println("Big hole is made byt WallBigHoleMaker");
}
}
class SmallDrillBit implements DrillBit {
@Override
public void makeSmallHole() {
System.out.println("Small hole is made byt WallSmallHoleMaker");
}
@Override
public void makeBigHole() {
// do nothing
}
}
// Adapter class
class Drill implements HoleMaker {
private DrillBit drillBit;
public Drill(int diameter) {
drillBit = getMakerByDiameter(diameter);
}
@Override
public void makeHole(int diameter) {
if (isSmallDiameter(diameter)) {
drillBit.makeSmallHole();
} else {
drillBit.makeBigHole();
}
}
private DrillBit getMakerByDiameter(int diameter) {
if (isSmallDiameter(diameter)) {
return new SmallDrillBit();
}
return new BigDrillBit();
}
private boolean isSmallDiameter(int diameter) {
return diameter < 10;
}
}
// Client class
class HoleMakerImpl implements HoleMaker {
@Override
public void makeHole(int diameter) {
HoleMaker maker = new Drill(diameter);
maker.makeHole(diameter);
}
}
可以看到,hole 是由所匹配的DrillBit对象制成的。如果孔的直径小于10,则使用SmallDrillBit。如果它更大,我们使用BigDrillBit。
思路就是,要打洞,那就要有打洞的工具,这里提供一个电钻接口和钻头。电钻就是用来打洞的,所以,它就一个接口方法即可,接下来定义钻头的接口,无非就是钻头的尺寸标准,然后搞出两个钻头实现类出来,接下来就是把钻头和电钻主机组装起来咯,也就是Drill
类,里面有电钻接口+钻头(根据要钻的孔大小来确定用哪个钻头),其实也就是把几个单一的东西组合起来拥有丰富的功能,最后我们进行封装下:HoleMakerImpl
,这样只需要根据尺寸就可以打相应的孔了,对外暴露的接口极为简单,无须管内部逻辑是多么复杂
Spring使用适配器设计模式来处理不同servlet容器中的加载时编织(load-time-weaving)。在面向切面编程(AOP)中使用load-time-weaving,一种方式是在类加载期间将AspectJ的方面注入字节码。另一种方式是对类进行编译时注入或对已编译的类进行静态注入。
总结:适配器模式,其实就是我们用第一人称的视角去看世界,我想拓展我自己的技能的时候,就实行拿来主义,就好比这里的我是电钻的视角,那么我想拥有钻大孔或者小孔的功能,那就把钻头拿到手组合起来就好。
和装饰模式的区别:装饰模式属于第三人称的视角,也就是上帝视角!我只需要把几个功能性的组件给拿到手,进行组合一下,实现一个更加niubility
的功能这里提前说下,这样看下面的内容能好理解些。下面解释装饰模式
13、装饰
这种设计模式的主要作用是为给定的对象添加补充角色。举个现实的例子,就拿咖啡来讲。通常越黑越苦,你可以添加(装饰
)糖和牛奶,使咖啡不那么苦。咖啡在这里被装饰的对象,糖与牛奶是用来装饰的。可以参考下面的例子:
public class DecoratorSample {
@Test
public void test() {
Coffee sugarMilkCoffee=new MilkDecorator(new SugarDecorator(new BlackCoffee()));
assertEquals(sugarMilkCoffee.getPrice(), 6d, 0d);
}
}
// decorated
abstract class Coffee{
protected int candied=0;
protected double price=2d;
public abstract int makeMoreCandied();
public double getPrice(){
return this.price;
}
public void setPrice(double price){
this.price+=price;
}
}
class BlackCoffee extends Coffee{
@Override
public int makeMoreCandied(){
return 0;
}
@Override
public double getPrice(){
return this.price;
}
}
// abstract decorator
abstract class CoffeeDecorator extends Coffee{
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee){
this.coffee=coffee;
}
@Override
public double getPrice(){
return this.coffee.getPrice();
}
@Override
public int makeMoreCandied(){
return this.coffee.makeMoreCandied();
}
}
// concrete decorators
class MilkDecorator extends CoffeeDecorator{
public MilkDecorator(Coffee coffee){
super(coffee);
}
@Override
public double getPrice(){
return super.getPrice()+1d;
}
@Override
public int makeMoreCandied(){
return super.makeMoreCandied()+1;
}
}
class SugarDecorator extends CoffeeDecorator{
public SugarDecorator(Coffee coffee){
super(coffee);
}
@Override
public double getPrice(){
return super.getPrice()+3d;
}
@Override
public int makeMoreCandied(){
return super.makeMoreCandied()+1;
}
}
上面这个简单的装饰器的小例子是基于对父方法的调用,从而改变最后的属性(我们这里是指价格和加糖多少)。在Spring中,我们在处理与Spring管理缓存同步事务的相关类中可以 发现装饰器设计模式的例子。这个类是org.springframework.cache.transaction.TransactionAwareCacheDecorator。
这个类的哪些特性证明它是org.springframework.cache.Cache对象的装饰器?首先,与我们的咖啡示例一样,TransactionAwareCacheDecorator
的构造函数接收参数装饰对象(Cache):
private final Cache targetCache;
/**
* Create a new TransactionAwareCache for the given target Cache.
* @param targetCache the target Cache to decorate
*/
public TransactionAwareCacheDecorator(Cache targetCache) {
Assert.notNull(targetCache, "Target Cache must not be null");
this.targetCache = targetCache;
}
其次,通过这个对象,我们可以得到一个新的行为:为给定的目标缓存创建一个新的TransactionAwareCache。这个我们可以在TransactionAwareCacheDecorator
的注释中可以阅读到,其主要目的是提供缓存和Spring事务之间的同步级别。这是通过org.springframework.transaction.support.TransactionSynchronizationManager中的两种缓存方法实现的:put
和 evict
(其实最终不还是通过targetCache
来实现的么):
@Override
public void put(final Object key, final Object value) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
targetCache.put(key, value);
}
});
}
else {
this.targetCache.put(key, value);
}
}
@Override
public void evict(final Object key) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
targetCache.evict(key);
}
});
}
else {
this.targetCache.evict(key);
}
}
这种模式看起来类似于适配器,对吧?但是,它们还是有区别的。我们可以看到,适配器将对象适配到运行时环境,即。如果我们在JBoss 6中运行,我们使用与JBoss 7不同的类加载器。Decorator每次使用相同的主对象(Cache)工作,并且仅向其添加新行为(与本例中的Spring事务同步),另外,可以通过我在解读这个设计模式之前的说法来区分二者。
我们再以springboot的初始化来举个例子的,这块后面会进行仔细的源码分析的,这里就仅仅用设计模式来说下的:
/**
* Event published as early as conceivably possible as soon as a {@link SpringApplication}
* has been started - before the {@link Environment} or {@link ApplicationContext} is
* available, but after the {@link ApplicationListener}s have been registered. The source
* of the event is the {@link SpringApplication} itself, but beware of using its internal
* state too much at this early stage since it might be modified later in the lifecycle.
*
* @author Dave Syer
*/
@SuppressWarnings("serial")
public class ApplicationStartedEvent extends SpringApplicationEvent {
/**
* Create a new {@link ApplicationStartedEvent} instance.
* @param application the current application
* @param args the arguments the application is running with
*/
public ApplicationStartedEvent(SpringApplication application, String[] args) {
super(application, args);
}
}
从注释可以看出 ApplicationListener
要先行到位的,然后就是started的时候Event published
走起,接着就是Environment
配置好,ApplicationContext
进行初始化完毕,那我们去看ApplicationListener
的源码:
/**
* Listener for the {@link SpringApplication} {@code run} method. * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader} * and should declare a public constructor that accepts a {@link SpringApplication} * instance and a {@code String[]} of arguments. A new * {@link SpringApplicationRunListener} instance will be created for each run. * * @author Phillip Webb * @author Dave Syer
*/
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very * early initialization.
*/
void started();
/**
* Called once the environment has been prepared, but before the * {@link ApplicationContext} has been created. * @param environment the environment
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* Called once the {@link ApplicationContext} has been created and prepared, but * before sources have been loaded. * @param context the application context
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been * refreshed. * @param context the application context
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes. * @param context the application context or null if a failure occurred before the * context was created * @param exception any run exception or null if run completed successfully.
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
}
看类注释我们可以知道,需要实现此接口内所定义的这几个方法,ok,来看个实现类:
/**
* {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.
* <p>
* Uses an internal {@link ApplicationEventMulticaster} for the events that are fired
* before the context is actually refreshed.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final ApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public int getOrder() {
return 0;
}
@Override
public void started() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
// Listeners have been registered to the application context so we should
// use it at this point
context.publishEvent(getFinishedEvent(context, exception));
}
private SpringApplicationEvent getFinishedEvent(
ConfigurableApplicationContext context, Throwable exception) {
if (exception != null) {
return new ApplicationFailedEvent(this.application, this.args, context,
exception);
}
return new ApplicationReadyEvent(this.application, this.args, context);
}
}
从上可以看出,EventPublishingRunListener
里对接口功能的实现,主要是通过SpringApplication
ApplicationEventMulticaster
来实现的,自己不干活,挂个虚名,从上帝模式的角度来看,这不就是应用了装饰模式来实现的么。
14、单例
单例,我们最常用的设计模式。正如我们在很多Spring Framework中关于单例和原型bean的文章中已经看到过的,单例是几个bean作用域中的中的一个。此作用域在每个应用程序上下文中仅创建一个给定bean的实例。与signleton设计模式有所区别的是,Spring将实例的数量限制的作用域在整个应用程序的上下文。而Singleton设计模式在Java应用程序中是将这些实例的数量限制在给定类加载器管理的整个空间中。这意味着我们可以为两个Spring的上下文(同一份配置文件起两个容器,也就是不同端口的容器实例)使用相同的类加载器,并检索两个单例作用域的bean。
一个Java的单例例子:
public class SingletonTest {
@Test
public void test() {
President president1 = (President) SingletonsHolder.PRESIDENT.getHoldedObject();
President president2 = (President) SingletonsHolder.PRESIDENT.getHoldedObject();
assertTrue("Both references of President should point to the same object", president1 == president2);
System.out.println("president1 = "+president1+" and president2 = "+president2);
// sample output
// president1 = com.waitingforcode.test.President@17414c8 and president2 = com.waitingforcode.test.President@17414c8
}
}
enum SingletonsHolder {
PRESIDENT(new President());
private Object holdedObject;
private SingletonsHolder(Object o) {
this.holdedObject = o;
}
public Object getHoldedObject() {
return this.holdedObject;
}
}
class President {
}
这个测试例子证明,只有一个由SingletonsHolder所持有的President实例。在Spring中,我们可以在bean工厂中找到单例应用的影子(例如在org.springframework.beans.factory.config.AbstractFactoryBean中):
/**
* Expose the singleton instance or create a new prototype instance.
* @see #createInstance()
* @see #getEarlySingletonInterfaces()
*/
@Override
public final T getObject() throws Exception {
if (isSingleton()) {
return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
}
else {
return createInstance();
}
}
我们看到,当需求对象被视为单例时,它只被初始化一次,并且在每次使用同一个bean类的实例后返回。我们可以在给定的例子中看到,类似于我们以前看到的President情况。
测试用例如下所示:
public class SingletonSpringTest {
@Test
public void test() {
// retreive two different contexts
ApplicationContext firstContext = new FileSystemXmlApplicationContext("applicationContext-test.xml");
ApplicationContext secondContext = new FileSystemXmlApplicationContext("applicationContext-test.xml");
// prove that both contexts are loaded by the same class loader
assertTrue("Class loaders for both contexts should be the same",
firstContext.getClassLoader() == secondContext.getClassLoader());
// compare the objects from different contexts
ShoppingCart firstShoppingCart = (ShoppingCart) firstContext.getBean("shoppingCart");
ShoppingCart secondShoppingCart = (ShoppingCart) secondContext.getBean("shoppingCart");
assertFalse("ShoppingCart instances got from different application context shouldn't be the same",
firstShoppingCart == secondShoppingCart);
// compare the objects from the same context
ShoppingCart firstShoppingCartBis = (ShoppingCart) firstContext.getBean("shoppingCart");
assertTrue("ShoppingCart instances got from the same application context should be the same",
firstShoppingCart == firstShoppingCartBis);
}
}
这个测试案例显示了Spring单例模式与纯粹的单例设计模式的主要区别。尽管使用相同的类加载器来加载两个应用程序上下文,但是ShoppingCart的实例是不一样的。但是,当我们比较两次创建并属于相同上下文的实例时,我们认为它们是相等的。
15、命令模式
它允许将请求封装在一个对象内并附加一个回调动作(每次遇到所所谓的回调大家就只需要理解为一个函数方法就好,省的去浪费那么多脑子)。请求被封装在命令对象之下,而请求的结果被发送到接收者。命令本身不是由调用者执行。为了直白了解其中的主要思想,想象一下管理服务器的情况(远程通过ssh
操作Linux
服务器)。管理员(invoker
)在命令行(commands
)中启动一些操作,将结果发送到服务器(接收器)。在这里,所有这一切都是由客户端的终端(也就是我们用的xshell
)来完成的。搞个Demo
来说明一下(对于命令,它的动作就是执行,对于管理员来讲,我们的动作其实就是一个回车,执不执行当然是管理员说的算了,执行交给命令对象了,服务器最后就是一个展示结果):
public class CommandTest {
// This test method is a client
@Test
public void test() {
Administrator admin = new Administrator();
Server server = new Server();
// start Apache
admin.setCommand(new StartApache(server));
admin.typeEnter();
// start Tomcat
admin.setCommand(new StartTomcat(server));
admin.typeEnter();
// check executed commands
int executed = server.getExecutedCommands().size();
assertTrue("Two commands should be executed but only "+
executed+ " were", executed == 2);
}
}
// commands
abstract class ServerCommand {
protected Server server;
public ServerCommand(Server server) {
this.server = server;
}
public abstract void execute();
}
class StartTomcat extends ServerCommand {
public StartTomcat(Server server) {
super(server);
}
@Override
public void execute() {
server.launchCommand("sudo service tomcat7 start");
}
}
class StartApache extends ServerCommand {
public StartApache(Server server) {
super(server);
}
@Override
public void execute() {
server.launchCommand("sudo service apache2 start");
}
}
// invoker
class Administrator {
private ServerCommand command;
public void setCommand(ServerCommand command) {
this.command = command;
}
public void typeEnter() {
this.command.execute();
}
}
// receiver
class Server {
// as in common terminals, we store executed commands in history
private List<String> executedCommands = new ArrayList<String>();
public void launchCommand(String command) {
System.out.println("Executing: "+command+" on server");
this.executedCommands.add(command);
}
public List<String> getExecutedCommands() {
return this.executedCommands;
}
}
命令模式不仅允许封装请求(ServerCommand)并将其传输到接收器(Server),而且还可以更好地处理给定的请求。在这里,这种更好的处理是通过存储命令的执行历史。在Spring中,我们在beanFactory后置处理器的特性中来找到指令设计模式的原理。要通过快速对它们进行定义,应用程序上下文会启动后置处理器,并可以用来对创建的bean进行一些操作。
当我们将先前Demo里呈现的命令逻辑转换并对比到Spring bean工厂后处理器
时,我们可以区分以下actors
:后置处理器bean(是指实现BeanFactoryPostProcessor
接口)是命令,org.springframework.context.support.PostProcessorRegistrationDelegate是调用者(它执行postProcessBeanFactory
方法注册所有的后置处理器bean,此处看下面第二段代码)和接收器org.springframework.beans.factory.config.ConfigurableListableBeanFactory可以在元素(bean)构造初始化之前修改它们(例如:在初始化bean之前可以更改属性)。
另外,回顾下上面的那个Demo,和我们的Demo中的命令历史管理一样。PostProcessorRegistrationDelegate
包含一个内部类BeanPostProcessorChecker
,它可以记录当一个bean不符合处理条件的情况。
可以观察PostProcessorRegistrationDelegate
中的两段代码:
/**
* BeanPostProcessor that logs an info message when a bean is created during
* BeanPostProcessor instantiation, i.e. when a bean is not eligible for
* getting processed by all BeanPostProcessors.
*/
private static class BeanPostProcessorChecker implements BeanPostProcessor {
private static final Log logger = LogFactory.getLog(BeanPostProcessorChecker.class);
private final ConfigurableListableBeanFactory beanFactory;
private final int beanPostProcessorTargetCount;
public BeanPostProcessorChecker(ConfigurableListableBeanFactory beanFactory, int beanPostProcessorTargetCount) {
this.beanFactory = beanFactory;
this.beanPostProcessorTargetCount = beanPostProcessorTargetCount;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean != null && !(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
if (logger.isInfoEnabled()) {
logger.info("Bean '" + beanName + "' of type [" + bean.getClass() +
"] is not eligible for getting processed by all BeanPostProcessors " +
"(for example: not eligible for auto-proxying)");
}
}
return bean;
}
private boolean isInfrastructureBean(String beanName) {
if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {
BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);
return RootBeanDefinition.ROLE_INFRASTRUCTURE == bd.getRole();
}
return false;
}
}
定义后的调用,用的就是ConfigurableListableBeanFactory
的实例(看BeanPostProcessorChecker
注释):
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
//BeanPostProcessorChecker
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(beanFactory, orderedPostProcessors);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(beanFactory, internalPostProcessors);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
总结一个过程就是,我要BeanFactory里面得到对象(也就是为了得到一个命令的执行结果),那么,想要在得到对象的时候就已经实现了一些对其修改的想法,那么就通过后置处理器,也是就实现了后置处理器接口的beans(命令里可以通过传入不同的参数来得到不同结果,或者对命令的脚本进行修改),然后还需要一个执行者(我们在做自动化运维的时候,不止操作一个脚本,这里的PostProcessorRegistrationDelegate
就是集中来管理这些的),最后得到的结果就由BeanFactory
来展示了。
16、访问者模式
抽象一点就是通过另一种类型的对象来使一个对象访问。在这个简短定义中,使用这个设计模式中的对象将被视为访问者或对象可被访问。第一个访问者要有可访问支持。这个模式的一个现实的例子可以是一个汽车质检员,他们检查一些汽车零件,比如轮子,制动器和发动机,以判断汽车质量是否合格。我们来做个JUnit测试用例:
public class VisitorTest {
@Test
public void test() {
CarComponent car = new Car();
Mechanic mechanic = new QualifiedMechanic();
car.accept(mechanic);
assertTrue("After qualified mechanics visit, the car should be broken",
car.isBroken());
Mechanic nonqualifiedMechanic = new NonQualifiedMechanic();
car.accept(nonqualifiedMechanic);
assertFalse("Car shouldn't be broken becase non qualified mechanic " +
" can't see breakdowns", car.isBroken());
}
}
// visitor
interface Mechanic {
public void visit(CarComponent element);
public String getName();
}
class QualifiedMechanic implements Mechanic {
@Override
public void visit(CarComponent element) {
element.setBroken(true);
}
@Override
public String getName() {
return "qualified";
}
}
class NonQualifiedMechanic implements Mechanic {
@Override
public void visit(CarComponent element) {
element.setBroken(true);
}
@Override
public String getName() {
return "unqualified";
}
}
// visitable
abstract class CarComponent {
protected boolean broken;
public abstract void accept(Mechanic mechanic);
public void setBroken(boolean broken) {
this.broken = broken;
}
public boolean isBroken() {
return this.broken;
}
}
class Car extends CarComponent {
private boolean broken = false;
private CarComponent[] components;
public Car() {
components = new CarComponent[] {
new Wheels(), new Engine(), new Brake()
};
}
@Override
public void accept(Mechanic mechanic) {
this.broken = false;
if (mechanic.getName().equals("qualified")) {
int i = 0;
while (i < components.length && this.broken == false) {
CarComponent component = components[i];
mechanic.visit(component);
this.broken = component.isBroken();
i++;
}
}
// if mechanic isn't qualified, we suppose that
// he isn't able to see breakdowns and so
// he considers the car as no broken
// (even if the car is broken)
}
@Override
public boolean isBroken() {
return this.broken;
}
}
class Wheels extends CarComponent {
@Override
public void accept(Mechanic mechanic) {
mechanic.visit(this);
}
}
class Engine extends CarComponent {
@Override
public void accept(Mechanic mechanic) {
mechanic.visit(this);
}
}
class Brake extends CarComponent {
@Override
public void accept(Mechanic mechanic) {
mechanic.visit(this);
}
}
在这个例子中,我们可以看到他们有两个机制(访问者,其实就是免检和不免检):合格和不合格。暴露于他们的可见对象是汽车。通过其接受方式,决定哪个角色应该适用于被访问者(通过代码mechanic.getName().equals("qualified")
来判断)。当访问者合格时,Car让他分析所有组件。如果访问者不合格,Car认为其干预是无用的,并且在方法isBroken()
中直接返回false
(其实就是为了达到一个免检的效果)。Spring在beans配置中实现了访问者设计模式
。为了观察,我们可以看看org.springframework.beans.factory.config.BeanDefinitionVisitor对象,该对象用于解析bean元数据
并将其解析为String
(例如:具有作用域或工厂方法名称的XML属性)或Object
(例如:构造函数定义中的参数)。已解析的值在与分析的bean关联的BeanDefinition
实例中进行判断设置。具体的源码请看BeanDefinitionVisitor
的代码片段:
/**
* Traverse the given BeanDefinition object and the MutablePropertyValues
* and ConstructorArgumentValues contained in them.
* @param beanDefinition the BeanDefinition object to traverse
* @see #resolveStringValue(String)
*/
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
visitPropertyValues(beanDefinition.getPropertyValues());
ConstructorArgumentValues cas = beanDefinition.
getConstructorArgumentValues();
visitIndexedArgumentValues(cas.
getIndexedArgumentValues());
visitGenericArgumentValues(cas.
getGenericArgumentValues());
}
protected void visitParentName(BeanDefinition beanDefinition) {
String parentName = beanDefinition.getParentName();
if (parentName != null) {
String resolvedName = resolveStringValue(parentName);
if (!parentName.equals(resolvedName)) {
beanDefinition.setParentName(resolvedName);
}
}
}
在这种情况下,他们只是访问方式,没有对访问者做任何补充的控制(在Demo里对car的质检员做了控制)。这里访问包括分析给定BeanDefinition
的参数,并将其替换为已解析对象。
网友评论