Java开源框架中的设计模式以及应用场景,对日java开发面试经验

这种模式,常见于在构建一个复杂的对象,里面可能包含一些业务逻辑,比如检查,属性转换等。如果都在客户端手动去设置,那么会产生大量的冗余代码。那么这时候,我们就可以考虑使用构造器模式。

比如,在MyBatis中,MappedStatement的创建过程就使用了构造器模式。

我们知道,XML文件中的每一个SQL标签就要生成一个MappedStatement对象,它里面包含很多个属性,我们要构造的对象也是它。

public final class MappedStatement {

private String resource;

private Configuration configuration;

private String id;

private SqlSource sqlSource;

private ParameterMap parameterMap;

private List resultMaps;

//…省略大部分属性

}

然后有一个内部类Builder,它负责完成MappedStatement对象的构造。

首先,这个Builder类,通过默认的构造函数,先完成对MappedStatement对象,部分的构造。

public static class Builder {

private MappedStatement mappedStatement = new MappedStatement();

public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {

mappedStatement.configuration = configuration;

mappedStatement.id = id;

mappedStatement.sqlSource = sqlSource;

mappedStatement.statementType = StatementType.PREPARED;

mappedStatement.resultSetType = ResultSetType.DEFAULT;

//…省略大部分过程

}

}

然后,通过一系列方法,可以设置特定的属性,并返回这个Builder类,这里的方法适合处理一些业务逻辑。

public static class Builder {

public Builder parameterMap(ParameterMap parameterMap) {

mappedStatement.parameterMap = parameterMap;

return this;

}

public Builder resultMaps(List resultMaps) {

mappedStatement.resultMaps = resultMaps;

for (ResultMap resultMap : resultMaps) {

mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();

}

return this;

}

public Builder statementType(StatementType statementType) {

mappedStatement.statementType = statementType;

return this;

}

public Builder resultSetType(ResultSetType resultSetType) {

mappedStatement.resultSetType = resultSetType == null ? ResultSetType.DEFAULT : resultSetType;

return this;

}

}

最后呢,就是提供一个build方法,返回构建完成的对象就好了。

public MappedStatement build() {

assert mappedStatement.configuration != null;

assert mappedStatement.id != null;

assert mappedStatement.sqlSource != null;

assert mappedStatement.lang != null;

mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);

return mappedStatement;

}

在客户端使用的时候,先创建一个Builder,然后链式的调用一堆方法,最后再调用一次build()方法,我们需要的对象就有了,这就是构造器模式的应用。

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);

MappedStatement statement = statementBuilder.build();

configuration.addMappedStatement(statement);

return statement;

五、适配器模式

适配器模式是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

适配器模式一般用于屏蔽业务逻辑与第三方服务的交互,或者是新老接口之间的差异。

我们知道,在Dubbo中,所有的数据都是通过Netty来负责传输的,然后这就涉及了消息编解码的问题。

所以,首先它有一个编解码器的接口,负责编码和解码。

@SPI

public interface Codec2 {

@Adaptive({Constants.CODEC_KEY})

void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException;

@Adaptive({Constants.CODEC_KEY})

Object decode(Channel channel, ChannelBuffer buffer) throws IOException;

enum DecodeResult {

NEED_MORE_INPUT, SKIP_SOME_INPUT

}

}

然后,有几个实现类,比如DubboCountCodec、DubboCodec、ExchangeCodec等。

但是,当我们打开这些类的时候,就会发现,他们都是Dubbo中普通的类,只是实现了Codec2接口,其实不能直接作用于Netty编解码。

这是因为,Netty编解码需要实现ChannelHandler接口,这样才会被声明成Netty的处理组件。比如像MessageToByteEncoder、ByteToMessageDecoder那样。

鉴于此,Dubbo搞了一个适配器,专门来适配编解码器接口。

