参考
​https://yq.aliyun.com/wenji/2...​​​


1.声明ZuulServlet


@Configuration
@EnableConfigurationProperties({ZuulProperties.class})
@Import(ServerPropertiesAutoConfiguration.class)
public class ZuulConfigurationCustom {
@Autowired
protected ZuulProperties zuulProperties;

@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),this.zuulProperties.getServletPattern());
// The whole point of exposing this servlet is to provide a route that doesn't
// buffer requests.
servlet.addInitParameter("buffer-requests", "false");
return servlet;
}
}


以上通过 ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),this.zuulProperties.getServletPattern()); 使用配置的URL mapping实例化一个Servlet,其中URL mapping在 application.properties 文件中配置,例如:


#拦截路径
zuul.servletPath=/openapi/


以上实现了 /openapi/ 被 ZuulServlet 处理的逻辑。

2.ZuulServlet处理逻辑

当请求path为 /openapi/ 的请求进入网关后,就会被 ZuulServlet 处理,以下为 ZuulServlet 处理流程:

ZuulServlet源码分析及ZuulFilter加载_初始化ZuulServlet源码分析及ZuulFilter加载_初始化_02


public class ZuulServlet extends HttpServlet {

private static final long serialVersionUID = -3374242278843351500L;
private ZuulRunner zuulRunner; //ZuulServlet逻辑真正的实现类


@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);

String bufferReqsStr = config.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;

zuulRunner = new ZuulRunner(bufferReqs);
}

@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);//将servletRequest和servletResponse存入RequestContext(RequestContext.getCurrentContext())

// Marks this request as having passed through the "Zuul engine", as opposed to servlets
// explicitly bound in web.xml, for which requests will not have the same data attached
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();

try {
preRoute();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
postRoute();
} catch (ZuulException e) {
error(e);
return;
}

} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}

/**
* executes "post" ZuulFilters
*
* @throws ZuulException
*/
void postRoute() throws ZuulException {
zuulRunner.postRoute();
}

/**
* executes "route" filters
*
* @throws ZuulException
*/
void route() throws ZuulException {
zuulRunner.route();
}

/**
* executes "pre" filters
*
* @throws ZuulException
*/
void preRoute() throws ZuulException {
zuulRunner.preRoute();
}

/**
* initializes request
*
* @param servletRequest
* @param servletResponse
*/
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
zuulRunner.init(servletRequest, servletResponse);
}

/**
* sets error context info and executes "error" filters
*
* @param e
*/
void error(ZuulException e) {
RequestContext.getCurrentContext().setThrowable(e);
zuulRunner.error();
}
}

View Code

以上 service() 方法中 init() 会在​​ZuulRunner​​中将 servletRequest 和 servletResponse 存入 RequestContext(RequestContext.getCurrentContext()) ,如下:


public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

RequestContext ctx = RequestContext.getCurrentContext();
if (bufferRequests) {
ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
} else {
ctx.setRequest(servletRequest);
}

ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}


 ZuulServlet 的 preRoute() 方法通过 zuulRunner 的 preRoute() 实现; zuulRunner 的 preRoute() 通过调用 FilterProcessor.getInstance().preRoute() 实现; FilterProcessor 的 preRoute() 执行自身 runFilters("pre") 方法执行 prefilter ; runFilters("pre") 方法会从 FilterLoader.getInstance().getFiltersByType(sType) 中过滤出pre filter循环执行 processZuulFilter(zuulFilter) ,以下为服务调用层次关系。


ZuulServlet.preRoute();
|_____ zuulRunner.preRoute();
|_____ FilterProcessor.getInstance().preRoute();
|_____ runFilters("pre");
|_____ List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
| |_____ Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
|_____ processZuulFilter(zuulFilter);
|_____ ZuulFilterResult result = filter.runFilter();//执行filter,其中包括filter是否需要执行(shouldFilter())
//filterRegistry为单例模式,通过FilterRegistry.instance()获取实例,getAllFilters()方法获取类型为ConcurrentHashMap<String, ZuulFilter>的filters。


3.ZuulFilter 加载

针对上述代码中的 FilterRegistry 中的​​filters​​需要在项目启动时,显示声明进行初始化:


 @Configuration
protected static class ZuulFilterConfiguration {

//按类型将所有ZuulFilter注入到map中
@Autowired
private Map<String, ZuulFilter> filters;

@Bean
public ZuulFilterInitializer zuulFilterInitializer() {
return new ZuulFilterInitializer(this.filters); //将项目中的ZuulFilter存入FilterRegistry.instance()的filters中
}

}


其中 ZuulFilterInitializer 为实现了 ServletContextListener 类,会根据项目声明在项目启动时进行对 FilterRegistry.instance() 的 filters 进行初始化。


@CommonsLog
public class ZuulFilterInitializer implements ServletContextListener {

private Map<String, ZuulFilter> filters;

public ZuulFilterInitializer(Map<String, ZuulFilter> filters) {
this.filters = filters;
}

@Override
public void contextInitialized(ServletContextEvent sce) {

log.info("Starting filter initializer context listener");

// FIXME: mocks monitoring infrastructure as we don't need it for this simple app
MonitoringHelper.initMocks();

FilterRegistry registry = FilterRegistry.instance();

for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
registry.put(entry.getKey(), entry.getValue());
}
}
..........后继代码省略
}


4.其他

 ZuulServlet 的 route() 、 postRoute() 、 error(e) 方法执行逻辑与 preRoute() 相同。