将一个类的接口适配成另一个类的接口所期望的形式,适配器模式实际上是一种补救模式,为了适应两种不同的标准而产生的一种兼容方案。
在现实生活中,电源适配器就是一个非常容易帮助我们理解适配器模式的案例,比如有些国家标准电压是110V,我们国家则是220V,两个标准都不能修改,又不得不使用,于是就有了电源适配器,可以接入110V,输出220V,或者接入220V,输出110V。
类似的场景还有很多,比如翻译、转接头等等。
/**
* 目标接口,即客户端本身所期望请求的接口
*/
public interface Target {
void request();
}
/**
* 适配者,需要适配的接口
*/
public class Adaptee {
public void specificRequest(){
System.out.println("特殊的请求");
}
}
/**
* 适配器,转换的接口
*/
public class Adapter implements Target {
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
System.out.println("正常请求");
adaptee.specificRequest();
}
}
public class Test {
public static void main(String[] args) {
// 对客户端来说,只知道一个request接口
Target target = new Adapter();
target.request();
}
}
系统需要使用现有的类,而此类的接口不符合系统的需要
想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
可以让任何两个没有关联的类一起运行。
提高了类的复用
增加了类的透明度
灵活性好。
适配器,spring-webmvc
定义的标准处理请求的方法为handle
,这个方法中有一个handler参数,实际上就是适配者
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
调用handle
方法,实际上调用的是Servlet
协议中定义的service
方法,也就是我们适配的接口
public class SimpleServletHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Servlet);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((Servlet) handler).service(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
}
HandlerAdapter
接口的其他实现类,在handle
方法中逻辑更为复杂,但无论怎样最终都会调用到目标接口:service
MyBatis
中有一个Log
接口,其定义了自己的日志级别,但同时为了能够适应其他日志框架的日志级别,于是又针对每一种日志框架做了不同的实现。
MyBatis中自己的日志级别标准
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String s, Throwable e);
void error(String s);
void debug(String s);
void trace(String s);
void warn(String s);
}
适应别的日志框架
public class Jdk14LoggingImpl implements Log {
private final Logger log;
public Jdk14LoggingImpl(String clazz) {
log = Logger.getLogger(clazz);
}
@Override
public boolean isDebugEnabled() {
return log.isLoggable(Level.FINE);
}
@Override
public boolean isTraceEnabled() {
return log.isLoggable(Level.FINER);
}
@Override
public void error(String s, Throwable e) {
log.log(Level.SEVERE, s, e);
}
@Override
public void error(String s) {
log.log(Level.SEVERE, s);
}
@Override
public void debug(String s) {
log.log(Level.FINE, s);
}
@Override
public void trace(String s) {
log.log(Level.FINER, s);
}
@Override
public void warn(String s) {
log.log(Level.WARNING, s);
}
}
Jdk中的WindowAdapter、MouseAdapter、KeyAdapter
等等awt.event
包下有很多类似这样的适配类,这是一种非典型的使用方法,他们分别实现了各自的XXXListener
接口,但却没有对其中的逻辑进行重写,都是空现实,把具体实现逻辑交给了具体的调用者,这样做的目的主要是因为这些XXXListener
接口中的方法较多,但调用者又不会每一个接口方法都会用到,所以就通过适配类来全部实现,而调用者只需要继承适配类,重写自己需要的方法即可。
public abstract class WindowAdapter
implements WindowListener, WindowStateListener, WindowFocusListener
{
/**
* Invoked when a window has been opened.
*/
public void windowOpened(WindowEvent e) {}
/**
* Invoked when a window is in the process of being closed.
* The close operation can be overridden at this point.
*/
public void windowClosing(WindowEvent e) {}
/**
* Invoked when a window has been closed.
*/
public void windowClosed(WindowEvent e) {}
/**
* Invoked when a window is iconified.
*/
public void windowIconified(WindowEvent e) {}
/**
* Invoked when a window is de-iconified.
*/
public void windowDeiconified(WindowEvent e) {}
/**
* Invoked when a window is activated.
*/
public void windowActivated(WindowEvent e) {}
/**
* Invoked when a window is de-activated.
*/
public void windowDeactivated(WindowEvent e) {}
/**
* Invoked when a window state is changed.
* @since 1.4
*/
public void windowStateChanged(WindowEvent e) {}
/**
* Invoked when the Window is set to be the focused Window, which means
* that the Window, or one of its subcomponents, will receive keyboard
* events.
*
* @since 1.4
*/
public void windowGainedFocus(WindowEvent e) {}
/**
* Invoked when the Window is no longer the focused Window, which means
* that keyboard events will no longer be delivered to the Window or any of
* its subcomponents.
*
* @since 1.4
*/
public void windowLostFocus(WindowEvent e) {}
}
public static void main(String[] args) {
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
}
});
}
同样的方式,在Spring
事务中也有运用到,也是为了实现者不必每次都要实现TransactionSynchronization
接口的所有方法,不过从spring 5.3
版本开始,由于JDK
支持接口可以有默认的实现方法,因此接口实现者也就不需要实现全部接口了,自然TransactionSynchronizationAdapter
也就没有存在的意义了。
/**
* Simple {@link TransactionSynchronization} adapter containing empty
* method implementations, for easier overriding of single methods.
*
* Also implements the {@link Ordered} interface to enable the execution
* order of synchronizations to be controlled declaratively. The default
* {@link #getOrder() order} is {@link Ordered#LOWEST_PRECEDENCE}, indicating
* late execution; return a lower value for earlier execution.
*
* @author Juergen Hoeller
* @since 22.01.2004
* @deprecated as of 5.3, in favor of the default methods on the
* {@link TransactionSynchronization} interface
*/
@Deprecated
public abstract class TransactionSynchronizationAdapter implements TransactionSynchronization, Ordered {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void suspend() {
}
@Override
public void resume() {
}
@Override
public void flush() {
}
@Override
public void beforeCommit(boolean readOnly) {
}
@Override
public void beforeCompletion() {
}
@Override
public void afterCommit() {
}
@Override
public void afterCompletion(int status) {
}
}
责任链模式也是非常实用的一种设计模式,尤其是在许多框架开发中都有应用,因为它可以用来提供框架的扩展点,一种非常经典的应用场景类型就是过滤器、拦截器。
责任链模式是一种行为型的设计模式,主要是为了将请求的发送和接收解耦,这样就可以方便扩展多个对象来处理请求,每个对象之间的处理顺序可以自由调配,每个对象处理后的数据也可以互相传递。
public abstract class Handler {
private Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public Handler getSuccessor() {
return successor;
}
public abstract void handleRequest(String msg);
}
import org.apache.commons.lang3.StringUtils;
public class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(String msg) {
if (StringUtils.isNotEmpty(msg)) {
System.out.println("ConcreteHandlerA pass");
if (getSuccessor() != null) {
getSuccessor().handleRequest(msg);
}
} else {
System.out.println("msg 不能为空!");
}
}
}
public class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(String msg) {
if (msg.length() <= 16) {
System.out.println("ConcreteHandlerB pass");
if (getSuccessor() != null) {
getSuccessor().handleRequest(msg);
}
} else {
System.out.println("msg长度不能超过16!");
}
}
}
public class Main {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandlerA();
Handler handler2 = new ConcreteHandlerB();
handler1.setSuccessor(handler2);
handler1.handleRequest("text");
}
}
前面有介绍了,责任链模式主要就是为了避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
因此责任链模式在针对这种需要多个对象配合处理的情况下非常好用,比如:多级别的审批流程、多级别的数据安全防护处理,还有框架中常见的过滤器、拦截器等。
降低耦合度,它将请求的发送者和接收者解耦。
简化了对象,使得对象不需要知道链的结构。
增强给对象指派职责的灵活性,通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
增加新的请求处理类很方便。
Servlet
规范中有一个非常经典的设计就是Filter
,它主要用来实现对HTTP请求的过滤,比如:鉴权、限流、参数校验、记录日志等等。
Filter接口
Filter就相当于Handler
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;
public void destroy();
}
FilterChain
FilterChain相当于前面main
方法中的逻辑,通常会做一层封装,封装后就是HandlerChain
.
public interface FilterChain {
public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;
}
我们知道Servlet
只定义标准,具体实现由各个web容器来完成,比如ApplicationFilterChain
,就是tomcat中提供的实现类。
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 省略部分代码...
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
// 省略部分代码...
// 通过递归 + pos下标的方式,遍历处理所有的filter
filter.doFilter(request, response, this);
return;
}
// 省略部分代码...
servlet.service(request, response);
}
给filterChain添加filter
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMap, requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
Spring Interceptor和Servlet Filter差不多,是spring框架中的功能,当客户端的请求发送过来后,会先经过Servlet Filter再经过Spring Interceptor,最后才到达业务代码中。
HandlerInterceptor
HandlerInterceptor接口相当于Handler
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
HandlerExecutionChain
HandlerExecutionChain接口相当于HandlerChain
public class HandlerExecutionChain {
private final Object handler;
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
private int interceptorIndex = -1;
public void addInterceptor(HandlerInterceptor interceptor) {
this.interceptorList.add(interceptor);
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
public static void main(String[] args) {
CharSequence str = "hello world
";
String escape = EscapeUtil.escapeHtml4(str);
System.out.println(escape);
String unescape = EscapeUtil.unescapeHtml4(escape);
System.out.println(unescape);
}
输出结果
<html><p>hello world</p></html>
hello world
escapeHtml4
方法可以转义html中的特殊字符,其中就使用到了责任链模式来完成特殊字符的替换。
写明了就是要用责任链模式,设计了一个通用的接口。
Chain
/**
* 责任链接口
* @author Looly
*
* @param 元素类型
* @param 目标类类型,用于返回this对象
*/
public interface Chain<E, T> extends Iterable<E>{
/**
* 加入责任链
* @param element 责任链新的环节元素
* @return this
*/
T addChain(E element);
}
ReplacerChain
/**
* 字符串替换链,用于组合多个字符串替换逻辑
*
* @author looly
* @since 4.1.5
*/
public class ReplacerChain extends StrReplacer implements Chain<StrReplacer, ReplacerChain> {
private static final long serialVersionUID = 1L;
private final List<StrReplacer> replacers = new LinkedList<>();
/**
* 构造
*
* @param strReplacers 字符串替换器
*/
public ReplacerChain(StrReplacer... strReplacers) {
for (StrReplacer strReplacer : strReplacers) {
addChain(strReplacer);
}
}
@SuppressWarnings("NullableProblems")
@Override
public Iterator<StrReplacer> iterator() {
return replacers.iterator();
}
@Override
public ReplacerChain addChain(StrReplacer element) {
replacers.add(element);
return this;
}
@Override
protected int replace(CharSequence str, int pos, StrBuilder out) {
int consumed = 0;
for (StrReplacer strReplacer : replacers) {
consumed = strReplacer.replace(str, pos, out);
if (0 != consumed) {
return consumed;
}
}
return consumed;
}
}
添加chain的逻辑放在构造方法中
public static String escapeHtml4(CharSequence html) {
Html4Escape escape = new Html4Escape();
return escape.replace(html).toString();
}
public Html4Escape() {
addChain(new LookupReplacer(BASIC_ESCAPE));
addChain(new LookupReplacer(ISO8859_1_ESCAPE));
addChain(new LookupReplacer(HTML40_EXTENDED_ESCAPE));
}
责任链的写法有多种,可以使用一开始demo中的方式,省去HandlerChain
,直接在每个Handler
中设置后链的关系,也可以像Filter
、Interceptor
、Hutool
那样定义一个集合,把所有的Handler
都放入集合中,并让HandlerChain
去维护,调用的时候只需要遍历集合去处理即可。
策略模式是一种常用又特别容易理解的设计模式,它也是一种行为型模式,最常用于解决if...else...
这样的分支逻辑判断,每一个if
分支都对应一种不同的策略,或者叫算法,每一种算法独立存在,互不影响,互可替换,很明显策略模式起到的作用就是解耦、可扩展。
public interface Strategy {
void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithmInterface() {
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithmInterface() {
}
}
public class ConcreteStrategyC implements Strategy {
@Override
public void algorithmInterface() {
}
}
public class Context {
Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public void contextInterface(){
strategy.algorithmInterface();
}
}
public class Test {
public static void main(String[] args) {
Context context = new Context(new ConcreteStrategyA());
context.contextInterface();
}
}
策略模式不仅仅可以用来解决if...else...
问题,更重要的是可以解耦不同的业务处理逻辑,让代码更容易扩展,更容易维护,减少复杂度,满足开闭原则,所以一般而言只要某种业务存在多种算法或者行为,并且又需要方便切换,变更的场景,就可以使用策略模式。
源码中使用策略模式的也非常多,这里不一一列举,但是我们有必要来看看一些没有完全按照标准结构来实现的策略模式。
public enum Strategy {
/**
* Default: Retain the first value only.
*/
RETAIN_FIRST,
/**
* Retain the last value only.
*/
RETAIN_LAST,
/**
* Retain all unique values in the order of their first encounter.
*/
RETAIN_UNIQUE
}
void dedupe(HttpHeaders headers, Config config) {
String names = config.getName();
Strategy strategy = config.getStrategy();
if (headers == null || names == null || strategy == null) {
return;
}
for (String name : names.split(" ")) {
dedupe(headers, name.trim(), strategy);
}
}
private void dedupe(HttpHeaders headers, String name, Strategy strategy) {
List<String> values = headers.get(name);
if (values == null || values.size() <= 1) {
return;
}
switch (strategy) {
case RETAIN_FIRST:
headers.set(name, values.get(0));
break;
case RETAIN_LAST:
headers.set(name, values.get(values.size() - 1));
break;
case RETAIN_UNIQUE:
headers.put(name, values.stream().distinct().collect(Collectors.toList()));
break;
default:
break;
}
}
public static class Config extends AbstractGatewayFilterFactory.NameConfig {
private Strategy strategy = Strategy.RETAIN_FIRST;
public Strategy getStrategy() {
return strategy;
}
public Config setStrategy(Strategy strategy) {
this.strategy = strategy;
return this;
}
}
private interface Strategy {
Iterator<String> iterator(Splitter splitter, CharSequence toSplit);
}
public static Splitter on(final CharMatcher separatorMatcher) {
checkNotNull(separatorMatcher);
return new Splitter(
new Strategy() {
@Override
public SplittingIterator iterator(Splitter splitter, final CharSequence toSplit) {
return new SplittingIterator(splitter, toSplit) {
@Override
int separatorStart(int start) {
return separatorMatcher.indexIn(toSplit, start);
}
@Override
int separatorEnd(int separatorPosition) {
return separatorPosition + 1;
}
};
}
});
}
private static Splitter on(final CommonPattern separatorPattern) {
checkArgument(
!separatorPattern.matcher("").matches(),
"The pattern may not match the empty string: %s",
separatorPattern);
return new Splitter(
new Strategy() {
@Override
public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
final CommonMatcher matcher = separatorPattern.matcher(toSplit);
return new SplittingIterator(splitter, toSplit) {
@Override
public int separatorStart(int start) {
return matcher.find(start) ? matcher.start() : -1;
}
@Override
public int separatorEnd(int separatorPosition) {
return matcher.end();
}
};
}
});
}
public static Splitter fixedLength(final int length) {
checkArgument(length > 0, "The length may not be less than 1");
return new Splitter(
new Strategy() {
@Override
public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
return new SplittingIterator(splitter, toSplit) {
@Override
public int separatorStart(int start) {
int nextChunkStart = start + length;
return (nextChunkStart < toSplit.length() ? nextChunkStart : -1);
}
@Override
public int separatorEnd(int separatorPosition) {
return separatorPosition;
}
};
}
});
}
常见的组合方式
public interface AopProxy {
Object getProxy();
Object getProxy(@Nullable ClassLoader classLoader);
}
不同的策略实现
class CglibAopProxy implements AopProxy, Serializable {
}
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
}
class ObjenesisCglibAopProxy extends CglibAopProxy {
}
策略工厂定义
public interface AopProxyFactory {
AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}
具体策略工厂实现
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
// 根据不同参数,选择不同的策略(动态代理方式)
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!IN_NATIVE_IMAGE &&
(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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
public class Context {
public static Strategy getStrategy(String Type) {
switch (Type) {
case "A":
return new ConcreteStrategyA();
case "B":
return new ConcreteStrategyB();
case "C":
return new ConcreteStrategyC();
default:
throw new IllegalArgumentException("Type error!");
}
}
}
public class Main {
public static void main(String[] args) {
Strategy strategyA = Context.getStrategy("A");
}
}
public interface Strategy {
// 增加一个策略枚举类
StrategyEnum getStrategyType();
void algorithmInterface();
}
public enum StrategyEnum {
StrategyA,
StrategyB,
StrategyC
}
@Component
public class ConcreteStrategyA implements Strategy {
@Override
public StrategyEnum getStrategyType() {
return StrategyEnum.StrategyA;
}
@Override
public void algorithmInterface() {
}
}
@Component
public class ConcreteStrategyB implements Strategy {
@Override
public StrategyEnum getStrategyType() {
return StrategyEnum.StrategyB;
}
@Override
public void algorithmInterface() {
}
}
@Component
public class ConcreteStrategyC implements Strategy {
@Override
public StrategyEnum getStrategyType() {
return StrategyEnum.StrategyC;
}
@Override
public void algorithmInterface() {
}
}
@Component
public class StrategyContext implements ApplicationContextAware {
private static final Map<StrategyEnum, Strategy> registerMap = new ConcurrentHashMap<>();
public void algorithmInterface(StrategyEnum strategyEnum) {
registerMap.get(strategyEnum).algorithmInterface();
}
// 自动注入到map中
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Strategy> strategyMap = applicationContext.getBeansOfType(Strategy.class);
strategyMap.values().forEach(strategyService -> registerMap.put(strategyService.getStrategyType(), strategyService));
}
}
使用方式
@Resource
private StrategyContext strategyContext;
@Test
public void contextRegister(){
strategyContext.algorithmInterface(StrategyEnum.StrategyA);
strategyContext.algorithmInterface(StrategyEnum.StrategyB);
strategyContext.algorithmInterface(StrategyEnum.StrategyC);
}
建造者模式属于创建型模式,它可以用来优雅的构建一个复杂的对象,其定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
public class Product {
private String id;
private String name;
}
public interface Builder {
void buildId();
void buildName();
Product build();
}
public class ConcreteBuilder implements Builder {
private Product product = new Product();
@Override
public void buildId() {
product.setId("1");
}
@Override
public void buildName() {
product.setName("abc");
}
@Override
public Product build() {
return product;
}
}
public class Director {
public Product getProduct(Builder builder){
builder.buildId();
builder.buildName();
return builder.build();
}
}
public class Test {
public static void main(String[] args) {
Director director = new Director();
Product product = director.getProduct(new ConcreteBuilder());
System.out.println(product);
}
}
当一个对象构建的属性特别多,又或者是构建起来步骤特别的多,内部结构复杂,但整体的组成逻辑通常是稳定的,其对象的组成部件基本不会改变,但组合方式或者部件的内部逻辑经常会发生改变,当遇到这类场景时,就可以考虑使用构造者模式了。
现在建造者模式也常常用来简化普通对象的构建,比如lombok提供的@Builder
注解。
平时我们构建对象时一般都是像如下这样,在属性比较少,且不复杂时完全没有问题,但如果属性很多,就不太合适了。
// 构造方法
Product p = new Product("2","def");
// set方法
Product p = new Product();
p.setId("2");
p.setName("def");
lombok提供的@Builder
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private String id;
private String name;
}
直接这样使用即可,看起来非常的简洁
Product p = Product.builder()
.id("2")
.name("def")
.build();
编辑后的class文件,可以看到具体的实现方式
public class Product {
private String id;
private String name;
public static Product.ProductBuilder builder() {
return new Product.ProductBuilder();
}
public String getId() {
return this.id;
}
public String getName() {
return this.name;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Product)) {
return false;
} else {
Product other = (Product)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$id = this.getId();
Object other$id = other.getId();
if (this$id == null) {
if (other$id != null) {
return false;
}
} else if (!this$id.equals(other$id)) {
return false;
}
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Product;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
return result;
}
public String toString() {
return "Product(id=" + this.getId() + ", name=" + this.getName() + ")";
}
public Product() {
}
public Product(String id, String name) {
this.id = id;
this.name = name;
}
public static class ProductBuilder {
private String id;
private String name;
ProductBuilder() {
}
public Product.ProductBuilder id(String id) {
this.id = id;
return this;
}
public Product.ProductBuilder name(String name) {
this.name = name;
return this;
}
public Product build() {
return new Product(this.id, this.name);
}
public String toString() {
return "Product.ProductBuilder(id=" + this.id + ", name=" + this.name + ")";
}
}
}
源码中大多数的建造者模式都没有按照标准的结构来,所以我们学习设计模式一定要学会变通,万万不可生搬硬套。
JDK中Calendar类,就可以通过构造者方法的方式构建
其有一个内部对象Builder,提供了一系列的set
方法,以及最后构建的build
方法。
public Calendar build() {
if (locale == null) {
locale = Locale.getDefault();
}
if (zone == null) {
zone = TimeZone.getDefault();
}
Calendar cal;
if (type == null) {
type = locale.getUnicodeLocaleType("ca");
}
if (type == null) {
if (locale.getCountry() == "TH"
&& locale.getLanguage() == "th") {
type = "buddhist";
} else {
type = "gregory";
}
}
switch (type) {
case "gregory":
cal = new GregorianCalendar(zone, locale, true);
break;
case "iso8601":
GregorianCalendar gcal = new GregorianCalendar(zone, locale, true);
// make gcal a proleptic Gregorian
gcal.setGregorianChange(new Date(Long.MIN_VALUE));
// and week definition to be compatible with ISO 8601
setWeekDefinition(MONDAY, 4);
cal = gcal;
break;
case "buddhist":
cal = new BuddhistCalendar(zone, locale);
cal.clear();
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, locale, true);
break;
default:
throw new IllegalArgumentException("unknown calendar type: " + type);
}
cal.setLenient(lenient);
if (firstDayOfWeek != 0) {
cal.setFirstDayOfWeek(firstDayOfWeek);
cal.setMinimalDaysInFirstWeek(minimalDaysInFirstWeek);
}
if (isInstantSet()) {
cal.setTimeInMillis(instant);
cal.complete();
return cal;
}
if (fields != null) {
boolean weekDate = isSet(WEEK_YEAR)
&& fields[WEEK_YEAR] > fields[YEAR];
if (weekDate && !cal.isWeekDateSupported()) {
throw new IllegalArgumentException("week date is unsupported by " + type);
}
// Set the fields from the min stamp to the max stamp so that
// the fields resolution works in the Calendar.
for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
for (int index = 0; index <= maxFieldIndex; index++) {
if (fields[index] == stamp) {
cal.set(index, fields[NFIELDS + index]);
break;
}
}
}
if (weekDate) {
int weekOfYear = isSet(WEEK_OF_YEAR) ? fields[NFIELDS + WEEK_OF_YEAR] : 1;
int dayOfWeek = isSet(DAY_OF_WEEK)
? fields[NFIELDS + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
cal.setWeekDate(fields[NFIELDS + WEEK_YEAR], weekOfYear, dayOfWeek);
}
cal.complete();
}
return cal;
}
MyBatis中用到Build的方式还是挺多的,CacheBuilder、 MappedStatement、SqlSessionFactoryBuilder
等。
Cache
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache;
return cache;
}
MappedStatement
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
return statement;
}
无独有偶,Guava中的缓存对象,也使用了Builder的方式
SpringMVC中构建ServerHttpRequest
对象的方式,到还挺符合建造者模式的标准结构的
ServerHttpRequest
接口内部还有一个Builder接口
注意这个默认方法
default ServerHttpRequest.Builder mutate() {
return new DefaultServerHttpRequestBuilder(this);
}
interface Builder {
Builder uri(URI uri);
Builder path(String path);
Builder contextPath(String contextPath);
Builder header(String headerName, String... headerValues);
Builder headers(Consumer<HttpHeaders> headersConsumer);
Builder sslInfo(SslInfo sslInfo);
Builder remoteAddress(InetSocketAddress remoteAddress);
ServerHttpRequest build();
}
然后有具体的实现类
class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
private URI uri;
private HttpHeaders headers;
private String httpMethodValue;
@Nullable
private String uriPath;
@Nullable
private String contextPath;
@Nullable
private SslInfo sslInfo;
@Nullable
private InetSocketAddress remoteAddress;
private Flux<DataBuffer> body;
private final ServerHttpRequest originalRequest;
public DefaultServerHttpRequestBuilder(ServerHttpRequest original) {
Assert.notNull(original, "ServerHttpRequest is required");
this.uri = original.getURI();
this.headers = HttpHeaders.writableHttpHeaders(original.getHeaders());
this.httpMethodValue = original.getMethodValue();
this.contextPath = original.getPath().contextPath().value();
this.remoteAddress = original.getRemoteAddress();
this.body = original.getBody();
this.originalRequest = original;
}
@Override
public ServerHttpRequest.Builder method(HttpMethod httpMethod) {
this.httpMethodValue = httpMethod.name();
return this;
}
@Override
public ServerHttpRequest.Builder uri(URI uri) {
this.uri = uri;
return this;
}
@Override
public ServerHttpRequest.Builder path(String path) {
Assert.isTrue(path.startsWith("/"), "The path does not have a leading slash.");
this.uriPath = path;
return this;
}
@Override
public ServerHttpRequest.Builder contextPath(String contextPath) {
this.contextPath = contextPath;
return this;
}
@Override
public ServerHttpRequest.Builder header(String headerName, String... headerValues) {
this.headers.put(headerName, Arrays.asList(headerValues));
return this;
}
@Override
public ServerHttpRequest.Builder headers(Consumer<HttpHeaders> headersConsumer) {
Assert.notNull(headersConsumer, "'headersConsumer' must not be null");
headersConsumer.accept(this.headers);
return this;
}
@Override
public ServerHttpRequest.Builder sslInfo(SslInfo sslInfo) {
this.sslInfo = sslInfo;
return this;
}
@Override
public ServerHttpRequest.Builder remoteAddress(InetSocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
return this;
}
@Override
public ServerHttpRequest build() {
return new MutatedServerHttpRequest(getUriToUse(), this.contextPath,
this.httpMethodValue, this.sslInfo, this.remoteAddress, this.body, this.originalRequest);
}
// ...省略部分代码
}
具体用法,先调用mutate
方法,获得默认的构造者实现类,即:DefaultServerHttpRequestBuilder
,然后再根据需要选择是否重写方法。
比如AddRequestHeaderGatewayFilterFactory
重写了header
方法
ServerHttpRequest request = exchange.getRequest().mutate().header(config.getName(), value).build();
ForwardPathFilter
重写了path
方法
exchange = exchange.mutate().request(exchange.getRequest().mutate().path(routeUri.getPath()).build()).build();
工厂模式应该是我们最常遇见的设计模式了,我们见过太多XXXFactory
这样的类名了,同样,它也是创建型模式,工厂模式又分为简单工厂模式、工厂模式、抽象工厂模式,其中前两种是非常常见、实用的方式,相比抽象工厂模式比较复杂,实际中使用的也非常少,所以不过多介绍。
简单工厂模式
工厂模式
public interface OrderFactory {
Order createOrder();
}
public class NormalOrderFactory implements OrderFactory {
@Override
public Order createOrder() {
return new NormalOrder();
}
}
public class VIPOrderFactory implements OrderFactory {
@Override
public Order createOrder() {
return new VIPOrder();
}
}
public interface Order {
void create();
}
public class NormalOrder implements Order {
@Override
public void create() {
System.out.println("普通的订单");
}
}
public class VIPOrder implements Order {
@Override
public void create() {
System.out.println("VIP的订单");
}
}
简单工厂模式
public class SimpleOrderFactory {
private static final Map<String, Order> orderMap = new HashMap<>();
static {
orderMap.put("normal", new NormalOrder());
orderMap.put("vip", new VIPOrder());
}
public static Order createOrder(String orderType) {
return orderMap.get(orderType);
}
}
public class Test {
public static void main(String[] args) {
// 使用工厂模式
OrderFactory normalOrderFactory = new NormalOrderFactory();
Order normalOrder = normalOrderFactory.createOrder();
normalOrder.create();
OrderFactory vipOrderFactory = new VIPOrderFactory();
Order vipOrder = vipOrderFactory.createOrder();
vipOrder.create();
// 使用简单工厂模式
Order vip = SimpleOrderFactory.createOrder("vip");
vip.create();
}
}
工厂模式使用起来非常简单,只要稍微复杂一点的对象,都可以使用工厂模式来创建,以此来简化客户端的逻辑。
扩展性好、避免大量的if...else...
代码,将复杂的逻辑封装起来,客户端直接调用即可,对客户端友好。
每增加一个新的产品时,就需要实现一套具体的类和工厂,这样会使得相关业务中的类数量非常多,一定程度上也增加了系统的复杂度。
工厂模式的应用真的非常多,最典型的应用应属Spring IOC容器,其中BeanFactory是其核心的底层接口。
此外,工厂模式还有很多变种,有一些使用了工厂模式的思想,但并没有完全按照标准的结构来实现,这也是能运用好设计模式的关键。
比如JDK中的Calendar类中的getInstance
方法,根据传入不同的参数,创建出不同的Calendar对象
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
public static Calendar getInstance(TimeZone zone)
{
return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
public static Calendar getInstance(Locale aLocale)
{
return createCalendar(TimeZone.getDefault(), aLocale);
}
public static Calendar getInstance(TimeZone zone,
Locale aLocale)
{
return createCalendar(zone, aLocale);
}
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
模板方法模式一般用于有标准流程性的业务场景,总体流程不变,但流程中的每一个环节可能有所不同,用标准定义来描述就是,模板方法模式定义了一个算法的步骤,并允许子类为一个或多个步骤提供具体的实现,让子类可以在不改变算法架构的情况下,重新定义算法中的某些步骤。
public abstract class AbstractClass {
public final void templateMethod(){
method1();
method2();
method3();
}
protected abstract void method3();
protected final void method2(){
System.out.println("AbstractClass method2");
}
protected abstract void method1();
}
public class ConcreteClassA extends AbstractClass {
@Override
protected void method3() {
System.out.println("ConcreteClassA method3");
}
@Override
protected void method1() {
System.out.println("ConcreteClassA method1");
}
}
public class ConcreteClassB extends AbstractClass {
@Override
protected void method3() {
System.out.println("ConcreteClassB method3");
}
@Override
protected void method1() {
System.out.println("ConcreteClassB method1");
}
}
当业务逻存在一些需要多个步骤来完成,某些步骤是标准统一的,某些步骤根据不同的场景又有不同的处理逻辑,如果全部写在一个类中,可能那些不同场景步骤会出现各种条件判断,无论哪个场景发生修改,都会影响整个流程,这不符合开闭原则,但如果为每个场景都分别实现一套,同样对应相同的步骤就会出现重复的逻辑,又会带来大量的重复代码。
那么,模板方法就是解决上述问题的一种非常好的模式,利用模板方法模式可以实现代码的复用和扩展,这实际上在对代码重构时就非常有帮助。
对于不变的部分,与变的部分做到了隔离,符合开闭原则,使用业务非常容易扩展。
对于公用逻辑易于维护。
由于每一个不同的实现都要一个子类来实现,会导致类的个数变得更多。
AbstractApplicationContext类的refresh方法,这里就用到了模板方法模式,有一些子方法是由AbstractApplicationContext自己实现的,有一些子方法则是让不同的实现类各自实现。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
还有BeanPostProcessor类中定义的两个方法postProcessBeforeInitialization和postProcessAfterInitialization,我们一般会称之为前置处理器和后置处理器,光从名字就能感觉到有模板方法含义,实际上也是spring在某处留好了这两个方法的调用,所以应该说是模板方法中的钩子方法。
AbstractController提供了handleRequest
方法,负责请求处理并返回ModelAndView,其也是通过模板方法模式来实现,这点作者甚至直接写在了文档说明中。
public abstract class AbstractController extends WebContentGenerator implements Controller {
@Override
@Nullable
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return null;
}
// Delegate to WebContentGenerator for checking and preparing.
checkRequest(request);
prepareResponse(response);
// Execute handleRequestInternal in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
/**
* Template method. Subclasses must implement this.
* The contract is the same as for {@code handleRequest}.
* @see #handleRequest
*/
@Nullable
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
public abstract class InputStream implements Closeable {
// 留给子类实现
public abstract int read() throws IOException;
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}
其他IO、NIO类库中也有很多类似的设计。
这也是比较经典的应用,其定义的doPost/doGet
等方法是可以让子类进行重写的
public abstract class HttpServlet extends GenericServlet
implements java.io.Serializable
{
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
}
BaseExecutor是MyBatis中经典的模板方法模式应用,其主要是用来执行SQL。
query
方法可以看作是定义的主流程,doQuery
方法是其留给子类实现的。
public abstract class BaseExecutor implements Executor {
// 几个do开头的方法都是留给子类实现的
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 具体query方式,交由子类实现
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}