源码中的设计模式之拦截器设计模式
文章目录
前言
这个拦截器的源码是从jersey框架下扒出来的,拦截器在这个框架中是对输入输出流信息的拦截,过滤器则是对请求与响应的过滤。
一、ReaderInterceptorExecutor源码
删除不必要的代码留下核心代码~~
读取拦截执行器下面 成员变量Iterator interceptors是核心,在创捷执行器的时候被初始化,这个集合interceptors是在服务启动时且相关配置OK的条件下通过一个Provider对象配置好的。process()方法就是拦截器执行器的执行方法来自接口ReaderInterceptorContext,通过遍历集合 interceptors获取下一个拦截器,拦截器主要执行的业务逻辑就在aroundReadFrom,这个方法将自己的引用当参数传入了aroundReadFrom()方法中(这里经典,嘿,get到了)。拦截器的链的最末端拦截器是TerminalReaderInterceptor对象。
/**
* Represents reader interceptor chain executor for both client and server side.
* It constructs wrapped interceptor chain and invokes it. At the end of the chain
* a {@link MessageBodyReader message body reader} execution interceptor is inserted,
* which finally reads an entity from the output stream provided by the chain.
*
* @author Miroslav Fuksa
* @author Jakub Podlesak (jakub.podlesak at oracle.com)
*/
public final class ReaderInterceptorExecutor extends InterceptorExecutor<ReaderInterceptor>
implements ReaderInterceptorContext, ServiceLocatorSupplier {
private static final Logger LOGGER = Logger.getLogger(ReaderInterceptorExecutor.class.getName());
private final MultivaluedMap<String, String> headers;
private final Iterator<ReaderInterceptor> interceptors;
private final MessageBodyWorkers workers;
private final boolean translateNce;
private final ServiceLocator serviceLocator;
private InputStream inputStream;
private int processedCount;
public ReaderInterceptorExecutor(final Class<?> rawType, final Type type,
final Annotation[] annotations,
final MediaType mediaType,
final MultivaluedMap<String, String> headers,
final PropertiesDelegate propertiesDelegate,
final InputStream inputStream,
final MessageBodyWorkers workers,
final Iterable<ReaderInterceptor> readerInterceptors,
final boolean translateNce,
final ServiceLocator serviceLocator) {
super(rawType, type, annotations, mediaType, propertiesDelegate);
this.headers = headers;
this.inputStream = inputStream;
this.workers = workers;
this.translateNce = translateNce;
this.serviceLocator = serviceLocator;
final List<ReaderInterceptor> effectiveInterceptors = Lists.newArrayList(readerInterceptors);
effectiveInterceptors.add(new TerminalReaderInterceptor());
this.interceptors = effectiveInterceptors.iterator();
this.processedCount = 0;
}
/**
* Starts the interceptor chain execution.
*
* @return an entity read from the stream.
*/
@Override
@SuppressWarnings("unchecked")
public Object proceed() throws IOException {
if (!interceptors.hasNext()) {
throw new ProcessingException(LocalizationMessages.ERROR_INTERCEPTOR_READER_PROCEED());
}
final ReaderInterceptor interceptor = interceptors.next();
traceBefore(interceptor, MsgTraceEvent.RI_BEFORE);
try {
return interceptor.aroundReadFrom(this);
} finally {
processedCount++;
traceAfter(interceptor, MsgTraceEvent.RI_AFTER);
}
}
/**
* Terminal reader interceptor which choose the appropriate {@link MessageBodyReader}
* and reads the entity from the input stream. The order of actions is the following: <br>
* 1. choose the appropriate {@link MessageBodyReader} <br>
* 3. reads the entity from the output stream <br>
*/
private class TerminalReaderInterceptor implements ReaderInterceptor {
@Override
@SuppressWarnings("unchecked")
public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
processedCount--; //this is not regular interceptor -> count down
traceBefore(null, MsgTraceEvent.RI_BEFORE);
try {
final TracingLogger tracingLogger = getTracingLogger();
if (tracingLogger.isLogEnabled(MsgTraceEvent.MBR_FIND)) {
tracingLogger.log(MsgTraceEvent.MBR_FIND,
context.getType().getName(),
(context.getGenericType() instanceof Class
? ((Class) context.getGenericType()).getName() : context.getGenericType()),
String.valueOf(context.getMediaType()), java.util.Arrays.toString(context.getAnnotations()));
}
final MessageBodyReader bodyReader = workers.getMessageBodyReader(
context.getType(),
context.getGenericType(),
context.getAnnotations(),
context.getMediaType(),
ReaderInterceptorExecutor.this);
final EntityInputStream input = new EntityInputStream(context.getInputStream());
if (bodyReader == null) {
if (input.isEmpty() && !context.getHeaders().containsKey(HttpHeaders.CONTENT_TYPE)) {
return null;
} else {
LOGGER.log(Level.FINE, LocalizationMessages.ERROR_NOTFOUND_MESSAGEBODYREADER(context.getMediaType(),
context.getType(), context.getGenericType()));
throw new MessageBodyProviderNotFoundException(LocalizationMessages.ERROR_NOTFOUND_MESSAGEBODYREADER(
context.getMediaType(), context.getType(), context.getGenericType()));
}
}
Object entity = invokeReadFrom(context, bodyReader, input);
if (bodyReader instanceof CompletableReader) {
entity = ((CompletableReader) bodyReader).complete(entity);
}
return entity;
} finally {
clearLastTracedInterceptor();
traceAfter(null, MsgTraceEvent.RI_AFTER);
}
}
}
}
二.输入端拦截器接口ReaderInterceptor
实现该拦截器接口的类,必须需要显性调用 context.proceed()才能让executor执行当前拦截器链的下一个拦截器,否则就默认当前输入流就被拦截不许继续向下执行。
public interface ReaderInterceptor {
/**
* Interceptor method wrapping calls to {@link MessageBodyReader#readFrom} method.
*
* The parameters of the wrapped method called are available from {@code context}.
* Implementations of this method SHOULD explicitly call {@link ReaderInterceptorContext#proceed}
* to invoke the next interceptor in the chain, and ultimately the wrapped
* {@link MessageBodyReader#readFrom} method.
*
* @param context invocation context.
* @return result of next interceptor invoked or the wrapped method if last interceptor in chain.
* @throws java.io.IOException if an IO error arises or is thrown by the wrapped
* {@code MessageBodyReader.readFrom} method.
* @throws javax.ws.rs.WebApplicationException
* thrown by the wrapped {@code MessageBodyReader.readFrom} method.
*/
public Object aroundReadFrom(ReaderInterceptorContext context)
throws java.io.IOException, javax.ws.rs.WebApplicationException;
}
三.框架自己实现的一些拦截器接口的类ContentEncoder
@Priority(Priorities.ENTITY_CODER)
@Contract
public abstract class ContentEncoder implements ReaderInterceptor, WriterInterceptor {
private final Set<String> supportedEncodings;
protected ContentEncoder(String... supportedEncodings) {
if (supportedEncodings.length == 0) {
throw new IllegalArgumentException();
}
this.supportedEncodings = Collections.unmodifiableSet(Sets.newHashSet(Arrays.asList(supportedEncodings)));
}
public final Set<String> getSupportedEncodings() {
return supportedEncodings;
}
public abstract InputStream decode(String contentEncoding, InputStream encodedStream) throws IOException;
public abstract OutputStream encode(String contentEncoding, OutputStream entityStream) throws IOException;
@Override
public final Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
String contentEncoding = context.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
if (contentEncoding != null && getSupportedEncodings().contains(contentEncoding)) {
context.setInputStream(decode(contentEncoding, context.getInputStream()));
}
return context.proceed();
}
@Override
public final void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
// must remove Content-Length header since the encoded message will have a different length
String contentEncoding = (String) context.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
if (contentEncoding != null && getSupportedEncodings().contains(contentEncoding)) {
context.setOutputStream(encode(contentEncoding, context.getOutputStream()));
}
context.proceed();
}
}
四. MessageBodyFactory
这家伙就是调用拦截器的家伙,其实它也是给InboundMessageContext,OutboundMessageContext干活。分别处理输入输出的上下文信息,
再往前找就是ContainerRequest(Jersey 容器的 请求上下文专门处理来自客户端的每一个请求),再往前就到了ServerRuntime(一个服务端的请求运行时)。
public class MessageBodyFactory implements MessageBodyWorkers {
private static final Logger LOGGER = Logger.getLogger(MessageBodyFactory.class.getName());
@Override
public Object readFrom(final Class<?> rawType,
final Type type,
final Annotation[] annotations,
final MediaType mediaType,
final MultivaluedMap<String, String> httpHeaders,
final PropertiesDelegate propertiesDelegate,
final InputStream entityStream,
final Iterable<ReaderInterceptor> readerInterceptors,
final boolean translateNce) throws WebApplicationException, IOException {
final ReaderInterceptorExecutor executor = new ReaderInterceptorExecutor(
rawType,
type,
annotations,
mediaType,
httpHeaders,
propertiesDelegate,
entityStream,
this,
readerInterceptors,
translateNce,
serviceLocator);
final TracingLogger tracingLogger = TracingLogger.getInstance(propertiesDelegate);
final long timestamp = tracingLogger.timestamp(MsgTraceEvent.RI_SUMMARY);
try {
final Object instance = executor.proceed();
if (!(instance instanceof Closeable) && !(instance instanceof Source)) {
final InputStream stream = executor.getInputStream();
if (stream != entityStream && stream != null) {
// We only close stream if it differs from the received entity stream,
// otherwise we let the caller close the stream.
ReaderWriter.safelyClose(stream);
}
}
return instance;
} finally {
tracingLogger.logDuration(MsgTraceEvent.RI_SUMMARY, timestamp, executor.getProcessedCount());
}
}
@Override
public OutputStream writeTo(final Object t,
final Class<?> rawType,
final Type type,
final Annotation[] annotations,
final MediaType mediaType,
final MultivaluedMap<String, Object> httpHeaders,
final PropertiesDelegate propertiesDelegate,
final OutputStream entityStream,
final Iterable<WriterInterceptor> writerInterceptors)
throws IOException, WebApplicationException {
final WriterInterceptorExecutor executor = new WriterInterceptorExecutor(
t,
rawType,
type,
annotations,
mediaType,
httpHeaders,
propertiesDelegate,
entityStream,
this,
writerInterceptors,
serviceLocator);
final TracingLogger tracingLogger = TracingLogger.getInstance(propertiesDelegate);
final long timestamp = tracingLogger.timestamp(MsgTraceEvent.WI_SUMMARY);
try {
executor.proceed();
} finally {
tracingLogger.logDuration(MsgTraceEvent.WI_SUMMARY, timestamp, executor.getProcessedCount());
}
return executor.getOutputStream();
}
}
水完才叫学到,嘿嘿。