SSM

使用传统的xml形式的SSM实现一遍,描述大致流程

代码地址:

https://gitee.com/chenscript/mybatis_learning.git

前言、tomcat是如何实现servlet的?

将SSM项目用tomcat启动,你会看到日志的第一行是

D:\tomcat8.5.50\apache-tomcat-8.5.50\bin\catalina.bat run
[2019-12-28 10:20:09,678] Artifact SSM:war exploded: Waiting for server connection to start artifact deployment…
Using CATALINA_BASE: “C:\Users\Administrator.IntelliJIdea2017.3\system\tomcat\Unnamed_parent”
Using CATALINA_HOME: “D:\tomcat8.5.50\apache-tomcat-8.5.50”
Using CATALINA_TMPDIR: “D:\tomcat8.5.50\apache-tomcat-8.5.50\temp”
Using JRE_HOME: “D:\Java\jdk1.8.0_161”
Using CLASSPATH: “D:\tomcat8.5.50\apache-tomcat-8.5.50\bin\bootstrap.jar;D:\tomcat8.5.50\apache-tomcat-8.5.50\bin\tomcat-juli.jar”

这里就是我们的入口:catalina.bat 。 在代码里,你会发现实质启动的是tomcat的一个类:Bootstrap。
tomcat Bootstrap源码:
SSM_第1张图片

如何调试tomcat源码? --> 直接在Bootstrap中右键运行main()就好了。(记得ant-build一下)
参考链接:

https://blog.csdn.net/yekong1225/article/details/81000446

有些单元测试会报错,但是可以注释掉。

tomcat是最腰间盘突出的设计模式就是状态设计模式(参考start()方法),采用netty方式实现获取请求,发送请求的。

直奔主题吧。tomcat如何实现servlet的?
先看tomcat容器关系图:
SSM_第2张图片
来自极客时间的图。

可以看到Servlet是在最底层的,所以跟着代码走,直接找到Wrapper的代码。假设代码使用的是JSP,那么这个Wrapper就是JspServletWrapper。(因为在tomcat代码有这包,方便看代码)
如果认真起来找的话,还追溯到这些类上,跟着上面的图一个个套进去找吧。
SSM_第3张图片
可以参考调用栈这里一步步调试。
SSM_第4张图片
在JspServlet的init()方法中,核心方法是

wrapper.service(request, response, precompile);

进行编译、加载文件、处理一定数量的加载的jsp、处理单线程请求。

当然,核心 是这个方法 wrapper.service() 再调用了 servlet.service(request, response); 这样就最终还是回到了原生httpservlet上进行doget,dopost请求了。
JspServletWrapper.service() 逻辑为例:

(1) Compile 编译文件
(2) (Re)load servlet class file 加载或重加载servlet类
(3) Handle limitation of number of loaded Jsps 处理一定数量的加载完的jsp页面
(4) Service request 处理页面请求

以上是关于tomcat代码中处理servlet的一些逻辑。详细的源码还需要另起专题研究,这里就引导一下吧。
下面开始SSM部分的分析。

一、web.xml 元素解析?

web.xml的解析在tomcat源码中解析

org.apache.catalina.startup.ContextConfig.configureContext(WebXml webxml)

  • :上下文参数。 web.xml的context-param有一些默认的参数,可以配置就使用.
  • SSM_第5张图片
    -\ :监听器。关于 org.springframework.web.context.ContextLoaderListener 是用来监听容器关闭之后进行关闭spring的