final public class NettyCodecAdapter {

private final ChannelHandler encoder = new InternalEncoder();

private final ChannelHandler decoder = new InternalDecoder();

private final Codec2 codec;

private final URL url;

private final org.apache.dubbo.remoting.ChannelHandler handler;

public NettyCodecAdapter(Codec2 codec, URL url, org.apache.dubbo.remoting.ChannelHandler handler) {

this.codec = codec;

this.url = url;

this.handler = handler;

}

public ChannelHandler getEncoder() {

return encoder;

}

public ChannelHandler getDecoder() {

return decoder;

}

private class InternalEncoder extends MessageToByteEncoder {

@Override

protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {

org.apache.dubbo.remoting.buffer.ChannelBuffer buffer = new NettyBackedChannelBuffer(out);

Channel ch = ctx.channel();

NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);

codec.encode(channel, buffer, msg);

}

}

private class InternalDecoder extends ByteToMessageDecoder {

@Override

protected void decode(ChannelHandlerContext ctx, ByteBuf input, List out) throws Exception {

ChannelBuffer message = new NettyBackedChannelBuffer(input);

NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);

//解码对象

codec.decode(channel, message);

//省略部分代码…

}

}

}

上面的代码中,我们看到,NettyCodecAdapter类适配的是Codec2接口,通过构造函数传递实现类,然后定义了内部的编码器实现和解码器实现,同时它们都是ChannelHandler。

这样的话,在内部类里面的编码和解码逻辑,真正调用的还是Codec2接口。

最后我们再来看看,该适配器的调用方式。

//通过SPI方式获取编解码器的实现类,比如这里是DubboCountCodec

Codec2 codec = ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(“dubbo”);

URL url = new URL(“dubbo”, “localhost”, 22226);

//创建适配器

NettyCodecAdapter adapter = new NettyCodecAdapter(codec, url, NettyClient.this);

//向ChannelPipeline中添加编解码处理器

ch.pipeline()

.addLast(“decoder”, adapter.getDecoder())

.addLast(“encoder”, adapter.getEncoder())

以上,就是Dubbo中关于编解码器对于适配器模式的应用。

六、责任链模式

责任链模式为请求创建了一个接收者对象的链。允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。

我们来看一个Netty中的例子。我们知道,在Netty中服务端处理消息,就要添加一个或多个ChannelHandler。那么,承载这些ChannelHandler的就是ChannelPipeline,它的实现过程就体现了责任链模式的应用。

ServerBootstrap serverBootstrap = new ServerBootstrap();

serverBootstrap.childHandler(new ChannelInitializer() {

protected void initChannel(NioSocketChannel channel) {

channel.pipeline()

.addLast(new ChannelHandler1())

.addLast(new ChannelHandler2())

.addLast(new ChannelHandler3());

}

});

需要知道的是,在Netty整个框架里面,一条连接对应着一个Channel,每一个新创建的Channel都将会被分配一个新的ChannelPipeline。

ChannelPipeline里面保存的是ChannelHandlerContext对象,它是Channel相关的上下文对象,里面包着我们定义的处理器ChannelHandler。

根据事件的起源,IO事件将会被ChannelInboundHandler或者ChannelOutboundHandler处理。随后,通过调用ChannelHandlerContext实现,它将被转发给同一超类型的下一个ChannelHandler。

1、ChannelHandler

首先,我们来看责任处理器接口,Netty中的ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。

public interface ChannelHandler {

//当把 ChannelHandler 添加到 ChannelPipeline 中时被调用

void handlerAdded(ChannelHandlerContext ctx) throws Exception;

//当从 ChannelPipeline 中移除 ChannelHandler 时被调用

void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

//当处理过程中在 ChannelPipeline 中有错误产生时被调用

void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

}

然后Netty定义了下面两个重要的ChannelHandler子接口:

1、ChannelInboundHandler,处理入站数据以及各种状态变化;

