人的知识就好比一个圆圈,圆圈里面是已知的,圆圈外面是未知的。你知道得越多,圆圈也就越大,你不知道的也就越多。

0%

定义

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易实现。

角色

  • 外观(Facade):提供一个外观接口,对外,它提供一个易于客户端访问的接口,对内,它可以访问子系统中的所有功能。
  • 子系统(Subsystem):子系统在整个系统中可以是一个或多个模块,每个模块都有若干类组成,这些类可能相互之间有着比较复杂的关系。

类图

Facade UML

实现

1
2
3
4
5
6
7
8
9
public class Facade {
private SubsystemA systemA;
private SubsystemB systemB;
private SubsystemC systemC;

public void operation() {
// system A、B、C operations
}
}

优缺点

优点

  • 减少系统相互依赖
  • 提高了灵活性
  • 提高了安全性

缺点

  • 不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。

适用场景

  • 为复杂的模块或子系统提供外界访问的模块
  • 子系统相对独立
  • 预防低水平人员带来的风险

模式应用

Spring IoC

Spring IoC容器中的ApplicationContext同时实现了多个接口,并通过引用这些接口的实例,对外统一提供:加载配置、解析资源、创建Bean、提供环境、启动流程等功能,客户代码只需要操作context就可以获取spring的提供的功能,而无需关心内部的细节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

@Nullable
String getId();

String getApplicationName();

String getDisplayName();

long getStartupDate();

@Nullable
ApplicationContext getParent();

AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}

实际应用

controller、service可以理解为就是门面模式的运用。

定义

代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。

角色

  • 抽象主题(Subject):声明真实对象和代理对象共同的接口。
  • 真实主题(Real Subject):业务逻辑的具体执行者。
  • 代理(Proxy):也叫做委托类、代理类,它负责对真实对象的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后工作。

类图

Proxy UML

实现

普通代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface Subject {

void request();
}

public class RealSubject implements Subject {

public void request() {
System.out.println("real request");
}
}

public class Proxy implements Subject {
private Subject subject;

public Proxy(Subject subject) {
this.subject = subject;
}

public void request() {
System.out.println("before request");
subject.request();
System.out.println("after request");

}
}

jdk动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SubjectInvocationHandler implements InvocationHandler {
private Subject subject;

public SubjectInvocationHandler(Subject subject) {
this.subject = subject;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke");
Object result = method.invoke(subject, args);
System.out.println("after invoke");
return result;
}

public static void main(String[] args) {
Subject subject = new RealSubject();
SubjectInvocationHandler invocationHandler = new SubjectInvocationHandler(subject);
Subject proxy = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), invocationHandler);
proxy.request();
}
}

cglib动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SubjectProxy implements MethodInterceptor {

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before invoke");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after invoke");
return result;
}

public static void main(String[] args) {
SubjectProxy subjectProxy = new SubjectProxy();

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(subjectProxy);

Subject proxy = (RealSubject) enhancer.create();
proxy.request();
}
}

优缺点

优点

  • 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
  • 客户端可以针对抽象类进行编程,增加和更换代理类无需修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。

缺点

  • 由于在客户端和真实类之间增加了代理类,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
  • 实现代理模式需要额外的工作,而且有些代理模式的实现较为复杂,例如远程代理。

适用场景

  • 控制访问
  • 远程代理
    远程代理可以作为另一个JVM上对象的本地代表。调用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再由代理将结果转给客户。
  • 虚拟代理
    虚拟代理作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟对象来扮演对象的替身。对象创建后,代理会将请求直接委托给对象。
  • 保护代理
  • 防火墙代理
    控制网络资源的访问,保护主题免于“坏客户”的侵害。
  • 智能引用代理
    当主题被引用时,进行额外的动作,例如计算一个对象被引用的次数。
  • 缓存代理
    为开销大的运算结果提供暂时存储;它也允许多个客户共享结果,以减少计算或网络延迟。
  • 同步代理
    在多线程的情况下为主题提供安全的访问。
  • 复杂隐藏代理
    用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也成为外观代理,这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口。
  • 写入时复制代理
    用来控制对象的复制,方法是延迟对象的复制,知道客户真的需要为止。这是虚拟代理的变体。

模式应用

Spring AOP模式默认采用JDK动态代理来实现,可以配置为使用cglib动态代理。
JDK动态代理只能对实现了接口的类生成代理,而不能针对类;cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final。