The created application context will be registered into the ServletContext under
the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
and the Spring application context will be closed when the {@link #contextDestroyed} lifecycle method is invoked on this listener

  • 是配套使用的,用来定义使用到的servlet实现类。
    tomcat是在哪个地方使用这个标签的内容? 组建webXml对象的时候,
public void addServlet(ServletDef servletDef) {
        servlets.put(servletDef.getServletName(), servletDef);
        if (overridable) {
            servletDef.setOverridable(overridable);
        }
    }

SSM_第6张图片
这是在通过WebXmlParser的digester.parse(source);解析后,调用的方法。 以后使用就直接调用getServlet()就是了。
然后就通过像JspServletWrapper.service()的操作进行解析。
以上就是使用的一些标签的解析。

二、Tomcat启动后,容器是如何搭建的?(应用程序代码上的分析)

这节就看SSM源码吧。
久闻DispatcherServlet()的九大组件,这个类的工作是要依赖这九大组件进行的,所以,所有工作当然是从初始化开始了。
在初始化的地方打上断点,可以看到调用链如下:
SSM_第7张图片
从下往上看,主要有几步:

Servlet的loadOnStartup,获取即时加载的servlet.
init()—GenericServlet
initWebApplicationContext ----- FrameworkServlet
createWebApplicationContext ----FrameworkServlet
configureAndRefreshWebApplicationContext ----FrameworkServlet
publishEvent
OnApplicationEvent
onRefresh()

解析一下: 就是要创建一个上下文servlet,但要创建这个上下文之前,需要与spring容器结合,所以就要先创建springWeb容器,也就是createWebApplicationContext,也就是调用了springIOC的refresh()。当完成spring容器之后,再提出事件发布,让FrameworkServlet中监听ApplicationEvent的观察者进行工作。如图。
SSM_第8张图片
这里就开始了DispatcherServlet的旅程。 (绕了一大圈啊,发现这文章越写越长了。。)

SSM_第9张图片
onRefresh()下的方法就是九大组件的初始化策略。
九大组件有哪些呢?

MultipartResolver:文件、多媒体等解析器
LocaleResolver: 其主要作用在于根据不同的用户区域展示不同的视图
ThemeResolver:Spring MVC的主题就是一些静态资源的集合,即包括样式及图片,用来控制应用的视觉风格。
HandlerMappings:就是 请求地址与请求方法映射了
HandlerAdapters:Handler的适配器,用来处理请求与Handler故事。
HandlerExceptionResolvers: 处理异常的处理器
RequestToViewNameTransLator: 请求与视图名的绑定
ViewResolver: 视图解析器
FlashMapManager:session管理器。 使用模板方法,实现一个SessionFlashMapManager管理request的session

这个初始化过程就是从spring容器中获取一个名叫以上九个组件的实例,另外就是要把DispatcherServlet的默认策略加入到各个组件中。

三、发起一个请求到响应返回数据,发生了什么?(只作Java源码分析)

发送一个请求,如果用原生的servlet进行使用的话,那就是会直接走doGet或者doPost方法。所以,我们现在的目标也这么搞。在SSM项目中的Controller请求第一行打断点,直接跑调试。看方法调用栈!

SSM_第10张图片SSM_第11张图片
截图很长! 不管,就这么看。

看上面几步,

一、开线程,走NioEndpoint的doRun方法。(再回想endpoint作用是啥?是连接器中的一部分。献上极客时间的图。在文末!)

二、通过Processor处理http的内容,分门别类

三、将请求信息发给Adapter处理,转成与引擎适配的Request,Response.也就是,碰见是HTTP的话,就会转成HttpServletRequest,HttpServletResponse

四、调用服务器访问日志。

五、访问相对应的host,进行权限验证信息

六、寻找适配的服务器容器,将请求转发入该容器中,匹配相对应的ServletWrapper,为什么要有Wrapper ? the destination is to process the requests directed to a particular servlet.

七、请求进入Servlet前的过滤器的处理

八、开始调用HttpServlet.service()

具体分析一下吧~
DispatcherServlet的继承图如下
SSM_第12张图片
所以调用方法的时候会子类优先嘛,去找DispatcherServlet的service()方法,但发现只有FrameworkServlet实现了这个方法。再看,我们使用的是doGet()方法,还是要走else分支,跑到父类HttpServletBean上找,无果,再往上找到HttpServlet,找到了。
SSM_第13张图片
doGet(),doPost()截图不到。。。
SSM_第14张图片
点开doGet(),找到子类实现的doGet()
SSM_第15张图片
在processRequest中算是做了三件事情吧。
1.解决语言区域问题
2.处理请求和响应
SSM_第16张图片
3.推送请求处理完毕的事件
SSM_第17张图片
我们重点看请求处理的部分的。

doService

处理分两部分:
1.将全局属性(webApplicationContext,localeResolver,themeResolver,themeSource,flashManager)注入到request中
2.分发request,也就是doDispatch();
SSM_第18张图片

doDispatch

1.检查是否有文件|图片等
2.mappedHandler = getHandler(processedRequest); 获取mappingHandler
3.根据获得的handler获取适配器 HandlerAdapter
4.处理拦截器请求的preHandler
5.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());使用handlerAdapter让handler处理请求request,返回一个ModelAndView对象
6.查找并使用默认的视图名字
7.处理拦截器postHandler方法
8.processDispatchResult 处理请求后的结果,包括异常 。

  • 如果有异常就处理异常并返回视图在这里插入图片描述
  • 如果没有异常,但是返回了视图需要渲染
    SSM_第19张图片
    渲染视图有两步:1.使用相对应的语言渲染 2.使用对应的模板方法渲染视图view.render(mv.getModelInternal(), request, response);

到此。后面还有一步就是发送通知事件,我这个请求可以结束了,请大家知悉。然后退场。。

附加:
tomcat的整体Server结构可以根据server.xml的标签来记忆。
SSM_第20张图片
SSM_第21张图片

你可能感兴趣的:(spring)