程序员要提高,阅读优秀的书籍及源码是必不可少的过程。然而,有时源码过于复杂有大量的类配置等,甚至都不知道从哪里开始。这时候,找一个比较简单的类似版本开始阅读也是不错的选择。Tomcat的源码就比较复杂,所以先了解一些其类似产品MiniCat
一开始一直不了解Filter是使用函数回调实现的是什么意思?具体是如何实现的!!直到看完了MiniCat源码,才恍然大悟,原来使用函数回调实现是这个意思。希望阅读本博客的人也有所收获。
MiniCat的整体请求流程
1.创建 mini cat 服务器,并初始化服务器配置(TCP端口) 2.初始化 Servlet,Filter 并存储到对应容器 3.处理请求 3.1 接受 TCP 连接 3.2 http 构建,进入 builder方法 3.3 MiniCat 流程执行分发MinicatProcess.doService(this); 3.4 根据url从容器中取出url 3.5 创建过滤链,执行 Filter 过滤器前半部分 3.6 执行完所有的过滤器后,执行 Servlet 3.7 执行过滤器后半部分
从main方法开始,进入init(此为吾后来添加上去的main方法,原并无。执行此方法就可运行当前MiniCat容器,可以在浏览器访问:http://localhost:80/index.do?name=xx 访问到服务(TestServlet)
public static void main(String[] args) {
// 放入需要初始化的 Servlet,Filter
init(
TestServlet.class, MultipartServlet.class, MiniCatTestServlet.class,
BaseFilter.class, GeneralFilter.class
);
}
从CoreApp的init方法开始(附init方法源码,及详细注释)
public static void init(Class<?>... clazzs) {
long startTime = System.currentTimeMillis();
// 初始化 MiniCat 服务器
MiniCatService miniCatService = new BioService();
if (MiniCatConfig.MODEL == 2) {
miniCatService = new NioService();
}
System.out.println("引用模式>>" + miniCatService.getClass().getName());
try {
if (StringUtil.isNullOrEmpty(clazzs)) {
System.err.println("初始化Servlet为空");
return;
}
// 打开端口
miniCatService.openPort(MiniCatConfig.HTTP_PORT, MiniCatConfig.SESSION_TIMEOUT);
System.out.println("监听端口>>" + MiniCatConfig.HTTP_PORT);
for (Class<?> clazz : clazzs) {
if (!MiniCatHttpPart.class.isAssignableFrom(clazz)) {
continue;
}
// 通过注解获取 Servlet 对象
Servlet servletFlag = clazz.getAnnotation(Servlet.class);
if (servletFlag != null && !StringUtil.isNullOrEmpty(servletFlag.value())) {
// 初始化对象
HttpServlet servlet = (HttpServlet) clazz.getDeclaredConstructor().newInstance();
System.out.println("注册Servlet>>" + clazz.getName() + ">>" + servletFlag.value());
// Servlet 容器中,容器使用Map,是因为通过url快速匹配
ServletContainer.putServlet(servletFlag.value(), servlet);
}
// 通过注解获取 Filter 过滤器对象
Filter filterFlag=clazz.getAnnotation(Filter.class);
if (filterFlag != null && !StringUtil.isNullOrEmpty(filterFlag.value())) {
// 初始化过滤器
HttpFilter filter = (HttpFilter) clazz.getDeclaredConstructor().newInstance();
// 过滤的url条件
filter.setMapping(filterFlag.value());
System.out.println("注册Filter>>" + clazz.getName() + ">>" + filterFlag.value());
// 存储到过滤器容器中,过滤器容器使用List集合存储,是因为所有的过滤器都要执行
FilterContainer.pushFilter(filter);
}
}
System.out.println("MiniCat启动完成,耗时>>" + (System.currentTimeMillis() - startTime) + "ms");
// 处理请求
miniCatService.doService();
} catch (Exception e) {
// TODO: handle exception
}
}
具体流程步骤(以BioService为例) init方法到miniCatService.doService();之前都是在初始化容器配置,及Servlet,Filter
- 进入BioService的doService方法,TCP阻塞等待连接
- 连接上,执行doServlet方法(使用线程池)构建HttpBuilder
- 进入builder方法(new一个HttpRequest对象,HttpRequestHander数据设置,new 一个HttpRespose对象,
public void builder() {
try {
// 创建HttpRequest
buildRequest();
// request请求头设置
buildRequestHeader();
// 创建Respone
this.response = new HttpServletResponse();
// 执行分发
MinicatProcess.doService(this);
buildResponse();
} catch (Exception e) {
// 方法过程了,删除部分捕抓异常代码
e.printStackTrace();
try {
buildResponse(500, "error execution");
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
分发 MinicatProcess.doService(this);
public static void doService(HttpBuilder build) throws Exception {
// 根据url从Servlet容器中拿到对应的 servlet 对象
HttpServlet servlet = ServletContainer.getServlet(build.getRequest().getRequestURI());
// 创建过滤链
ApplicationFilterChain chain=new ApplicationFilterChain(servlet);
// 执行过滤器
chain.doFilter(build.getRequest(), build.getResponse());
}
进入过滤链方法chain.doFilter(),在方法的最后执行servlet.doService()到Controller中进行处理
public void doFilter(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 过滤链出来逻辑
if (pos < FilterContainer.FILTER_CONTAINER.size()) {
HttpFilter filter = FilterContainer.FILTER_CONTAINER.get(pos++);
if(!AntUtil.isAntMatch(request.getRequestURI(), filter.getMapping())){
doFilter(request, response);
return;
}
// 过滤器 doFilter 函数回调(有点类似递归,只是调用的方式不一样)
filter.doFilter(request, response, this);
return;
}
if (servlet == null) {
throw new PageNotFoundException("该页面未找到>>" + request.getRequestURI());
}
// 过滤链执行完成,执行 servlet 方法(即Controller中的方法)
servlet.doService(request, response);
}
即,此就是MiniCat Web服务器中一次请求的过程。到这里也就理解Filter的函数回调是什么意思。感谢开源作者写的MiniCat!!!