public interface ChannelInboundHandler extends ChannelHandler {

//当 Channel 已经注册到它的 EventLoop 并且能够处理 I/O 时被调用

void channelRegistered(ChannelHandlerContext ctx) throws Exception;

//当 Channel 从它的 EventLoop 注销并且无法处理任何 I/O 时被调用

void channelUnregistered(ChannelHandlerContext ctx) throws Exception;

//当 Channel 处于活动状态时被调用;Channel 已经连接/绑定并且已经就绪

void channelActive(ChannelHandlerContext ctx) throws Exception;

//当 Channel 离开活动状态并且不再连接它的远程节点时被调用

void channelInactive(ChannelHandlerContext ctx) throws Exception;

当从 Channel 读取数据时被调用

void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;

//当 Channel上的一个读操作完成时被调用

void channelReadComplete(ChannelHandlerContext ctx) throws Exception;

void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;

void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;

void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

}

2、ChannelOutboundHandler,处理出站数据并且允许拦截所有的操作;

public interface ChannelOutboundHandler extends ChannelHandler {

//当请求将 Channel 绑定到本地地址时被调用

void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;

//当请求将 Channel 连接到远程节点时被调用

void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,SocketAddress localAddress,

ChannelPromise promise) throws Exception;

//当请求将 Channel 从远程节点断开时被调用

void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

//当请求关闭 Channel 时被调用

void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

//当请求将 Channel 从它的 EventLoop 注销时被调用

void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

//当请求从 Channel 读取更多的数据时被调用

void read(ChannelHandlerContext ctx) throws Exception;

//当请求通过 Channel 将数据写到远程节点时被调用

void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;

//当请求通过 Channel 将入队数据冲刷到远程节点时被调用

void flush(ChannelHandlerContext ctx) throws Exception;

}

2、ChannelPipeline

既然叫做责任链模式,那就需要有一个“链”,在Netty中就是ChannelPipeline。

ChannelPipeline提供了ChannelHandler链的容器,并定义了用于在该链上传播入站和出站事件流的方法,另外它还具有添加删除责任处理器接口的功能。

public interface ChannelPipeline{

ChannelPipeline addFirst(String name, ChannelHandler handler);

ChannelPipeline addLast(String name, ChannelHandler handler);

ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);

ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler);

ChannelPipeline remove(ChannelHandler handler);

ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);

@Override

ChannelPipeline fireChannelRegistered();

@Override

ChannelPipeline fireChannelActive();

@Override

ChannelPipeline fireExceptionCaught(Throwable cause);

@Override

ChannelPipeline fireUserEventTriggered(Object event);

@Override

ChannelPipeline fireChannelRead(Object msg);

@Override

ChannelPipeline flush();

//省略部分方法…

}

然后我们看它的实现,默认有两个节点,头结点和尾结点。并在构造函数中,使它们首尾相连。这就是标准的链式结构。

public class DefaultChannelPipeline implements ChannelPipeline {

final AbstractChannelHandlerContext head;

final AbstractChannelHandlerContext tail;

private final Channel channel;

protected DefaultChannelPipeline(Channel channel) {

this.channel = ObjectUtil.checkNotNull(channel, “channel”);

tail = new TailContext(this);

head = new HeadContext(this);

head.next = tail;

tail.prev = head;

}

}

当有新的ChannelHandler被添加时,则将其封装为ChannelHandlerContext对象,然后插入到链表中。

private void addLast0(AbstractChannelHandlerContext newCtx) {

AbstractChannelHandlerContext prev = tail.prev;

newCtx.prev = prev;

newCtx.next = tail;

prev.next = newCtx;

tail.prev = newCtx;

}

3、ChannelHandlerContext

ChannelHandlerContext代表了ChannelHandler和ChannelPipeline之间的关联,每当有ChannelHandler添加到ChannelPipeline中时,都会创建ChannelHandlerContext。

ChannelHandlerContext的主要功能是管理它所关联的ChannelHandler和在同一个ChannelPipeline中的其他ChannelHandler之间的交互。