Spring AOP代理类最终都是由AopProxyFactory创建的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 如果设置了代理应该执行积极的优化(默认为false),或显示设置使用cglib代理,或代理的对象没有实现接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果目标类是接口,或者已经是Jdk的动态代理类,则创建jdk动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}

/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}

}

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
...

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

...
}

定义

适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

角色

  • 目标(Target):所期待得到的接口。
  • 源(Adaptee):需要适配的接口。
  • 适配器(Adapter):设配器模式的核心。把原接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

类图

Adaptee UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface Target {

void request();
}

public class Adaptee {

public void specificRequest() {
System.out.println("specific request");
}
}

public class Adapter implements Target {
private Adaptee adaptee;

public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}

public void request() {
adaptee.specificRequest();
}
}

优缺点

优点

  • 可以让两个没有关联的类一起运行
  • 提高了类的复用
  • 增加了类的透明度
  • 灵活性好

缺点

  • 过多的使用适配器,会让系统非常凌乱,不易进行整体把握。比如,明明里看到的是调用A接口,其内部被适配成了B接口的实现,一个系统如果出现太多这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构
  • 由于Java至多继承一个类,所以只能适配一个适配者类,而且目标类必须是抽象类

适用场景

  • 当需要修改一个已经投产的接口时,适配器模式可以优先考虑
  • 系统扩展时,需要使用一个已有或新建立的类,但是这个类不符合系统的接口
  • 与二方或三方系统进行数据交互的时候,双方使用的对象不同,需要进行转换的时候

模式应用

FutureTask

FutureTask继承自Future接口,它代表异步计算的结果。其源码中有以下两个构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class FutureTask<V> implements RunnableFuture<V> {

...

// 构造函数1
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}

// 构造函数2
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}

...

}

在构造函数2中,通过调用Executors.callable(runnable, result)Runnable接口转换为Callable接口,此处即用到了适配器模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Executors {

...

public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}

static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}

...

}

Spring MVC

在Spring MVC中,DispatcherServlet通过HandlerMappings找到相应的handler,即controller(宽泛的概念controller,如Controller、HttpRequestHandler、Servlet等等),然后执行controller中相应的方法并返回ModelAndView。
由于handler的类型不同,因此调用方式就是不确定的。如果直接调用controller方法,那么调用的时候就得不断使用if else来判断是哪一种子类然后执行。如果后面要扩展controller,就得修改原来的代码,这样就违背了开闭原则。
Spring创建了一个适配器接口(HandlerAdapter),使得每一种handler有一种对应的适配器实现类,让适配器代替handler执行相应的方法。这样在扩展controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

Controller UML
HandlerAdapter UML

Spring IoC

在Spring IoC中,有多重方式可以自定义Bean销毁前操作,如既可以实现DisposableBean接口,又可以实现DestructionAwareBeanPostProcessor,还可以自定义destroy方法,那么在Spring内部要怎么统一处理呢?
这时就可以利用适配器模式,将上面各中方式都适配成一个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

/**
* @see AbstractBeanFactory
*/
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
// Register a DisposableBean implementation that performs all destruction
// work for the given bean: DestructionAwareBeanPostProcessors,
// DisposableBean interface, custom destroy method.
registerDisposableBean(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
else {
// A bean with a custom scope...
Scope scope = this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
}
scope.registerDestructionCallback(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
}
}

class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
....
public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
List<BeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) {

Assert.notNull(bean, "Disposable bean must not be null");
this.bean = bean;
this.beanName = beanName;
this.invokeDisposableBean =
(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
this.acc = acc;
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
this.destroyMethodName = destroyMethodName;
Method destroyMethod = determineDestroyMethod(destroyMethodName);
if (destroyMethod == null) {
if (beanDefinition.isEnforceDestroyMethod()) {
throw new BeanDefinitionValidationException("Could not find a destroy method named '" +
destroyMethodName + "' on bean with name '" + beanName + "'");
}
}
else {
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
if (paramTypes.length > 1) {
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
beanName + "' has more than one parameter - not supported as destroy method");
}
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
beanName + "' has a non-boolean parameter - not supported as destroy method");
}
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
}
this.destroyMethod = destroyMethod;
}
this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
}

...
}

定义

享元模式让某个类的一个实例能用来提供许多“虚拟实例”。

