责任链模式-实现和Servlet、Spring Aop、Zuul中的责任链模式分析
目录一、责任链实现二、Servlet、Spring Aop、Zuul中的责任链模式分析1、Servlet中的Filter2、Zuul中的责任链模式分析1)、FilterFileManager2)、ZuulServlet3)、Spring Aop责任链模式责任链模式定义:使多个对象都有处理请求的机会,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象串成一...
设计模式-总览https://mp.csdn.net/mp_blog/creation/editor/122202507目录
二、Servlet、Spring Aop、Zuul中的责任链模式分析
责任链模式定义:使多个对象都有处理请求的机会,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象串成一条链,并沿着这条链一直传递该请求,直到有对象处理它为止。有两个角色:
抽象任务角色:1)、当前执行者、下一个任务者的链接(可以调用下一个任务方法)
具体任务角色:一大堆抽象任务角色的实现类
个人理解:类似filter等使用责任链模式,或者一大堆的if else if... 等,有一大堆同一类(或者同级的事)需要处理,也就是上面说的具体任务角色。使用责任链模式,每个请求(或者调用)的时候肯定不是把一大堆都全部执行,那么就有两种方式。第一、把所有任务角色组成责任链模式,每个请求进来全部去走一遍,要处理就处理,不处理就调用下一个进行处理。 第二、每个请求把自己会执行的具体任务觉得挑选起来组成责任链,然后只管调用,每个处理完之后就调用下一个。
当然责任链有几个变种的方式,一个是抽象每一个具体的任务角色,在具体的实现类中再去实现,还有就是在抽象任务角色中维护一个列表(数组或者链表),调用的时候将当前对象this传递进行。
一、责任链实现
1)、先定义责任链抽象
public abstract class ChainHandler {
/**
* 下一个处理者
*/
private ChainHandler nextHandler;
public ChainHandler() {
}
public ChainHandler(ChainHandler nextHandler) {
this.nextHandler = nextHandler;
}
/**
* 当前处理任务
*/
protected abstract void handle();
public ChainHandler getNextHandler() {
return nextHandler;
}
}
2)、责任链抽象实现(一系列)
public class AChainHandler extends ChainHandler {
public AChainHandler() {
}
public AChainHandler(ChainHandler nextHandler) {
super(nextHandler);
}
@Override
protected void handle() {
System.out.println("我执行了方法A!");
if (getNextHandler() != null) {
System.out.println("----- 我呼叫了下一个任务!");
getNextHandler().handle();
}
}
}
public class BChainHandler extends ChainHandler {
public BChainHandler() {
}
public BChainHandler(ChainHandler nextHandler) {
super(nextHandler);
}
@Override
protected void handle() {
System.out.println("我执行了方法B!");
if (getNextHandler() != null) {
System.out.println("----- 我呼叫了下一个任务!");
getNextHandler().handle();
}
}
}
3)、测试责任链实现
public class ChainHandlerTest {
public static void main(String[] args) {
// 任务一放入链
AChainHandler a = new AChainHandler();
// 任务二放入链
BChainHandler b = new BChainHandler(a);
// 再放一个任务一到链中
AChainHandler a1 = new AChainHandler(b);
// 执行任务
a1.handle();
}
}
执行结果:
我执行了方法A!
----- 我呼叫了下一个任务!
我执行了方法B!
----- 我呼叫了下一个任务!
我执行了方法A!
二、Servlet、Spring Aop、Zuul中的责任链模式分析
责任链模式的应用场景,还是直接分析别人是怎么写的吧。
- servlet中的Filter
- zuul中的Filter
- dubbo中的Filter
- mybatis中的Plugin
1、Servlet中的Filter
定义接口为:FilterChain,里面维护的是Filter,只定义了一个接口。刚开始在想为什么不定义一个抽象就可以了。后面看了一下FIlter本身也是一个非常庞大的体系,并且抽象了三个接口需要自己的子类实现。
public interface Filter {
public default void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public default void destroy() {}
}
public interface FilterChain {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
FilterChain的只有ApplicationFilterChain是在Servlet规范中实现的,其他的都是Spring实现的。所以就分析ApplicationFilterChain和测试框架的MockFilterChain。
1)、ApplicationFilterChain
其中维护了一个调用链,并不是Filter数组。
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
有一个添加ApplicationFilterConfig的方法:
void addFilter(ApplicationFilterConfig filterConfig) {
// Prevent the same filter being added multiple times
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;
if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}
其实Filter是ApplicationFilterConfig内部的一个字段,并且提供了getFilter方法来或者当前的Filter。其构造方法如下:
ApplicationFilterConfig(Context context, FilterDef filterDef)
throws ClassCastException, ReflectiveOperationException, ServletException,
NamingException, IllegalArgumentException, SecurityException {
super();
this.context = context;
this.filterDef = filterDef;
// Allocate a new filter instance if necessary
if (filterDef.getFilter() == null) {
getFilter();
} else {
this.filter = filterDef.getFilter();
context.getInstanceManager().newInstance(filter);
initFilter();
}
}
那么ApplicationFilterConfig与Filter是一对一的关系,即ApplicationFilterChain也相当于维护了一个Filter数组。
再看看ApplicationFilterChain的doFilter方法:
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
internalDoFilter(request,response);
}
}
执行方法在internalDoFilter(request,response);中:
重要的是pos++(pos为当前的位置),即获取到了下一个Filter,后面执行了下一个Filter的doFilter方法。
2、Zuul中的责任链模式分析
在分析之前需要知道,Spring Cloud Zuul中Filter的大致情况,可以参见:Spring Cloud Zuul的Api网关实现、Spring Cloud Zuul详细说明。
其中用到了大量的单利模式,但是先不考虑这个。两个关键的类是 ZuulServlet 和 FilterFileManager,太多的细节不用进行考虑,因为这里是研究责任链模式。
1)、FilterFileManager
没有找到FilterFileManager的init方法在哪里初始化的,传入了轮训间隔时间和一个文件目录,进行初始化:
public static void init(int pollingIntervalSeconds, String... directories)
throws Exception, IllegalAccessException, InstantiationException {
if (INSTANCE == null) {
INSTANCE = new FilterFileManager();
}
INSTANCE.aDirectories = directories;
INSTANCE.pollingIntervalSeconds = pollingIntervalSeconds;
INSTANCE.manageFiles();
INSTANCE.startPoller();
}
但是在manageFiles方法中对目录下的文件进行了获取,并且将其注册到FilterLoader(同样是单利模式)中,后续会使用。
void manageFiles() throws Exception, IllegalAccessException, InstantiationException {
List<File> aFiles = this.getFiles();
this.processGroovyFiles(aFiles);
}
void processGroovyFiles(List<File> aFiles) throws Exception,
InstantiationException, IllegalAccessException {
Iterator var2 = aFiles.iterator();
while(var2.hasNext()) {
File file = (File)var2.next();
FilterLoader.getInstance().putFilter(file);
}
}
FilterLoader继承自ConcurrentHashMap的结构如下:
public class FilterLoader {
static final FilterLoader INSTANCE = new FilterLoader();
private static final Logger LOG = LoggerFactory.getLogger(FilterLoader.class);
private final ConcurrentHashMap<String, Long> filterClassLastModified = new ConcurrentHashMap();
private final ConcurrentHashMap<String, String> filterClassCode = new ConcurrentHashMap();
private final ConcurrentHashMap<String, String> filterCheck = new ConcurrentHashMap();
private final ConcurrentHashMap<String, List<ZuulFilter>> hashFiltersByType = new ConcurrentHashMap();
private FilterRegistry filterRegistry = FilterRegistry.instance();
static DynamicCodeCompiler COMPILER;
static FilterFactory FILTER_FACTORY = new DefaultFilterFactory();
public FilterLoader() {
}
public void setCompiler(DynamicCodeCompiler compiler) {
COMPILER = compiler;
}
public void setFilterRegistry(FilterRegistry r) {
this.filterRegistry = r;
}
public void setFilterFactory(FilterFactory factory) {
FILTER_FACTORY = factory;
}
public static FilterLoader getInstance() {
return INSTANCE;
}
public ZuulFilter getFilter(String sCode, String sName) throws Exception {
if (this.filterCheck.get(sName) == null) {
this.filterCheck.putIfAbsent(sName, sName);
if (!sCode.equals(this.filterClassCode.get(sName))) {
LOG.info("reloading code " + sName);
this.filterRegistry.remove(sName);
}
}
ZuulFilter filter = this.filterRegistry.get(sName);
if (filter == null) {
Class clazz = COMPILER.compile(sCode, sName);
if (!Modifier.isAbstract(clazz.getModifiers())) {
filter = FILTER_FACTORY.newInstance(clazz);
}
}
return filter;
}
public int filterInstanceMapSize() {
return this.filterRegistry.size();
}
public boolean putFilter(File file) throws Exception {
String sName = file.getAbsolutePath() + file.getName();
if (this.filterClassLastModified.get(sName) != null && file.lastModified() != (Long)this.filterClassLastModified.get(sName)) {
LOG.debug("reloading filter " + sName);
this.filterRegistry.remove(sName);
}
ZuulFilter filter = this.filterRegistry.get(sName);
if (filter == null) {
Class clazz = COMPILER.compile(file);
if (!Modifier.isAbstract(clazz.getModifiers())) {
filter = FILTER_FACTORY.newInstance(clazz);
List<ZuulFilter> list = (List)this.hashFiltersByType.get(filter.filterType());
if (list != null) {
this.hashFiltersByType.remove(filter.filterType());
}
this.filterRegistry.put(file.getAbsolutePath() + file.getName(), filter);
this.filterClassLastModified.put(sName, file.lastModified());
return true;
}
}
return false;
}
public List<ZuulFilter> getFiltersByType(String filterType) {
List<ZuulFilter> list = (List)this.hashFiltersByType.get(filterType);
if (list != null) {
return list;
} else {
List<ZuulFilter> list = new ArrayList();
Collection<ZuulFilter> filters = this.filterRegistry.getAllFilters();
Iterator iterator = filters.iterator();
while(iterator.hasNext()) {
ZuulFilter filter = (ZuulFilter)iterator.next();
if (filter.filterType().equals(filterType)) {
list.add(filter);
}
}
Collections.sort(list);
this.hashFiltersByType.putIfAbsent(filterType, list);
return list;
}
}
}
putFileter就是根据一个名词,从private FilterRegistry filterRegistry = FilterRegistry.instance();中获取出来就是一个Filter,或者使用反射进行实例化。
2)、ZuulServlet
很显然是一个Servlet,重要的两个方法就是init和service方法,
- 1、初始化的时候会调用init方法
public void init(ServletConfig config) throws ServletException {
super.init(config);
String bufferReqsStr = config.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true");
this.zuulRunner = new ZuulRunner(bufferReqs);
}
初始化了内部对象,ZuulRunner,其结构比较复杂后续再看。
- 2、容器请求进来会调用service方法
public void service(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException {
try {
this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
this.preRoute();
} catch (ZuulException var13) {
this.error(var13);
this.postRoute();
return;
}
try {
this.route();
} catch (ZuulException var12) {
this.error(var12);
this.postRoute();
return;
}
try {
this.postRoute();
} catch (ZuulException var11) {
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" +
var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
void postRoute() throws ZuulException {
this.zuulRunner.postRoute();
}
void route() throws ZuulException {
this.zuulRunner.route();
}
void preRoute() throws ZuulException {
this.zuulRunner.preRoute();
}
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
this.zuulRunner.init(servletRequest, servletResponse);
}
void error(ZuulException e) {
RequestContext.getCurrentContext().setThrowable(e);
this.zuulRunner.error();
}
执行顺序:preRoute方法、route方法、postRoute方法,但是只要异常都会执行error方法。很有意思的是,只要任何一步有异常error路由就会执行;不管怎么样postRoute都会执行。
每一个步骤执行的时候过程都大致一样,就分析preRoute类型。
this.zuulRunner.preRoute();
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
public void preRoute() throws ZuulException {
try {
this.runFilters("pre");
} catch (ZuulException var2) {
throw var2;
} catch (Throwable var3) {
throw new ZuulException(var3, 500,
"UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());
}
}
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for(int i = 0; i < list.size(); ++i) {
ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
Object result = this.processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= (Boolean)result;
}
}
}
return bResult;
}
最后还是根据类型,在之前初始化的FilterLoader中进行获取的,获取到了之后就for循环全部执行一遍,调用processZuulFilter方法。个人理解不完全是责任链吧,要是这样的化,我之前写过很多动态配置到一个lIst中,执行的时候从List中拿到任务进行执行,都算责任链了?但是网上很多都说这就是。
3)、Spring Aop责任链模式
Spring Aop实现时需要先解析增强(Advice),最好封装到ProxyFactory中。当从Spring容器中getBean的时候获取到的其实是代理对象,再代用切入点方法的时候,则会调用JdkDynamicAopProxy的invoke方法,或者CglibAopProxy的intercept方法。更多细节可以参考:SpringAop源码-EnableAspectJAutoProxy实现原理(调用)- invoke或者intercept(代理方法执行)
其内部实现方式都一样
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
先从ProxyFactory中获取到 获取到一个List<Object> chain其实是InterceptorAndDynamicMethodMatcher类型。
再调用ReflectiveMethodInvocation的proceed方法:
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
执行当前interceptor,传入this。知道责任链执行完成。
更多推荐
所有评论(0)