public interface ChannelHandlerContext{

Channel channel();

EventExecutor executor();

ChannelHandler handler();

ChannelPipeline pipeline();

@Override

ChannelHandlerContext fireChannelRegistered();

@Override

ChannelHandlerContext fireChannelUnregistered();

@Override

ChannelHandlerContext fireChannelActive();

@Override

ChannelHandlerContext fireChannelRead(Object msg);

@Override

ChannelHandlerContext read();

@Override

ChannelHandlerContext flush();

//省略部分方法……

}

ChannelHandlerContext负责在链上传播责任处理器接口的事件。

它有两个重要的方法,查找Inbound类型和Outbound类型的处理器。

值得注意的是,如果一个入站事件被触发,它将被从ChannelPipeline的头部开始一直被传播到ChannelPipeline的尾端;一个出站事件将从ChannelPipeline的最右边开始,然后向左传播。

abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {

volatile AbstractChannelHandlerContext next;

volatile AbstractChannelHandlerContext prev;

//查找下一个Inbound类型的处理器,左 > 右

private AbstractChannelHandlerContext findContextInbound(int mask) {

AbstractChannelHandlerContext ctx = this;

EventExecutor currentExecutor = executor();

do {

ctx = ctx.next;

} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));

return ctx;

}

//查找下一个Outbound类型的处理器,右 > 左

private AbstractChannelHandlerContext findContextOutbound(int mask) {

AbstractChannelHandlerContext ctx = this;

EventExecutor currentExecutor = executor();

do {

ctx = ctx.prev;

} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND));

return ctx;

}

}

4、处理流程

当我们向服务端发送消息的时候,将会触发read方法。

public abstract class AbstractNioByteChannel extends AbstractNioChannel {

public final void read() {

//从Channel中获取对应的ChannelPipeline

final ChannelPipeline pipeline = pipeline();

//数据载体

ByteBuf byteBuf = allocHandle.allocate(allocator);

//传递数据

pipeline.fireChannelRead(byteBuf);

}

}

上面的代码中,就会调用到ChannelPipeline,它会从Head节点开始,根据上下文对象依次调用处理器。

public class DefaultChannelPipeline implements ChannelPipeline {

public final ChannelPipeline fireChannelRead(Object msg) {

AbstractChannelHandlerContext.invokeChannelRead(head, msg);

return this;

}

}

因为第一个节点是默认的头结点HeadContext,所以它是从ChannelHandlerContext开始的。

abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {

//找到下一个ChannelHandler并执行

public ChannelHandlerContext fireChannelRead(final Object msg) {

invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);

return this;

}

}

然后在我们自定义的ChannelHandler中,就会被调用到。

public class ChannelHandler1 extends ChannelInboundHandlerAdapter {

public void channelRead(ChannelHandlerContext ctx, Object msg){

System.out.println(“ChannelHandler1:”+msg);

ctx.fireChannelRead(msg);

}

}

如果消息有多个ChannelHandler,你可以自由选择是否继续往下传递请求。

比如,如果你认为消息已经被处理且不应该继续往下调用,把上面的ctx.fireChannelRead(msg);注释掉就终止了整个责任链。

七、策略模式

该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。

策略模式是一个很常见,而且也很好用的设计模式,如果你的业务代码中有大量的if…else,那么就可以考虑是否可以使用策略模式改造一下。

RocketMQ我们大家都熟悉,是一款优秀的分布式消息中间件。消息中间件,简单来说,就是客户端发送一条消息,服务端存储起来并提供给消费者去消费。

请求消息的类型多种多样,处理过程肯定也不一样,每次都判断一下再处理就落了下乘。在RocketMQ里,它会把所有处理器注册起来,然后根据请求消息的code,让对应的处理器处理请求,这就是策略模式的应用。

首先,它们需要实现同一个接口,在这里就是请求处理器接口。

public interface NettyRequestProcessor {

RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request)throws Exception;

boolean rejectRequest();

}

这个接口只做一件事,就是处理来自客户端的请求。不同类型的请求封装成不同的RemotingCommand对象。

RocketMQ大概有90多种请求类型,都在RequestCode里以code来区分。