角色

  • 抽象享元类(Flyweight):通常是一个接口或抽象类,向外界提供享元对象的内部数据或外部数据。
  • 具体享元类(Concrete Flyweight):具体实现内部数据共享的类。
  • 享元工厂类(Flyweight Factory):用于创建和管理享元对象的工厂类。

类图

Flyweight UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* 抽象享元类
*/
public interface Flyweight {

/**
* 操作
* 外部状态在使用时由外部设置,不保存在享元对象中,即使是同一个对象,在每一次调用时也可以传入不同的外部状态
*
* @param extrinsicState 外部状态
*/
void operation(String extrinsicState);

/**
* 内部状态
*
* @return 内部状态
*/
String getIntrinsicState();
}

/**
* 具体享元类
*/
public class ConcreteFlyweight implements Flyweight {
/**
* 内部状态
*/
private String intrinsicState;

public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}

@Override
public void operation(String extrinsicState) {
System.out.println(String.format("内部状态:%s;外部状态:%s", intrinsicState, extrinsicState));
}

@Override
public String getIntrinsicState() {
return intrinsicState;
}
}

/**
* 享元工厂类
*/
public class FlyweightFactory {
public static final Map<String, Flyweight> FLYWEIGHT_MAP = new HashMap<>();

public static Flyweight getFlyweight(String intrinsicState) {
// 如果在享元池中存在对象,则直接获取
if (FLYWEIGHT_MAP.containsKey(intrinsicState)) {
return FLYWEIGHT_MAP.get(intrinsicState);
}
// 否则,新建对象,并放入到享元池
else {
ConcreteFlyweight flyweight = new ConcreteFlyweight(intrinsicState);
FLYWEIGHT_MAP.put(intrinsicState, flyweight);
return flyweight;
}
}
}

优缺点

优点

  • 大大减少对象的创建,降低系统的内存,使效率提高

缺点

  • 提高了系统的复杂度,需要分理出外部状态和内部状态,而且外部状态具有固化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱

适用场景

  • 系统有大量相似对象
  • 需要缓冲池的场景

模式应用

  • 连接池
  • 线程池
  • 字符串常量池

定义

命令模式将“请求”封装对象,以便使用不同的请、队列或日志来参数化其他第项。命令模式也支持可撤销的操作。

角色

  • 抽象命令类(Command):声明执行命令的接口,拥有执行命令的抽象方法 execute()。这个接口也具备一个undo()方法。
  • 具体命令角色(Concrete Command):是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  • 接收者(Receiver):执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  • 调用者(Invoker):是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

类图

Command UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public interface Command {

void execute();

void undo();
}

public class ConcreteCommand implements Command {
/**
* 命令接收者
*/
private Receiver receiver;

public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}

@Override
public void execute() {
receiver.action();
}

@Override
public void undo() {
// TODO
}
}

public class Receiver {

public void action() {
System.out.println("do action");
}
}

public class Invoker {
private Command command;

public void setCommand(Command command) {
this.command = command;
}

public void invoke() {
command.execute();
}
}

public class Client {

public static void main(String[] args) {
Receiver receiver = new Receiver();
ConcreteCommand command = new ConcreteCommand(receiver);

Invoker invoker = new Invoker();
invoker.setCommand(command);

invoker.invoke();
}
}

优缺点

优点

  • 降低了系统耦合度
  • 新的命令可以很容易添加到系统中去

缺点

  • 使用命令模式可能会导致某些系统有过多的具体命令类

适用场景

  • 认为是命令的地方都可以使用命令模式,比如:1.GUI中每一个按钮都是一条命令;2.模拟CMD。

模式应用

Hystrix

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public abstract class HystrixCommand<R> extends AbstractCommand<R> implements HystrixExecutable<R>, HystrixInvokableInfo<R>, HystrixObservable<R> {
...

public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}

