JavaWeb三⼤组件之⼀——Servlet源码解析(剖析底层代码,需要耐⼼看完)什么是Servlet?
Servlet的全称是Server Applet。通俗来说,它是⼀个请求处理器,它可以接受来⾃前端或者其他服务器端的请求。作为Java⼯程师,我们通常是将其⽤在后端处理前端请求,执⾏相应业务操作。
⽽在⽬前市场的常⽤的主流框架,如Spring、SpringMvc、SpringBoot等等,这些Java应⽤开发框架中都不见Servlet的踪影,难道Servlet已经没落了吗?当然不是,这些所谓的主流框架,在底层⽤的其实还是Servlet,只是这些框架已经把Servlet封装隐藏起来了。
因此,如果要了解JavaWeb相关框架,那么Servlet就是必不可少的⼀关。
初步了解Servlet接⼝
public interface Servlet {
/**
* 初始化⽅法
* 实例化servlet之后,该⽅法仅调⽤⼀次
* init⽅法必须执⾏完成,servlet才能接收任何请求
*/
public void init(ServletConfig config)throws ServletException;
/**
* 获取Servlet的初始化和启动参数对象
*/
public ServletConfig getServletConfig();
/**
servlet和tomcat的关系
* 处理request请求
*/
public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
/
**
* 返回有关servlet的信息,例如作者,版本和版权
*/
public String getServletInfo();
/**
* 销毁⽅法
*/
public void destroy();
}
Servlet相关体系
对于Servlet接⼝就不再重复介绍了
ServletConfig接⼝:⽤来定义⼀个在初始化期间将配置信息(Servlet名、初始化参数等)传递给Servlet的Servlet配置对象。它的主要实现⼦类是StandardWrapperFacade类。
GenericServlet抽象类:⽤于包装Servlet接⼝,其中提供了很多Servlet接⼝的默认实现,这样我们实现Servlet的时候,就不必实现Servlet接⼝的所有⽅法,只重写核⼼⽅法即可。
HttpServlet抽象类:听这个类的名字就⼤概能够知道,HttpServlet类是专门⽤于处理http请求的Servlet类。它继承了
GenericServlet类,其中有很多http请求专⽤的处理⽅法(例如:doGet、doPost、doPut等等⽅法)
Servlet使⽤例⼦
@WebServlet("/my")
public class MyServlet extends HttpServlet {
@Override
public void init(){
System.out.println("");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws IOException {
}
@Override
public void destroy(){
System.out.println("");
}
}
以上代码就是⼀个基础的Servlet代码,使⽤起来⼗分简单。我们只要继承HttpServlet,然后覆盖其init、destroy、doGet、doPost(还有其他的doXXX⽅法,后⾯源码分析会讲到)即可。
从使⽤层⾯上来看,实现⼀个Servlet真的没什么难度,但是我们关注不是如何使⽤,⽽是其中的实现原理。
Servlet执⾏流程
配置信息初始化阶段:当服务器启动后,⾸先会读取l配置⽂件或者注解配置的相关Servlet信息,然后创建对应的对象,为之后Servlet初始化做准备。
Servlet初始化阶段:当请求访问达到,Servlet⾸先调⽤init⽅法进⾏初始化。
Servlet执⾏阶段:当Servlet初始化完成后,就会调⽤service⽅法进⾏业务处理。
Servlet源码分析
基于以上对Servlet的了解,那么现在开始进⼊Servlet的源码分析。
1. 配置信息初始化阶段:
⾸先来了解下配置信息初始化阶段,就拿上⾯⾃定义的MyServlet类来作为例⼦讲解(为了讲解源码时候排除不必要的⼲扰,此后的源码解析内容只针对关键部分代码进⾏讲解)。
当我们定义好MyServlet类后,便启动Tomcat服务器。
通过调试可以发现配置信息初始化的⼊⼝是ContextConfig类的configureContext⽅法:
private void configureContext(WebXml webxml){
...// 省略⼲扰代码
/
/ 从l配置⽂件或注解配置信息中获取配置的Servlet,并将其封装成ServletDef对象(这个ServletDef是个什么?)
for(ServletDef servlet : Servlets().values()){
Wrapper wrapper = ateWrapper();// 从context容器中创建wrapper对象(这个context是个什么?warpper呢?)...
wrapper.ServletName());// 为wrapper设置Servlet名称
Map<String,String> params = ParameterMap();
for(Entry<String, String> entry : Set()){
wrapper.Key(), Value());// 为wrapper添加初始化参数
}
...
wrapper.ServletClass());// 为wrapper设置Servlet类限定名
...
}
这个就是启动Tomcat后,初始化Servlet配置信息的⼊⼝⽅法。如果你有⾜够的好奇⼼的话,肯定会对这个⽅法有不少的疑问。
那么接下来,让我⼀步步解析:
ServletDef:
/**
* 来看下官⽅解释:
* Web应⽤程序的Servlet定义的表⽰形式,如在部署描述符的<servlet>元素
* 例如以下Servlet配置:
* <servlet>
*  <servlet-name>MyServlet</servlet-name>
*  <servlet-class>com.servlet.MyServlet</servlet-class>
* </servlet>
*  * 说⽩了,其实这个ServletDef就是将我们配置的Servlet信息封装成了⼀个对象
* 其中,它还有两个重要属性:servletName、servletClass
*/
public class ServletDef implements Serializable {
...
private String servletName = null;// Servlet名,必须唯⼀。表⽰<servlet-name>标签中的内容
private String servletClass = null;// Servlet类全限定名。表⽰<servlet-class>标签中的内容
...
}
通过查看ServletDef类的⽅法和属性,以及官⽅解释,可以得知:这个ServletDef类其实就是⽤来封装Servlet相关配置信息的,我称其为Servlet定义对象。
context:
这个context对象,其实代表的是容器,它是StandardContext类的实例化对象。这个通过调试就可以知道,在此不解释太多。
wrapper:
对于warpper对象来说,它是通过调⽤ ateWrapper() ⽅法⽽来的。单看⽅法名调⽤就可以⼤概猜测到,它是在容器中⽣成并返回的,因此我们来看下StandarContext类的createWrapper⽅法:
/**
* 从这个⽅法的关键代码来看,其实这个wrapper对象是通过反射的⽅式创建的
* ⽽且它是StandarWrapper类的实例对象
*/
public Wrapper createWrapper(){
Wrapper wrapper = null;
if(wrapperClass != null){
try{
wrapper =(Wrapper) Constructor().newInstance();
}catch(Throwable t){
ExceptionUtils.handleThrowable(t);
<("createWrapper", t);
return null;
}
}else{
wrapper =new StandardWrapper();
}
...
return wrapper;
}
为了全⾯了解这个wrapper对象,我们继续往下看StandarWrapper类:
/**
* 看下官⽅解释:
* Wrapper接⼝的标准实现,表⽰单个servlet定义。不允许使⽤任何⼦容器,并且⽗容器必须是上下⽂
*/
public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter {
...
}
先不说StandardWrapper类其他细节,通过官⽅的解释,我们可以知道它其实是Wrapper接⼝的实现⼦类。那么为了搞清楚这个StandardWrapper类的具体⽬的,我们再来看下这个Wrapper接⼝:
/**
* 结合官⽅的注释,以及其中定义的各种抽象⽅法,可以知道:
* Wrapper的实现类负责管理其基础servlet类的servlet⽣命周期,包括在适当的时间调⽤init()和destroy()等等
*/
public interface Wrapper extends Container {
...// 其中的抽象⽅法都是针对于Servlet设置/获取相关信息,在此不⼀⼀列举
}
到这⾥可以明⽩StandardWrapper类其实是⽤于管理Servlet⽣命周期的,从创建到销毁。那么基于这个理论,我们再来看看它其中的重要属性和⽅法:
public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter {
// 真正的Servlet对象,默认为空
protected volatile Servlet instance = null;
...
// Servlet类的全限定名,默认为空
protected String servletClass = null;
@Override
public void setServletClass(String servletClass){// 设置Servlet类全限定名
String oldServletClass =this.servletClass;
this.servletClass = servletClass;
support.firePropertyChange("servletClass", oldServletClass,
this.servletClass);
if(Constants.JSP_SERVLET_CLASS.equals(servletClass)){
isJspServlet =true;
}
}
...
// 设置Servlet名(name属性并不是在当前类中定义的)
public void setServletName(String name){
setName(name);
}
}
到此, Servlet配置信息初始化阶段的源码解析就结束了。在这个阶段做的事情并不多,主要就是创建wrapper对象,并为其设置相应的Servlet配置信息,为之后的阶段做准备。
为了加深理解,送出下图:
2. Servlet初始化阶段
当Tomcat服务器启动后,Servlet相关的配置信息已经初始化好了。那么当我们在⽹页上通过⽹址访问后端Servlet时,此时就会进⼊Servlet的第⼆个阶段——Servlet初始化。
通过调试,可以知道Servlet初始化的⼊⼝是StandardWrapperValve类的invoke⽅法:
public final void invoke(Request request, Response response)throws IOException, ServletException { ...
Servlet servlet = null;
StandardWrapper wrapper =(StandardWrapper)getContainer();
...
// 分配⼀个Servlet实例来处理request请求
try{
if(!unavailable){
servlet = wrapper.allocate();
}
}
// 在之后的操作中会使⽤到上⾯分配到的servlet对象对过滤链进⾏初始化
// 因为在请求到达Servlet之前,会经过⼀系列的过滤器校验过滤
/
/ 但本⽂只是对Servlet进⾏源码解析,对于Filter过滤器的源码不做太多解释
...
}
通过上⾯的关键代码,可以了解到Servlet对象是通过wrapper对象进⾏分配的。
为此,来看下StandardWrapper类的allocate⽅法:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。