然后,定义一系列策略类。我们来看几个。

//默认的消息处理器

public class DefaultRequestProcessor implements NettyRequestProcessor {}

//发送消息的处理器

public class SendMessageProcessor extends AbstractSendMessageProcessor implements NettyRequestProcessor {}

//拉取消息的处理器

public class PullMessageProcessor implements NettyRequestProcessor {}

//查询消息的处理器

public class QueryMessageProcessor implements NettyRequestProcessor {}

//消费者端管理的处理器

public class ConsumerManageProcessor implements NettyRequestProcessor {}

接着,将这些策略类封装起来。在RocketMQ中,在启动Broker服务器的时候,注册这些处理器。

public class BrokerController {

public void registerProcessor() {

SendMessageProcessor sendProcessor = new SendMessageProcessor(this);

this.remotingServer.registerProcessor(RequestCode.SEND_MESSAGE, sendProcessor, this.sendMessageExecutor);

this.remotingServer.registerProcessor(RequestCode.SEND_MESSAGE_V2, sendProcessor, this.sendMessageExecutor);

this.remotingServer.registerProcessor(RequestCode.PULL_MESSAGE,this.pullMessageProcessor,this.pullMessageExecutor);

this.remotingServer.registerProcessor(RequestCode.SEND_REPLY_MESSAGE, replyMessageProcessor, replyMessageExecutor);

this.remotingServer.registerProcessor(RequestCode.SEND_REPLY_MESSAGE_V2, replyMessageProcessor, replyMessageExecutor);

NettyRequestProcessor queryProcessor = new QueryMessageProcessor(this);

this.remotingServer.registerProcessor(RequestCode.QUERY_MESSAGE, queryProcessor, this.queryMessageExecutor);

this.remotingServer.registerProcessor(RequestCode.VIEW_MESSAGE_BY_ID, queryProcessor, this.queryMessageExecutor);

ClientManageProcessor clientProcessor = new ClientManageProcessor(this);

this.remotingServer.registerProcessor(RequestCode.HEART_BEAT, clientProcessor, this.heartbeatExecutor);

this.remotingServer.registerProcessor(RequestCode.UNREGISTER_CLIENT, clientProcessor, this.clientManageExecutor);

//省略部分注册过程…

}

}

最后,在Netty接收到客户端的请求之后,就会根据消息的类型,找到对应的策略类,去处理消息。

public abstract class NettyRemotingAbstract {

public void processRequestCommand(final ChannelHandlerContext ctx, final RemotingCommand cmd) {

//根据请求类型找到对应的策略类

final Pair<NettyRequestProcessor, ExecutorService> matched = this.processorTable.get(cmd.getCode());

//如果没有找到就使用默认的

final Pair<NettyRequestProcessor, ExecutorService> pair =

null == matched ? this.defaultRequestProcessor : matched;

//执行策略

final RemotingCommand response = pair.getObject1().processRequest(ctx, cmd);

//省略大部分代码…

}

}

如果有了新的请求消息类型,RocketMQ也无需修改业务代码,新增策略类并将其注册进来就好了。

八、代理模式

代理模式,为其他对象提供一种代理以控制对这个对象的访问。

在一些开源框架或中间件产品中,代理模式会非常常见。我们使用的时候越简便,框架在背后帮我们做的事就可能越复杂。这里面往往都体现着代理模式的应用,颇有移花接木的味道。

1、Dubbo

Dubbo作为一个RPC框架,其中有一个很重要的功能就是:

提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节。

这里我们关注两个重点:

  • 面向接口代理;

  • 屏蔽调用底层细节。

比如我们有一个库存服务,它提供一个扣减库存的接口。

public interface StorageDubboService {

int decreaseStorage(StorageDTO storage);

}

在别的服务里,需要扣减库存的时候,就会通过Dubbo引用这个接口,也比较简单。

@Reference

StorageDubboService storageDubboService;

我们使用起来很简单,可StorageDubboService只是一个普通的服务类,并不具备远程调用的能力。