public Future<R> queue() {
/*
* The Future returned by Observable.toBlocking().toFuture() does not implement the
* interruption of the execution thread when the "mayInterrupt" flag of Future.cancel(boolean) is set to true;
* thus, to comply with the contract of Future, we must wrap around it.
*/
final Future<R> delegate = toObservable().toBlocking().toFuture();

final Future<R> f = new Future<R>() {

@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (delegate.isCancelled()) {
return false;
}

if (HystrixCommand.this.getProperties().executionIsolationThreadInterruptOnFutureCancel().get()) {
/*
* The only valid transition here is false -> true. If there are two futures, say f1 and f2, created by this command
* (which is super-weird, but has never been prohibited), and calls to f1.cancel(true) and to f2.cancel(false) are
* issued by different threads, it's unclear about what value would be used by the time mayInterruptOnCancel is checked.
* The most consistent way to deal with this scenario is to say that if *any* cancellation is invoked with interruption,
* than that interruption request cannot be taken back.
*/
interruptOnFutureCancel.compareAndSet(false, mayInterruptIfRunning);
}

final boolean res = delegate.cancel(interruptOnFutureCancel.get());

if (!isExecutionComplete() && interruptOnFutureCancel.get()) {
final Thread t = executionThread.get();
if (t != null && !t.equals(Thread.currentThread())) {
t.interrupt();
}
}

return res;
}

@Override
public boolean isCancelled() {
return delegate.isCancelled();
}

@Override
public boolean isDone() {
return delegate.isDone();
}

@Override
public R get() throws InterruptedException, ExecutionException {
return delegate.get();
}

@Override
public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return delegate.get(timeout, unit);
}

};

/* special handling of error states that throw immediately */
if (f.isDone()) {
try {
f.get();
return f;
} catch (Exception e) {
Throwable t = decomposeException(e);
if (t instanceof HystrixBadRequestException) {
return f;
} else if (t instanceof HystrixRuntimeException) {
HystrixRuntimeException hre = (HystrixRuntimeException) t;
switch (hre.getFailureType()) {
case COMMAND_EXCEPTION:
case TIMEOUT:
// we don't throw these types from queue() only from queue().get() as they are execution errors
return f;
default:
// these are errors we throw from queue() as they as rejection type errors
throw hre;
}
} else {
throw Exceptions.sneakyThrow(t);
}
}
}

return f;
}

...
}

定义

策略模式定义了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

角色

  • 抽象策略类(Strategy):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口。Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  • 具体策略类(Concrete Strategy):实现了Strategy定义的接口,提供具体的算法实现。
  • 应用场景(Context):内部维护一个Strategy的实例,负责动态设置运行时Strategy具体的实现算法。

类图

Strategy UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public interface Strategy {

void operation();
}

public class StrategyA implements Strategy {

public void operation() {
System.out.println("strategy A");
}
}

public class StrategyB implements Strategy {

public void operation() {
System.out.println("strategy B");
}
}

public class StrategyContext {
private Strategy strategy;

public StrategyContext(Strategy strategy) {
this.strategy = strategy;
}

public void operation() {
strategy.operation();
}

public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
}

优缺点

优点

  • 算法可以自由切换
  • 避免适用多重条件判断
  • 扩展性良好

缺点

  • 策略类会增多
  • 多有策略类都需要对外暴露

适用场景

  • 一个系统需要动态地在集中算法中选择一种
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好适用多重条件选择语句来实现
  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么是策略模式可以动态地让一个对象在许多行为中选择一种行为。

模式应用

SecurityContextHolderStrategy UML

实际应用

CacheStrategy UML

定义

建造者模式封装一个产品的构造过程,并允许按步骤构造。

角色

  • 抽象建造者(Builder):用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑。
  • 具体建造者(Concrete Builder):实现抽象建造者中定义的所有方法,并且返回一个组建好的产品实例。
  • 产品(Product):建造者模式中的复杂对象,一个系统中会有多于一个的产品类,这些产品类并不一定有共同的接口,完全可以是不相关联的。
  • 导演者(Director):负责安排已有模块的顺序,然后告诉Builder开始建造。

类图

Builder UML

优缺点

优点

  • 使用建造者模式可以使客户端不必知道产品内部组成的细节
  • 具体的建造者类之间是相互独立的,这有利于系统的扩展
  • 具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响

缺点

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大

适用场景

  • 需要生成的对象具有复杂的内部结构
  • 需要生成的对象内部属性本身相互依赖

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* 方式一
*/
public class ProductBuilder {
private Product product;

public ProductBuilder() {
this.product = new Product();
}

public Product buildPartA(String partA) {
product.setPartA(partA);
return this.product;
}

public Product buildPartB(String partB) {
product.setPartB(partB);
return this.product;
}

public Product buildPartC(String partC) {
product.setPartC(partC);
return this.product;
}

public Product build() {
return this.product;
}
}

