八
tomcat
设计模式
8.1
责任链模式
意图
:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
主要解决: 职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
何时使用: 在处理消息的时候以过滤很多道。
如何解决: 拦截的类都实现统一接口。
关键代码: Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
应用实例:
- 红楼梦中的”击鼓传花”。
- JS 中的事件冒泡。
- JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。
优点:
- 降低耦合度。它将请求的发送者和接收者解耦。
- 简化了对象。使得对象不需要知道链的结构。
- 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
- 增加新的请求处理类很方便。
缺点:
- 不能保证请求一定被接收。
- 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
- 可能不容易观察运行时的特征,有碍于除错。
使用场景:
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。
tomcat中责任链模式的实践主要是我们的阀门.可以看到容器是由管道组成的,管道中又组合了阀门.实际上责任链模式呢主要体现在了阀门这里的getNext(),setNext(),Invoke()方法了,由于整个阀门只是管道和容器中的一部分,所以在触发下一个阀门的时候有的时候是先调用了管道.下面我们正对源码来进行分析一下.
public interface Valve {
public Valve getNext();
public void setNext(Valve valve);
public void invoke(Request request, Response response) throws IOException, ServletException;
}
}
final class StandardContextValve extends ValveBase {
private static final StringManager sm = StringManager.getManager(StandardContextValve.class);
public StandardContextValve() {
super(true);
}
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
/**省略部分代码*/
wrapper.getPipeline().getFirst().invoke(request, response);
}
可以看到每一层容器的传输都是通过,传到下一层容器的第一个基础阀来的.然后后面基础阀再依次迭代,可以看到除了以容器名命名的基础阀是从容器中获取管道,然后再从管道中获取到基础阀,然后就依次迭代阀门了.这就是tomcat对责任链设计模式的使
public class PersistentValve extends ValveBase {
private static final ClassLoader MY_CLASSLOADER = PersistentValve.class.getClassLoader();
private volatile boolean clBindRequired;
protected Pattern filter = null;
public PersistentValve() {
super(true);
}
@Override
public void invoke(Request request, Response response)
throws IOException, ServletException {
// Update the session last access time for our session (if any)
String sessionId = request.getRequestedSessionId();
Manager manager = context.getManager();
if (sessionId != null && manager instanceof StoreManager) {
Store store = ((StoreManager) manager).getStore();
if (store != null) {
Session session = null;
try {
session = store.load(sessionId);
} catch (Exception e) {
container.getLogger().error("deserializeError");
}
if (session != null) {
if (!session.isValid() ||
isSessionStale(session, System.currentTimeMillis())) {
if (container.getLogger().isDebugEnabled()) {
container.getLogger().debug("session swapped in is invalid or expired");
}
session.expire();
store.remove(sessionId);
} else {
session.setManager(manager);
// session.setId(sessionId); Only if new ???
manager.add(session);
// ((StandardSession)session).activate();
session.access();
session.endAccess();
}
}
}
}
if (container.getLogger().isDebugEnabled()) {
container.getLogger().debug("sessionId: " + sessionId);
}
getNext().invoke(request, response);
if (!request.isAsync()) {
// Read the sessionid after the response.
// HttpSession hsess = hreq.getSession(false);
Session hsess;
try {
hsess = request.getSessionInternal(false);
} catch (Exception ex) {
hsess = null;
}
String newsessionId = null;
if (hsess!=null) {
newsessionId = hsess.getIdInternal();
}
if (container.getLogger().isDebugEnabled()) {
container.getLogger().debug("newsessionId: " + newsessionId);
}
if (newsessionId!=null) {
try {
bind(context);
if (manager instanceof StoreManager) {
Session session = manager.findSession(newsessionId);
Store store = ((StoreManager) manager).getStore();
boolean stored = false;
if (session != null) {
synchronized (session) {
if (store != null && session.isValid() &&
!isSessionStale(session, System.currentTimeMillis())) {
store.save(session);
((StoreManager) manager).removeSuper(session);
session.recycle();
stored = true;
}
}
}
} finally {
unbind(context);
}
}
}
}
用.成功把管道,阀门有机的整合在了一起.如下图所示
回旋链设计模式
tomcat中filter链的结构。ApplicationFilterChain 封装了所有的Filter链,依次执行。执行完以后再调用封装的servlet进行执行。执行方式1–2–3–3–2–1
代码工作原理 ApplicationFilterChain类
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
//此处省略部分代码
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();
//此处省略部分代码
filter.doFilter(request, response, this);
//此处省略部分代码
}
//此处省略部分代码
servlet.service(request, response);
}
参考代码
核心方法
public class FilterChain implements Filter {
private List<Filter> chains = new ArrayList<>();
public void addFilter(Filter filter) {
chains.add(filter);
}
private int index = 0;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
if (index >= chains.size()) {
return;
}
Filter filter = chains.get(index);
index++;
filter.doFilter(request, response, chain);
}
}
main函数
public class FilterTest{
public static void main(String[] args) {
FilterChain chain=new FilterChain();
Filter filter1=new CFilter1();
Filter filter2=new Cfilter2();
Filter filter3=new Cfilter3();
chain.addFilter(filter1);
chain.addFilter(filter2);
chain.addFilter(filter3);
chain.doFilter(new ServletRequest(),new ServletResponse(),chain);
}
}
运行结果
8.2
适配器模式
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用:
- 系统需要使用现有的类,而此类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
- 通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
应用实例:
1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。
2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。
3、在 LINUX 上运行 WINDOWS 程序。
4、JAVA 中的 jdbc。
优点:
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
- 灵活性好。
缺点:
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
- 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
如上图所示是tomcat对适配器模式的实践,接下来我们看看源码相关的写法
Public class Connector extends LifecycleMbeanBase{
protected void initInternal() throws LifecycleException {
super.initInternal();
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() && !AprLifecycleListener.isInstanceCreated()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
getProtocolHandlerClassName()));
}
if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
getProtocolHandlerClassName()));
}
if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
protocolHandler instanceof AbstractHttp11JsseProtocol) {
AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
(AbstractHttp11JsseProtocol<?>) protocolHandler;
if (jsseProtocolHandler.isSSLEnabled() &&
jsseProtocolHandler.getSslImplementationName() == null) {
// OpenSSL is compatible with the JSSE configuration, so use it if APR is available
jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
}
}
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
}
如上所示是容器的代码,在容器中我们组合了
protocolhandler
这个类,通过在这个类中组合
A
d
apter
这个适配器类,就可以通过获取适配器来处理方法和函数了.
public interface ProtocolHandler {
public Adapter getAdapter();
public void setAdapter(Adapter adapter);
public Executor getExecutor();
public void init() throws Exception;
public void start() throws Exception;
public void pause() throws Exception;
public void resume() throws Exception;
public void stop() throws Exception;
public void destroy() throws Exception;
public void closeServerSocketGraceful();
public boolean isAprRequired();
public boolean isSendfileSupported();
public void addSslHostConfig(SSLHostConfig sslHostConfig);
public SSLHostConfig[] findSslHostConfigs();
public void addUpgradeProtocol(UpgradeProtocol upgradeProtocol);
public UpgradeProtocol[] findUpgradeProtocols();
}
如上代码所示
H
a
ndler
中有
adaper
的设置和获取接口
.
在
AbstractProtocol
可以看到组合了
A
dap
ter
接口
public abstract class AbstractProtocol<S> implements ProtocolHandler,
MBeanRegistration {
private static final StringManager sm = StringManager.getManager(AbstractProtocol.class);
private static final AtomicInteger nameCounter = new AtomicInteger(0);
private int nameIndex = 0;
private final AbstractEndpoint<S> endpoint;
private Handler<S> handler;
private final Set<Processor> waitingProcessors =
Collections.newSetFromMap(new ConcurrentHashMap<Processor, Boolean>());
private AsyncTimeout asyncTimeout = null;
public AbstractProtocol(AbstractEndpoint<S> endpoint) {
this.endpoint = endpoint;
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
public boolean setProperty(String name, String value) {
return endpoint.setProperty(name, value);
}
public String getProperty(String name) {
return endpoint.getProperty(name);
}
protected ObjectName rgOname = null;
public ObjectName getGlobalRequestProcessorMBeanName() {
return rgOname;
}
protected Adapter adapter;
@Override
public void setAdapter(Adapter adapter) { this.adapter = adapter; }
@Override
public Adapter getAdapter() { return adapter; }
protected String clientCertProvider = null;
public String getClientCertProvider() { return clientCertProvider; }
public void setClientCertProvider(String s) { this.clientCertProvider = s; }
@Override
public boolean isSendfileSupported() {
return endpoint.getUseSendfile();
}
@Deprecated
public void setSendReasonPhrase(boolean sendReasonPhrase) {
this.sendReasonPhrase = sendReasonPhrase;
}
@Override
public Executor getExecutor() { return endpoint.getExecutor(); }
public void setExecutor(Executor executor) {
endpoint.setExecutor(executor);
}
public int getMaxThreads() { return endpoint.getMaxThreads(); }
public void setMaxThreads(int maxThreads) {
endpoint.setMaxThreads(maxThreads);
}
public int getMaxConnections() { return endpoint.getMaxConnections(); }
public void setMaxConnections(int maxConnections) {
endpoint.setMaxConnections(maxConnections);
}
public int getMinSpareThreads() { return endpoint.getMinSpareThreads(); }
public void setMinSpareThreads(int minSpareThreads) {
endpoint.setMinSpareThreads(minSpareThreads);
}
public int getThreadPriority() { return endpoint.getThreadPriority(); }
public void setThreadPriority(int threadPriority) {
endpoint.setThreadPriority(threadPriority);
}
public int getAcceptCount() { return endpoint.getAcceptCount(); }
public void setAcceptCount(int acceptCount) { endpoint.setAcceptCount(acceptCount); }
@Deprecated
public int getBacklog() { return endpoint.getBacklog(); }
@Deprecated
public void setBacklog(int backlog) { endpoint.setBacklog(backlog); }
public boolean getTcpNoDelay() { return endpoint.getTcpNoDelay(); }
public void setTcpNoDelay(boolean tcpNoDelay) {
endpoint.setTcpNoDelay(tcpNoDelay);
}
public int getConnectionLinger() { return endpoint.getConnectionLinger(); }
public void setConnectionLinger(int connectionLinger) {
endpoint.setConnectionLinger(connectionLinger);
}
@Deprecated
public int getSoLinger() { return endpoint.getSoLinger(); }
@Deprecated
public void setSoLinger(int soLinger) { endpoint.setSoLinger(soLinger); }
public int getKeepAliveTimeout() { return endpoint.getKeepAliveTimeout(); }
public void setKeepAliveTimeout(int keepAliveTimeout) {
endpoint.setKeepAliveTimeout(keepAliveTimeout);
}
public InetAddress getAddress() { return endpoint.getAddress(); }
public void setAddress(InetAddress ia) {
endpoint.setAddress(ia);
}
public int getPort() { return endpoint.getPort(); }
public void setPort(int port) {
endpoint.setPort(port);
}
public int getLocalPort() { return endpoint.getLocalPort(); }
public int getConnectionTimeout() {
return endpoint.getConnectionTimeout();
}
public void setConnectionTimeout(int timeout) {
endpoint.setConnectionTimeout(timeout);
}
@Deprecated
public int getSoTimeout() {
return getConnectionTimeout();
}
@Deprecated
public void setSoTimeout(int timeout) {
setConnectionTimeout(timeout);
}
public int getMaxHeaderCount() {
return endpoint.getMaxHeaderCount();
}
public void setMaxHeaderCount(int maxHeaderCount) {
endpoint.setMaxHeaderCount(maxHeaderCount);
}
public long getConnectionCount() {
return endpoint.getConnectionCount();
}
public void setAcceptorThreadCount(int threadCount) {
endpoint.setAcceptorThreadCount(threadCount);
}
public int getAcceptorThreadCount() {
return endpoint.getAcceptorThreadCount();
}
public void setAcceptorThreadPriority(int threadPriority) {
endpoint.setAcceptorThreadPriority(threadPriority);
}
public int getAcceptorThreadPriority() {
return endpoint.getAcceptorThreadPriority();
}
public synchronized int getNameIndex() {
if (nameIndex == 0) {
nameIndex = nameCounter.incrementAndGet();
}
return nameIndex;
}
public String getName() {
return ObjectName.quote(getNameInternal());
}
private String getNameInternal() {
StringBuilder name = new StringBuilder(getNamePrefix());
name.append('-');
if (getAddress() != null) {
name.append(getAddress().getHostAddress());
name.append('-');
}
int port = getPort();
if (port == 0) {
name.append("auto-");
name.append(getNameIndex());
port = getLocalPort();
if (port != -1) {
name.append('-');
name.append(port);
}
} else {
name.append(port);
}
return name.toString();
}
public void addWaitingProcessor(Processor processor) {
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractProtocol.waitingProcessor.add", processor));
}
waitingProcessors.add(processor);
}
public void removeWaitingProcessor(Processor processor) {
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractProtocol.waitingProcessor.remove", processor));
}
waitingProcessors.remove(processor);
}
public int getWaitingProcessorCount() {
return waitingProcessors.size();
}
protected AbstractEndpoint<S> getEndpoint() {
return endpoint;
}
protected Handler<S> getHandler() {
return handler;
}
protected void setHandler(Handler<S> handler) {
this.handler = handler;
protected abstract Processor createProcessor();
protected abstract Processor createUpgradeProcessor(
SocketWrapperBase<?> socket,
UpgradeToken upgradeToken);
@Override
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception {
oname = name;
mserver = server;
domain = name.getDomain();
return name;
}
}
接下来看看
adapter
适配器类
public interface Adapter {
public void service(Request req, Response res) throws Exception;
public boolean prepare(Request req, Response res) throws Exception;
public boolean asyncDispatch(Request req,Response res, SocketEvent status)
throws Exception;
public void log(Request req, Response res, long time);
public void checkRecycled(Request req, Response res)
public String getDomain();
}
这里就是Adapter接口的一个源代码了,这里可以看到核心类
public void
service
(
Request
req,
Response
res)
就是我们实现
handler
与
CoyoteAdapter的一个桥梁了.接下来我们看看这个桥梁做了什么事情.看一下CoyoteAdapter的源代码了.
public class CoyoteAdapter implements Adapter {
public CoyoteAdapter(Connector connector) {
super();
this.connector = connector;
}
/**省略部分代码*/
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
request.setResponse(response);
response.setRequest(request);
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
if (connector.getXpoweredBy()) {
response.addHeader("X-Powered-By", POWERED_BY);
}
boolean async = false;
boolean postParseSuccess = false;
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
try {
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
}
if (request.isAsync()) {
async = true;
ReadListener readListener = req.getReadListener();
if (readListener != null && request.isFinished()) {
ClassLoader oldCL = null;
try {
oldCL = request.getContext().bind(false, null);
if (req.sendAllDataReadEvent()) {
req.getReadListener().onAllDataRead();
}
} finally {
request.getContext().unbind(false, oldCL);
}
}
Throwable throwable =
(Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if (!request.isAsyncCompleting() && throwable != null) {
request.getAsyncContextInternal().setErrorState(throwable, true);
}
} else {
request.finishRequest();
response.finishResponse();
}
} catch (IOException e) {
} finally {
AtomicBoolean error = new AtomicBoolean(false);
res.action(ActionCode.IS_ERROR, error);
if (request.isAsyncCompleting() && error.get()) {
res.action(ActionCode.ASYNC_POST_PROCESS, null);
async = false;
}
if (!async && postParseSuccess) {
Context context = request.getContext();
Host host = request.getHost();
long time = System.currentTimeMillis() - req.getStartTime();
if (context != null) {
context.logAccess(request, response, time, false);
} else if (response.isError()) {
if (host != null) {
host.logAccess(request, response, time, false);
} else {
connector.getService().getContainer().logAccess(
request, response, time, false);
}
}
}
req.getRequestProcessor().setWorkerThreadName(null);
// Recycle the wrapper request and response
if (!async) {
updateWrapperErrorCount(request, response);
request.recycle();
response.recycle();
}
}
}
}
这里主要看一下 service(org.apache.coyote.Request req, org.apache.coyote.Response res)这个方法,通过这个具体的Adapter怎么把req和rep进行了转换.可以看到这里 connector.getService().getContainer().getPipeline().getFirst().
invoke(request, response);这里可以看到我们准备调用容器的门阀了,但是我们看到的是我们需要的参数数类型好好像跟Connector传过来给到我们的不太一样.为了满足这个需求.这里我们就采用了适配器模式.通过把如下两个请求对象
- org.apache.coyote.Request req
- org.apache.coyote.Response res
转化成容器需要的请求对象类型如下面所示的两个请求对象
- org.apache.catalina.connector.Request
- org.apache.catalina.connector.Response
就是通过 Adapter的具体适配器类来做了中间这个转化的.服务器处理一个请求分为 建立连接、按照协议解析请求、处理请求三个部分,因此Tomcat将其分成了两个模块,分别为Connector和Engine,为了方便两个模块之间彼此解耦,通过实现适配器模式达到两个模块的解耦.
8.3
模版方法模式
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
应用实例:
- 在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。
2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。
3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点:
- 封装不变部分,扩展可变部分。
2、提取公共代码,便于维护。
3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景:
- 有多个子类共有的方法,且逻辑相同。
2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。
如上图所示tomcat容器的架构层次就是使用的模版方法模式了.这里主要是tomcat容器的生命周期执行函数.我们可以先看一下这有一个父接口
Lifecycle
里面有生命周期方法.可以看到
Server
|
Host
|
Context
|
Service
|
Wapper
都是继承自这个接口的
public interface Lifecycle {
public void init() throws LifecycleException;
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
public void destroy() throws LifecycleException;
}
如上代码所示是我们容器的生命周期接口,可以看到就是我们一些生命周期方法的定义,下面我们来看看主要实现的核心代码
public abstract class LifecycleBase implements Lifecycle {
private static final Log log = LogFactory.getLog(LifecycleBase.class);
private static final StringManager sm = StringManager.getManager(LifecycleBase.class);
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
private volatile LifecycleState state = LifecycleState.NEW;
private boolean throwOnFailure = true;
public boolean getThrowOnFailure() {
return throwOnFailure;
}
public void setThrowOnFailure(boolean throwOnFailure) {
this.throwOnFailure = throwOnFailure;
}
@Override
public void addLifecycleListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
}
@Override
public LifecycleListener[] findLifecycleListeners() {
return lifecycleListeners.toArray(new LifecycleListener[0]);
}
@Override
public void removeLifecycleListener(LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}
@Override
public final synchronized void start() throws LifecycleException {
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();
if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
@Override
public final synchronized void stop() throws LifecycleException {
if (LifecycleState.STOPPING_PREP.equals(state) || LifecycleState.STOPPING.equals(state) ||
LifecycleState.STOPPED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStopped", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStopped", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
state = LifecycleState.STOPPED;
return;
}
if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.BEFORE_STOP_EVENT);
}
try {
if (state.equals(LifecycleState.FAILED)) {
fireLifecycleEvent(BEFORE_STOP_EVENT, null);
} else {
setStateInternal(LifecycleState.STOPPING_PREP, null, false);
}
stopInternal();
if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.AFTER_STOP_EVENT);
}
setStateInternal(LifecycleState.STOPPED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.stopFail", toString());
} finally {
if (this instanceof Lifecycle.SingleUse) {
setStateInternal(LifecycleState.STOPPED, null, false);
destroy();
}
}
}
@Override
public final synchronized void destroy() throws LifecycleException {
if (LifecycleState.FAILED.equals(state)) {
try {
// Triggers clean-up
stop();
} catch (LifecycleException e) {
// Just log. Still want to destroy.
log.error(sm.getString("lifecycleBase.destroyStopFail", toString()), e);
}
}
if (LifecycleState.DESTROYING.equals(state) || LifecycleState.DESTROYED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyDestroyed", toString()), e);
} else if (log.isInfoEnabled() && !(this instanceof Lifecycle.SingleUse)) {
log.info(sm.getString("lifecycleBase.alreadyDestroyed", toString()));
}
return;
}
if (!state.equals(LifecycleState.STOPPED) && !state.equals(LifecycleState.FAILED) &&
!state.equals(LifecycleState.NEW) && !state.equals(LifecycleState.INITIALIZED)) {
invalidTransition(Lifecycle.BEFORE_DESTROY_EVENT);
}
try {
setStateInternal(LifecycleState.DESTROYING, null, false);
destroyInternal();
setStateInternal(LifecycleState.DESTROYED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.destroyFail", toString());
}
}
protected abstract void destroyInternal() throws LifecycleException;
protected abstract void stopInternal() throws LifecycleException;
protected abstract void startInternal() throws LifecycleException;
protected abstract void initInternal() throws LifecycleException;
}
可以看到这个设计实际上是很精妙的,如上所示每个服务接口都继承来生命周期接口
Lifecycle
可以看到在每个服务接口的具体实现类里面.通过调用生命周期接口函数还调用子类.然而这个时候子类中并没有生命周期接口方法.而是把生命周期方法的实现放在了
LifecycleBase
这个抽象类中.然后通过在抽象类中定义相关接口的实现模版可以看到如上所示的四个
protected
抽象方法就是交给子类去实现的.所以每一次调用到生命周期函数.就会调回到父类的模版中,再调用子类对四个pro
tected
抽象方法的重写里面.以上就是
tomcat
里面比较典型的模版方法模式的使用
.
8
.
4
观察者模式
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例:
- 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
缺点:
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
注意事项:
- JAVA 中已经有了对观察者模式的支持类。
2、避免循环引用。
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
观察者模式,Tomcat的生命周期管理便很容易理解了。所涉及的类有:
Lifecycle:相当于抽象主题角色,所有的容器类与组件实现类都实现了这个接口。如StandardContext
LifecycleListener:相当于抽象观察者角色,具体的实现类有ContextConfig, HostConfig, EngineConfig类,它们在容器启动时与停止时触发。
LifecycleEvent:生命周期事件,对主题与发生的事件进行封装。
LifecycleBase:生命周期管理的实用类,提供对观察者的添加,删除及通知观察者的方法。
Lifecycle
接口
public interface Lifecycle {
public static final String BEFORE_INIT_EVENT = "before_init";
public static final String AFTER_INIT_EVENT = "after_init";
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
public static final String AFTER_DESTROY_EVENT = "after_destroy";
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
public static final String PERIODIC_EVENT = "periodic";
public static final String CONFIGURE_START_EVENT = "configure_start";
public static final String CONFIGURE_STOP_EVENT = "configure_stop";
public void addLifecycleListener(LifecycleListener listener);
public LifecycleListener[] findLifecycleListeners();
public void removeLifecycleListener(LifecycleListener listener);
public void init() throws LifecycleException;
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
public void destroy() throws LifecycleException;
public LifecycleState getState();
public String getStateName();
public interface SingleUse {
}
}
Lifecycle相当于观察者模式中的抽象主题角色(Observable),它定义了添加、删除及通知管理者的方法。还定义了与生命周期相关的6个事件。Start和stop方法是Lifecycle最重要的两个方法,分别代表启动与停止。所有四种容器的标准实现类(StandardEngine, StandardHost, StandardContext,StandardWrapper)和基本组件(Logger,Loader,Manager等)的实现类都实现了Lifecycle接口,这意味着它们都是具体的观察者,具有启动和停止方法。容器启动时,主要做三件事:调用组件的启动方法,启动组件;调用子容器的启动方法,启动子容器;通知容器的观察者,使其执行相应的启动动作。子容器启动也做这三件事,这样整个Tomcat便启动了。Tomcat的停止也类似。
LifecycleListener 抽象观察者
public interface LifecycleListener {
public void lifecycleEvent(LifecycleEvent event);
}
LifecycleBase 具体的主题
public abstract class LifecycleBase implements Lifecycle {
private static final Log log = LogFactory.getLog(LifecycleBase.class);
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
/**省略部分代码*/
@Override
public void addLifecycleListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
}
@Override
public LifecycleListener[] findLifecycleListeners() {
return lifecycleListeners.toArray(new LifecycleListener[0]);
}
@Override
public void removeLifecycleListener(LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
/**省略部分代码*/
}
具体的观察者 ContextConfig
public class ContextConfig implements LifecycleListener {
private static final Log log = LogFactory.getLog(ContextConfig.class);
protected static final LoginConfig DUMMY_LOGIN_CONFIG =
new LoginConfig("NONE", null, null, null);
protected static final Properties authenticators;
protected final Map<ServletContainerInitializer, Set<Class<?>>> initializerClassMap =
new LinkedHashMap<>();
protected final Map<Class<?>, Set<ServletContainerInitializer>> typeInitializerMap =
new HashMap<>();
protected boolean handlesTypesAnnotations = false;
protected boolean handlesTypesNonAnnotations = false;
public String getDefaultWebXml() {
if (defaultWebXml == null) {
defaultWebXml = Constants.DefaultWebXml;
}
return defaultWebXml;
}
@Override
public void lifecycleEvent(LifecycleEvent event) {
try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
}
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();
} else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
}
}
}
如上所示具体的观察者的话只要处理好
lifecycleEvent
(
LifecycleEvent
event)
这个的事件监听就好了.
LifecycleEvent
事件类
public final class LifecycleEvent extends EventObject {
private static final long serialVersionUID = 1L;
public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
super(lifecycle);
this.type = type;
this.data = data;
}
private final Object data;
private final String type;
public Object getData() {
return data;
}
public Lifecycle getLifecycle() {
return (Lifecycle) getSource();
}
public String getType() {
return this.type;
}
}
至此
tomcat
中观察者模式的实现已经完成了.
8
.
5
装饰者模式
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
何时使用:在不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。
关键代码:
- Component 类充当抽象角色,不应该具体实现。
2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
应用实例:
- 孙悟空有 72 变,当他变成”庙宇”后,他的根本还是一只猴子,但是他又有了庙宇的功能。
2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
使用场景:
1、扩展一个类的功能。
2、动态增加功能,动态撤销。
注意事项:可代替继承,意思就是说我们用继承的地方都可以使用装饰者模式。
8
.
6
外观模式
意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端之间的接口。
何时使用:
- 客户端不需要知道系统内部的复杂联系,整个系统只需提供一个”接待员”即可。
2、定义系统的入口。
如何解决:客户端不与系统耦合,外观类与系统耦合。
关键代码:在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。
应用实例:
- 去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便。
2、JAVA 的三层开发模式。
优点:
- 减少系统相互依赖。
- 提高灵活性。
3、提高了安全性。
缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
使用场景:
- 为复杂的模块或子系统提供外界访问的模块。
- 子系统相对独立。
3、预防低水平人员带来的风险。
注意事项:在层次化结构中,可以使用外观模式定义系统中每一层的入口。
public interface HttpServletRequest extends ServletRequest {
public static final String BASIC_AUTH = "BASIC";
public static final String FORM_AUTH = "FORM";
public static final String CLIENT_CERT_AUTH = "CLIENT_CERT";
public static final String DIGEST_AUTH = "DIGEST";
public String getAuthType();
public Cookie[] getCookies();
public long getDateHeader(String name);
public String getHeader(String name);
public Enumeration<String> getHeaders(String name);
public Enumeration<String> getHeaderNames();
public int getIntHeader(String name);
public String getMethod();
public String getPathInfo();
public String getPathTranslated();
public String getContextPath();
public String getQueryString();
public String getRemoteUser();
public boolean isUserInRole(String role);
public java.security.Principal getUserPrincipal();
public String getRequestedSessionId();
public String getRequestURI();
public StringBuffer getRequestURL();
public String getServletPath();
public HttpSession getSession(boolean create);
public HttpSession getSession();
public String changeSessionId();
public boolean isRequestedSessionIdValid();
public boolean isRequestedSessionIdFromCookie();
public boolean isRequestedSessionIdFromURL();
public boolean isRequestedSessionIdFromUrl();
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException;
public void login(String username, String password) throws ServletException;
public void logout() throws ServletException;
public Collection<Part> getParts() throws IOException,ServletException;
public Part getPart(String name) throws IOException, ServletException;
public <T extends HttpUpgradeHandler> T upgrade(Class<T> httpUpgradeHandlerClass) throws java.io.IOException, ServletException;
}
Request
public class Request implements HttpServletRequest {
private static final String HTTP_UPGRADE_HEADER_NAME = "upgrade";
private static final Log log = LogFactory.getLog(Request.class);
public Request() {
formats = new SimpleDateFormat[formatsTemplate.length];
for(int i = 0; i < formats.length; i++) {
formats[i] = (SimpleDateFormat) formatsTemplate[i].clone();
}
}
private HttpServletRequest applicationRequest = null;
protected void addPathParameter(String name, String value) {
coyoteRequest.addPathParameter(name, value);
}
protected String getPathParameter(String name) {
return coyoteRequest.getPathParameter(name);
}
public void setAsyncSupported(boolean asyncSupported) {
this.asyncSupported = Boolean.valueOf(asyncSupported);
}
/**省略部分代码*/
public void setRequest(HttpServletRequest applicationRequest) {
ServletRequest r = applicationRequest;
while (r instanceof HttpServletRequestWrapper) {
r = ((HttpServletRequestWrapper) r).getRequest();
}
if (r != facade) {
throw new IllegalArgumentException(sm.getString("request.illegalWrap"));
}
this.applicationRequest = applicationRequest;
}
}
RequestFacade
public class RequestFacade implements HttpServletRequest {
public RequestFacade(Request request) {
this.request = request;
}
protected Request request = null;
protected static final StringManager sm = StringManager.getManager(RequestFacade.class);
/**省略部分代码*/
public <T extends HttpUpgradeHandler> T upgrade(
Class<T> httpUpgradeHandlerClass) throws java.io.IOException, ServletException {
return request.upgrade(httpUpgradeHandlerClass);
}
public ApplicationMappingImpl getHttpServletMapping() {
return request.getHttpServletMapping();
}
public ApplicationPushBuilder newPushBuilder(javax.servlet.http.HttpServletRequest request) {
return this.request.newPushBuilder(request);
}
public ApplicationPushBuilder newPushBuilder() {
return request.newPushBuilder();
}
}
8
.
7
策略模式
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例:
- 诸葛亮的锦囊妙计,每一个锦囊就是一个策略。
- 旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。
- JAVA AWT 中的 LayoutManager。
优点:
- 算法可以自由切换。
- 避免使用多重条件判断。
- 扩展性良好。
缺点:
- 策略类会增多。
- 所有策略类都需要对外暴露。
使用场景:
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
- 一个系统需要动态地在几种算法中选择一种。
- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
通信方式包括BIO、NIO、NIO2等,应用层协议包括HTTP1.1 HTTP2、AJP等等,而用户可以通过组合的方式进行使用,因此Tomcat中存在多种实现网络通信功能的策略,诸如NIO + HTTP1.1 Strategy、NIO + AJP Strategy,因此在网络通信层面上,Tomcat需要组织代码结构以支持通信协议的动态变化(如某种协议的新增)。
我们可以先按照OO设计原则去设计。
封装变化,该场景下代码最核心的需要支持的变化是网络通信方式的变化,因此我们需要将此功能与其他功能分隔开;
针对接口编程而不是针对实现编程,因此我们需要定义interface ProtocolHandler,并让不同协议实现该接口;
多用组合 上层组件则委托上面定义的接口以使用不同协议实现的网络通信功能
其中Connector(Tomcat中的连接器组件)依赖ProtocolHandler以实现不同的网络通信方式,如此当我们有协议的增改时,我们只需要增加或修改对应的 Protocol实现类即可,无需修改Connector中的逻辑。接着我们再来看策略模式的定义:策略模式定义了算法簇,分别封装起来,让它们之间可以相互替换,此模式可以让算法的变化独立于使用算法的客户。
可以看到使用策略模式设计与遵循相应的设计原则设计一致,因此设计模式可以帮助我们可以更快更好的使用设计原则。
接着最核心的是我们需要在Connector中支持动态设置不同的协议实现类,Tomcat中是依据用户所使用的协议类型通过if-else实例化(new)出对应的协议实现类放在Connector中。
public class Connector extends LifecycleMBeanBase {
public Connector() {
this(null);
}
public Connector(String protocol) {
setProtocol(protocol);
ProtocolHandler p = null;
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
p = (ProtocolHandler) clazz.getConstructor().newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
} finally {
this.protocolHandler = p;
}
if (Globals.STRICT_SERVLET_COMPLIANCE) {
uriCharset = StandardCharsets.ISO_8859_1;
} else {
uriCharset = StandardCharsets.UTF_8;
}
if (Boolean.parseBoolean(System.getProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "false"))) {
encodedSolidusHandling = EncodedSolidusHandling.DECODE;
}
public void setProtocol(String protocol) {
boolean aprConnector = AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseAprConnector();
if ("HTTP/1.1".equals(protocol) || protocol == null) {
if (aprConnector) {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
} else {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
}
} else if ("AJP/1.3".equals(protocol)) {
if (aprConnector) {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
} else {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");
}
} else {
setProtocolHandlerClassName(protocol);
}
}
}
如上红色代码所示就是策略模式的体现.如上类图所示的结果.
https://blog.csdn.net/worn_xiao/article/details/122260936