Dubbo就是给这些服务类,创建了代理类。通过ReferenceBean来创建并返回一个代理对象。

public class ReferenceBean{

@Override

public Object getObject() {

return get();

}

public synchronized T get() {

if (ref == null) {

init();

}

return ref;

}

}

在我们使用的时候,实则调用的是代理对象,代理对象完成复杂的远程调用。比如连接注册中心、负载均衡、集群容错、连接服务器发送消息等功能。

2、MyBatis

还有一个典型的应用,就是我们经常在用的MyBatis。我们在使用的时候,一般只操作Mapper接口,然后MyBatis会找到对应的SQL语句来执行。

public interface UserMapper {

List getUserList();

}

如上代码,UserMapper也只是一个普通的接口,它是怎样最终执行到我们的SQL语句的呢?

答案也是代理。当MyBatis扫描到我们定义的Mapper接口时,会将其设置为MapperFactoryBean,并创建返回一个代理对象。

protected T newInstance(SqlSession sqlSession) {

final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);

return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);

}

代理对象去通过请求的方法名找到MappedStatement对象,调用执行器,解析SqlSource对象来生成SQL,执行并解析返回结果等。

以上案例具体的实现过程,在这里就不再深入细聊。有兴趣可能翻阅笔者其他文章~

九、装饰器模式

装饰器模式,在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

MyBatis里的缓存设计,就是装饰器模式的典型应用。

首先,我们知道,MyBatis执行器是MyBatis调度的核心,它负责SQL语句的生成和执行。

在创建SqlSession的时候,会创建这个执行器,默认的执行器是SimpleExecutor。

但是为了给执行器增加缓存的职责,就变成了在SimpleExecutor上一层添加了CachingExecutor。

在CachingExecutor中的实际操作还是委托给SimpleExecutor去执行,只是在执行前后增加了缓存的操作。

首先,我们来看看它的装饰过程。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

//默认的执行器

executorType = executorType == null ? defaultExecutorType : executorType;

executorType = executorType == null ? ExecutorType.SIMPLE : executorType;

Executor executor;

if (ExecutorType.BATCH == executorType) {

executor = new BatchExecutor(this, transaction);

} else if (ExecutorType.REUSE == executorType) {

executor = new ReuseExecutor(this, transaction);

} else {

executor = new SimpleExecutor(this, transaction);

}

//使用缓存执行器来装饰

if (cacheEnabled) {

executor = new CachingExecutor(executor);

}

executor = (Executor) interceptorChain.pluginAll(executor);

return executor;

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

面试题总结

其它面试题(springboot、mybatis、并发、java中高级面试总结等)

是委托给SimpleExecutor去执行,只是在执行前后增加了缓存的操作。

首先,我们来看看它的装饰过程。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

//默认的执行器

executorType = executorType == null ? defaultExecutorType : executorType;

executorType = executorType == null ? ExecutorType.SIMPLE : executorType;

Executor executor;

if (ExecutorType.BATCH == executorType) {

executor = new BatchExecutor(this, transaction);

} else if (ExecutorType.REUSE == executorType) {

executor = new ReuseExecutor(this, transaction);

} else {

executor = new SimpleExecutor(this, transaction);

}

//使用缓存执行器来装饰

if (cacheEnabled) {

executor = new CachingExecutor(executor);

}

executor = (Executor) interceptorChain.pluginAll(executor);

return executor;

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-xsy3zCRF-1711141423608)]
[外链图片转存中…(img-oV1d3ToZ-1711141423609)]
[外链图片转存中…(img-nofl3PwF-1711141423610)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-yRfHMgvq-1711141423610)]

面试题总结

其它面试题(springboot、mybatis、并发、java中高级面试总结等)

[外链图片转存中…(img-NgYJWcUx-1711141423610)]

[外链图片转存中…(img-ajYCP7tR-1711141423611)]

[外链图片转存中…(img-9ZPtcTW7-1711141423611)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值