/**
* 方式二
*/
public class ProductBuilder {
private Product product;

public ProductBuilder() {
this.product = new Product();
}

public ProductBuilder buildPartA(String partA) {
product.setPartA(partA);
return this;
}

public ProductBuilder buildPartB(String partB) {
product.setPartB(partB);
return this;
}

public ProductBuilder buildPartC(String partC) {
product.setPartC(partC);
return this;
}

public Product build() {
return this.product;
}
}

模式应用

Spring Data ElasticSearch中大量使用了建造者模式,以IndexQueryBuilder为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class IndexQueryBuilder {
private String id;
private Object object;
private Long version;
private String indexName;
private String type;
private String source;
private String parentId;

public IndexQueryBuilder() {
}

public IndexQueryBuilder withId(String id) {
this.id = id;
return this;
}

public IndexQueryBuilder withObject(Object object) {
this.object = object;
return this;
}

public IndexQueryBuilder withVersion(Long version) {
this.version = version;
return this;
}

public IndexQueryBuilder withIndexName(String indexName) {
this.indexName = indexName;
return this;
}

public IndexQueryBuilder withType(String type) {
this.type = type;
return this;
}

public IndexQueryBuilder withSource(String source) {
this.source = source;
return this;
}

public IndexQueryBuilder withParentId(String parentId) {
this.parentId = parentId;
return this;
}

public IndexQuery build() {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(this.id);
indexQuery.setIndexName(this.indexName);
indexQuery.setType(this.type);
indexQuery.setObject(this.object);
indexQuery.setParentId(this.parentId);
indexQuery.setSource(this.source);
indexQuery.setVersion(this.version);
return indexQuery;
}
}

