Servlet是Server+Applet的缩写,表示一个服务器应用。
其实就是一套规范,我们按照这套规范写的代码就可以直接在Java的服务器上面运行
为什么可以?因为Java的服务器也是按这规范写的
Servlet接口
既然Servlet是一套规范,那么最重要的当然就是接口了
//javax.servlet.Servlet
public interface Servlet{
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req,ServletResponse res) throws ServletException,IOException;
public String getServletInfo();
public void destroy();
}
init()在容器启动时被窗口调用(当load-on-startup设置为负数或者不设置时会在Servlet第一
次用到时才被调用),只会调用一次;
getServletConfig()用于获取ServletConfig;
service()用于具体处理一个请求;
getServletInfo方法可以获取一些Servlet相关的信息,如作者、版权等,这个方法需要自己实现,默认返回空字符串
destroy()主要用于在Servlet销毁(一般指关闭服务器)时释放一些资源,也只会调用一次
Init方法被调用时会接收到一个ServletConfig类型的参数,是容器传进去的。
Selvet-Config顾名思义指的是Servlet的配置
我们在web.xml中定义Servlet时通过init-param标签配置的参数就是通过ServletConfig来保存的,比如,定义SpringMVC的Servlet时指定配置文件位置的contextConfigLocation参数就保存在ServletConfig中
<servlet>
<servlet-name>实例名</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>demo-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Tomcat中Servlet的init方法是在org.apache.catalina.core.StandardWrapper的initServlet
方法中调用的,ServletConfig传入的是StandardWrapper(里面封装着Servlet)自身的门面类StandardWrapperFacade。
其实这个也很容易理解,Servlet是通过xml文件配置的,在解析xml时就会把配置参数给设置进去,这样StandardWrapper本身就包含配置项了,当然,并不是StandardWrapper的所有内容都是Config相关的,所以就用了其门面Facade类
//ServletConfig接口
package javax.servlet;
public interface ServletConfig{
//获取Servlet的名字,在web.xml中定义的<servlet-name>名字</servlet-name>
public String getServletName();
//非常重要
public ServletContext getServletContext();
//用于获取<init-param></init-param>配置的参数
public String getInitParameter(String name);
//获取所有init-param的名字集合
public Enumeration<String> getInitParameterNames();
}
getServletContext()非常重要,它的返回值ServletContext代表的是我们应用本身
ServletContext其实就是Tomcat中Context的门面类ApplicationContextFacade
(具体代码参考StandardContext的getServletContext方法)。
既然ServletContext代表应用本身,
那么ServletContext里边设置的参数就可以被当前应用的所有Servlet共享了。
我们做项目的时候都知道参数可以保存在Session中,也可以保存在Application中
而后者很多时候就是保存在了ServletContext中
我们可以这么理解
ServletConfig是Servlet级的,而ServletContext是Context(也就是Application)级的
当然ServletContext的功能要强大很多,并不只是保存一下配置参数
ServletConfig 和ServletContext最见的使用之一是传递初始化参数,我们以spring配置使用的最多的contextConfigLocation参数为例来看一下
<!-- web.xml -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>application-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>DemoServlet</servlet-name>
<servlet-class>com.excelib.DemoServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>demo-servlet.xml</param-value>
</init-param>
</servlet>
上面通过context-param配置的contextConfigLocation配置到了ServletContext中,而通过servlet下的init-param配置的contextConfigLocation配置到的ServletConfig中。在Servlet中可以分别通过它们的getInitParameter方法来获取
String contextLocation = getServletConfig().getServletContext()
.getInitParameter("contextConfigLocation");
String servletLocation = getServletConfig().getInitParameter("contextConfigLocation");
另外ServletContext中非常常用的用法就是保存Application级的属性,这个可以使用setAttribute来完成,比如
getServletContext().setAttribute(“contextConfigLocation”,“new path”);
这里设置同名的Attribute并不会覆盖initParameter中的参数值,它们是两套数据,互不干扰。ServletConfig不可以设置属性
GenericServlet(通用的Servlet)
GenericServlet是Servlet的默认实现,主要做了三件事
1.实现了ServletConfig接口,我们可以直接调用里面的方法
2.提供了无参的init方法
3.提供了log方法
GenericServlet实现了ServletConfig接口,我们在需要调用ServletConfig中方法的时候可以直接调用,而不再需要先获取ServletConfig了,比如,获取ServletContext的时候可以直接调用getServletContext,而无须调用getServletConfig().getServletContext()了,不过其底层实现其实是在内部调用了,getServletContext的代码如下
//javax.servlet.GenericServlet
public ServletContext getServletContext(){
//获取到ServletConfig
ServletConfig sc = getServletConfig();
if(sc != null){
//抛出异常
}
return sc.getServletContext();
}
GenericServlet是与具体协议无关的
HttpServlet
HttpServlet是用HTTP协议实现的Servlet的基类,写Servlet时直接继承它就可以了,不需要再从头实现Servlet接口,SpringMVC中的DispatcherServlet就是继承的HttpServlet
即然HttpServlet是跟协议相关的,当然主要关心的是如何处理请求了
所以HttpServlet主要重写了service方法。在servic方法中首先将ServletRequest和ServletResponse转换为了HttpServletRequest和HttpServletResponse,然后根据Http请求的类型不同将请求路由到了不同的处理方法,代码如下
//java.servlet.http.HttpServlet
public void service(ServletRequest res , ServletResponse res){
HttpServletRequest request;
HttpServletResponse response;
//如果请求类型不相符,则抛出异常
if(!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)){
//抛出异常
}
//转换请求的类型
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
//调用http的处理方法
service(request,response);
}
protected void service(HttpServletRequest req,HttpServletResponse resp){
//获取请求类型
String method = req.getMethod();
//将不同的请求类型路由到不同的方法去处理
if(method.equals(METHOD_GET)){
long lastModified = getLastModified(req);
if(lastModified == -1){
doGet(req,resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if(ifModifiedSince < lastModified){
maybeSetLastModified(reqp,lastModified);
doGet(req,resp);
} else{
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if(method.equals(METHOD_POST)){
doPost(req,resp);
//其他方法省略
}
}
这就是HttpServlet,它主要将不同的请求方式路由到了不同的处理方法,不过SpringMVC中由于处理思路不一样,又将所有请求合并到了统一的一个方法进行处理