实际应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
public class ExportDefinition {
/**
* 每张表最多只能存储1048576行数据
*/
public static final int MAX_ROW = 1048576;

/**
* 表名
*/
private String sheetName;

/**
* 标题定义
*/
private TitleDefinition title;

/**
* label定义列表
*/
private List<LabelDefinition> labels;

/**
* 属性定义列表
*/
private List<PropertyDefinition> properties;

/**
* 子定义列表(适用于子表)
*/
private List<ExportDefinition> childrenDefinitions;

/**
* 主题函数工厂(函数接受一个Workbook并返回一个CellStyle)
*/
private StyleFunctionFactory styleFunctionFactory;

/**
* 内存中数据量达到多少时刷新
*/
private int windowSize;

/**
* 是否压缩临时excel
*/
private boolean compress;

/**
* 打包导出时,每个excel的最大存储数据量
*/
private int packageSize;

private ExportDefinition(Builder builder) {
this.sheetName = builder.sheetName;
this.title = builder.title;
this.labels = builder.labels;
this.properties = builder.properties;
this.childrenDefinitions = builder.childrenDefinitions;
this.styleFunctionFactory = builder.styleFunctionFactory;
this.windowSize = builder.windowSize;
this.compress = builder.compress;
this.packageSize = builder.packageSize;

if (this.title != null) {
this.title.setWidth(this.properties.size());
}
}

public String getSheetName() {
return sheetName;
}

public TitleDefinition getTitle() {
return title;
}

public List<LabelDefinition> getLabels() {
return labels;
}

public List<PropertyDefinition> getProperties() {
return properties;
}

public List<ExportDefinition> getChildrenDefinitions() {
return childrenDefinitions;
}

public StyleFunctionFactory getStyleFunctionFactory() {
return styleFunctionFactory;
}

public int getWindowSize() {
return windowSize;
}

public boolean isCompress() {
return compress;
}

public int getPackageSize() {
return packageSize;
}

/**
* 返回表头高度
*/
public int getHeaderHeight() {
return labels.stream().map(LabelDefinition::getHeight).max(Integer::compareTo).orElse(0);
}

/**
* 返回表头宽度
*/
public int getHeaderWidth() {
return labels.stream().map(LabelDefinition::getWidth).max(Integer::compareTo).orElse(0);
}

/**
* 返回表头起始行索引
*/
public int getHeaderBeginRowIndex() {
return getTitle() == null ? 0 : getTitle().getHeight() + 1;
}

/**
* 返回数据起始行索引
*/
public int getDataBeginRowIndex() {
return getHeaderBeginRowIndex() + getHeaderHeight();
}

/**
* 构造器
*/
public static class Builder {
private String sheetName;

private TitleDefinition title;

private List<LabelDefinition> labels;

private List<PropertyDefinition> properties;

private List<ExportDefinition> childrenDefinitions;

/**
* 没有定义主题函数工厂,就使用默认的
*/
private StyleFunctionFactory styleFunctionFactory = new DefaultStyleFunctionFactory();

/**
* 内存中数据量达到100就刷新
*/
private int windowSize = SXSSFWorkbook.DEFAULT_WINDOW_SIZE;

/**
* 默认不压缩临时excel
*/
private boolean compress;

/**
* 打包导出时,默认每个excel的最大存储数据量为200000
*/
private int packageSize = 200000;

public Builder sheetName(String sheetName) {
this.sheetName = sheetName;
return this;
}

public Builder title(TitleDefinition title) {
this.title = title;
return this;
}

public Builder labels(List<LabelDefinition> labels) {
this.labels = labels;
return this;
}

public Builder properties(List<PropertyDefinition> properties) {
this.properties = properties;
return this;
}

public Builder childrenDefinitions(List<ExportDefinition> childrenDefinitions) {
this.childrenDefinitions = childrenDefinitions;
return this;
}

public Builder styleFunctionFactory(StyleFunctionFactory functionFactory) {
this.styleFunctionFactory = functionFactory;
return this;
}

public Builder windowSize(int windowSize) {
this.windowSize = windowSize;
return this;
}

public Builder compress(boolean compress) {
this.compress = compress;
return this;
}

public Builder packageSize(int packageSize) {
this.packageSize = packageSize;
return this;
}

public Builder simpleTitle(String title) {
this.title = new TitleDefinition.Builder().name(title).height(1).build();
return this;
}

public Builder simpleLabels(List<String> labels) {
this.labels = labels.stream()
.map(label -> new LabelDefinition.Builder().name(label).build())
.collect(Collectors.toList());
return this;
}

public Builder simpleProperties(List<String> properties) {
this.properties = properties.stream()
.map(property -> new PropertyDefinition.Builder().name(property).build())
.collect(Collectors.toList());
return this;
}

public ExportDefinition build() {
if (!CollectionUtils.isEmpty(this.properties) && !CollectionUtils.isEmpty(this.labels) &&
this.properties.size() != this.labels.stream()
.map(LabelDefinition::getWidth)
.reduce((pre, cur) -> pre + cur)
.orElse(0)) {
throw new ExportException("labels属性与properties属性未一一对应");
}
return new ExportDefinition(this);
}
}
}

定义

模板模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

角色

  • 抽象类(Abstract Class):实现了模板方法,定义了算法的骨架。
  • 具体类(Concrete Class):实现抽象类中的抽象方法,以完成完整的算法。

类图

Template UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public abstract class AbstractClass {

final void templateMethod() {
operation1();
operation2();
}

protected abstract void operation1();

protected abstract void operation2();
}

public class ConcreteClassA extends AbstractClass {

@Override
protected void operation1() {
System.out.println("operation1 of a");

}

@Override
protected void operation2() {
System.out.println("operation2 of a");
}
}

public class ConcreteClassB extends AbstractClass {

@Override
protected void operation1() {
System.out.println("operation1 of b");

}

@Override
protected void operation2() {
System.out.println("operation2 of b");
}
}

优缺点

优点

  • 封装必变部分,扩展可变部分
  • 提取公共代码,便于维护
  • 行为由父类控制,子类实现

缺点

  • 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大

适用场景

  • 有多个子类共有的方法,且逻辑相同
  • 重要的、复杂的方法,都可以考虑作为模板方法

模式应用

Spring Security

TokenGranter UML

Java AQS抽象队列同步器

队列同步器(AbstractQueuedSynchronizer)是基于模板方法模式设计的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器聚合在自定义的同步组件中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。

Java线程池

AbstractExecutorService

实际应用

需求:很多单据需要提供参照服务,其模式是固定的:接受参数 -> 解析参数 -> 查询 -> 类型转转 -> 返回,
除了查询方法不同之外,其他各步骤实现都可以是相同的。
实现:提供模板类,各单据继承该模板类。
代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class ReferService {

// 模板方法
public final <T> ReferPageResponse<T> queryReferPage(Map<String, Object> params, T clazz) {
Pageable pageable = buildPageable(params);
Page<T> page = queryForPage(pageable, params, clazz);
return buildResponse(page);
}

// 数据查询由子类实现
protected abstract <T> Page<T> queryForPage(Pageable pageable, Map<String, Object> params, T clazz);

private Pageable buildPageable(Map<String, Object> params) {
// TODO
return null;
}

private <T> ReferPageResponse<T> buildResponse(Page<T> page) {
// TODO
return null;
}
}

通过使用Java8 lambada函数,可以改进上面的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ReferUtils {

public final <T> ReferPageResponse<T> queryReferPage(Map<String, Object> params, BiFunction<Pageable, Map, Page<T>> function) {
Pageable pageable = buildPageable(params);
Page<T> page = function.apply(pageable, params);
return buildResponse(page);
}

private Pageable buildPageable(Map<String, Object> params) {
// TODO
return null;
}

private <T> ReferPageResponse<T> buildResponse(Page<T> page) {
// TODO
return null;
}
}

这样就不需要每个单据都创建子类了,而是改为传递数据查询函数。
简单业务中,如用到了行为型设计模式,基本都可以改为使用Java8 lambada函数,以避免类的增加。

简单工厂方法

简单工厂方法不是一个设计模式,更像是一种编程习惯。

类图

SimpleFactory UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
public class SimpleFactory {

public static Product create(String type) {
if ("A".equals(type)) {
return new ProjectA();
} else if ("B".equals(type)) {
return new ProjectB();
} else{
throw new RuntimeException("不支持该类产品的创建");
}
}
}

工厂方法模式

定义

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

类图

Factory UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface Factory {

Product create(String type);
}

public class FactoryA implements Factory {

public Product create(String type) {
if ("A".equals(type)) {
return new ProjectA();
} else{
throw new RuntimeException("不支持该类产品的创建");
}
}
}

public class FactoryB implements Factory {

public Product create(String type) {
if ("B".equals(type)) {
return new ProjectB();
} else{
throw new RuntimeException("不支持该类产品的创建");
}
}
}

抽象工厂模式

定义

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确执行具体类。

类图

AbstractFactory UML

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public interface Factory {

ProductA createProductA(String type);

ProductB createProductB(String type);
}

public class FactoryA implements Factory {

public ProductA createProductA(String type) {
if ("1".equals(type)) {
return new ProductA1();
} else{
throw new RuntimeException("不支持该类产品的创建");
}
}

public ProductB createProductB(String type) {
if ("1".equals(type)) {
return new ProductB1();
} else{
throw new RuntimeException("不支持该类产品的创建");
}
}
}

public class FactoryB implements Factory {

public ProductA createProductA(String type) {
if ("2".equals(type)) {
return new ProductA2();
} else{
throw new RuntimeException("不支持该类产品的创建");
}
}

public ProductB createProductB(String type) {
if ("2".equals(type)) {
return new ProductB2();
} else{
throw new RuntimeException("不支持该类产品的创建");
}
}
}

小结

  • 所有的工厂都是用来封装对象的创建
  • 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象
  • 工厂方法允许类将实例化延迟到子类中执行

模式应用

ThreadFactory

在使用线程池时,我们可以传递ThreadFactory参数到ThreadPoolExecutor构造函数中,这样就可以方便的设置从池中获取到的线程的名字等属性。在Executors中提供了默认实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Executors {

...

static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;

DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}

public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}

...

}

Spring IoC

BeanFactory

实际应用

根据页面类型决定使用哪一个PageDataBuilder构建页面数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class PageDataBuilderFactory {

public static PageDataBuilder getPageDataBuilder(String pageType) {
PageTypeEnum pageTypeEnum;
try {
pageTypeEnum = PageTypeEnum.valueOfName(pageType);
} catch (Exception e) {
throw new RuntimeException(String.format("尚未支持该页面类型:%s", pageType));
}

switch (pageTypeEnum) {
case LIST_PAGE:
return new ListPageDataBuilder();
case CARD_PAGE:
return new CardPageDataBuilder();
default:
throw new RuntimeException(String.format("尚未支持该页面类型:%s", pageTypeEnum.getName()));
}
}
}

定义

单例模式确保一个类只有一个实例,并提供一个全部访问点。

要素

  • 私有构造方法
  • 私有静态引用指向自己实例
  • 以自己实例为返回值的公有静态方法

类图

Singleton UML

实现方式

饿汉式

1
2
3
4
5
6
7
8
9
10
public class Singleton {
private static Singleton instance = new Singleton();

private Singleton() {
}

public static Singleton getSingleton() {
return instance;
}
}

饿汉式 & 枚举

1
2
3
public enum Singleton {
INSTANCE;
}

懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton {
/**
* volatile:保证线程间变量可见性
*/
private volatile static Singleton instance;

private Singleton() {
// 可能存在成员初始化操作,这时需要使用volatile关键字
}

public static Singleton getInstance() {
if (instance == null) {
// 只在第一次初始化时才同步
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

懒汉式 & 内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {

private Singleton() {
}

public static Singleton getInstance() {
return SingletonInner.instance;
}

private static class SingletonInner {
private static Singleton instance = new Singleton();
}
}

饿汉式 & 枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Singleton {
private static Singleton instance;

private Singleton() {
}

public static Singleton getInstance() {
return SingletonEnum.INSTANCE.getInstance();
}

private enum SingletonEnum {
/**
* 唯一值
*/
INSTANCE;

private Singleton singleton;

SingletonEnum() {
singleton = new Singleton();
}

public Singleton getInstance() {
return singleton;
}
}
}

小结

  • 实例较大,且不需要立即加载时,使用懒汉模式
  • 饿汉模式,优先使用枚举
  • 懒汉模式,优先使用静态类
  • 使用枚举,能避免使用反射或序列化/反序列化来创建多个实例
  • 使用了多个类加载器时,有可能会创建多个实例
  • 静态类相较于单例模式:仅在类自给自足,且不依赖于复杂的初始化,才可以考虑使用,不然可能会产生一些微妙的和初始化顺序有关的bug
  • 全局变量相较于单例模式:缺点有:1.不能延迟初始化;2.不能确保只有一个实例;3.污染命名空间

应用场景

  • 数据库连接池
  • 线程池
  • web应用配置对象的读取
  • 注册表
  • 网站计数器

模式应用

Unsafe

java不能直接访问操作系统底层,而是通过本地方法来访问,Unsafe类提供了硬件级别的原子操作,它就是个单例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final class Unsafe {
private static final Unsafe theUnsafe;

...

private Unsafe() {
}

...

@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}

...

static {
...
theUnsafe = new Unsafe();
...
}
}

Spring IoC

Spring容器注入的Bean实例默认都是单例的。
Bean的注入(包括lazy-init方式)都是发生在AbstractBeanFactorygetBean里,getBean调用doGetBeandoGetBean调用DefaultSingletonBeanRegistry里的getSingleton
lazy-init方式(lazy-init=”true”),在用户向容器第一次索要bean时进行调用;非lazy-init方式(lazy-init=”false”),在容器初始化时候进行调用。

同步线程安全的单例核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从缓存(ConcurrentHashMap)中获取bean实例
Object singletonObject = this.singletonObjects.get(beanName);
// 如果bean实例为null,且正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // double-check:1
synchronized (this.singletonObjects) {
// 从缓存(ConcurrentHashMap)中获取early bean实例
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果bean实例为null,且允许创建early reference
if (singletonObject == null && allowEarlyReference) { // double-check:2
// 这里并非bean实例,而是创建bean实例的工厂对象
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 返回真正的bean
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

Spring中的Bean的单例虽然是一种单例效果,但实现方式是通过容器缓存实现,严格来说是一种享元模式

实际应用

需求:根据单据类型,找到对应的service,执行审批相关扩展操作,如审批前、审批后、弃审前、弃审后。
实现:提供注册单例,将单据类型及对应的service注册到单例中。
代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public enum ApproveServiceRegistry {
INSTANCE;
private Map<String, ApproveServiceInfo<? extends BaseBillEntity>> registry = new ConcurrentHashMap<>();

public <T extends BaseBillEntity> void register(String billType, ApproveService<T> service) {
registry.put(billType, new ApproveServiceInfo<>(service, service.getEntityClass(), service.getBillType()));
}

public void remove(String billType) {
registry.remove(billType);
}

public boolean contains(String billType) {
return registry.containsKey(billType);
}

public ApproveServiceInfo<? extends BaseBillEntity> getService(String billType) {
return registry.get(billType);
}
}