Servlet
Servlet是基于Java技术的Web组件,被容器管理,用于生成动态内容。可以被基于Java技术的Web Server动态加载并运行。客户端通过Servlet容器实现的请求/应答模型与Servlet交互。
Servlet容器
Servlet容器是Java Web Server的一部分,它提供接受请求和发送响应的服务,基于MIME编解码请求和响应,Servlet容器同样能够管理Servlet生命周期。
Servlet接口
Servlet生命周期
Servlet生命周期由容器管理。在Servlet规范中规定了Servlet如何被加载、实例化、初始化、处理请求、结束服务。
加载、实例化
加载就是Servlet类的加载,我们可以通过文件、网络流等加载Servlet,容器通过自定义的类加载器可以实现多个Servlet项目在容器中运行,互不影响。在Servlet中加载和实例化是一起的,我们可以配置Servlet使得加载和实例化可以发生在容器启动时,或者发生在该Servlet第一次处理请求时。通过Servlet属性==loadOnStartup==来配置,当loadStartup大于0时会在容器启动时就加载和实例化,默认值为-1(第一次处理请求时加载和实例化)。
初始化
*Servlet的处理化通过调用init(ServletConfig)方法进行初始化。SerlvetConfig对象可以访问配置的Servlet InitParam属性,然后我们可以在初始化方法中进行其它的初始化操作。例如在Spring MVC中我们通过配置Dispatcher Servlet Param属性把Spring相关信息注入,然后在init()方法中获取并进行Spring容器的初始化,代码如下:
1 | <!-- 配置DispatcherServlet --> |
注意:一个Servlet对应一个ServletConfig,所以我们不应该把全局信息放入ServletConfig,除非只有一个Servlet。
处理请求
Servlet通过调用serivce(request,response)方法处理请求。客户端请求信息有ServletRequest及其子类表示,服务器响应信息有ServletResponse及其子类表示,Servlet通过调用service()方法处理请求并回复响应。由于容器可以并发的调用Servlet的service()方法,所以我们在实现service()方法或其子方法,例如doGet()方法时需要注意线程安全问题。
结束服务
容器调用Servlet的destory()方法进行结束服务,然后释放该Servlet实例以供GC。我们可以在destory()方法中释放其使用的资源或者保持其持久化状态(HttpServlet实现了Serializable接口)。
Servlet实例化数量
一般来讲,Servlet容器会对每个Servelt必须实例化一个对象(有且只有一个,实现了SingleThreadModel接口除外)。在分布式中,容器需要对每个JVM中个每个Servlet实例化一个对象(实现实现了SingleThreadModel接口除外)
Servlet处理请求
Servlet通过service()方法处理请求。通常我们继承HttpServlet,其实现的service()方法会自动把Http请求依照协议转发到相关方法上。
- doGet 处理 Http Get请求
- doPost 处理 Http Post请求
- doOptions 处理 Http Options请求
- ….
代码如下:
1 | protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
特别的GET处理(有条件支持)
在Http缓存规范中(可参考这篇文章)有==Last-Modified==和==if-Modified-Since==两个Header来进行Http缓存,Servlet处理GET请求时会通过处理这两个字段,有条件的支持缓存(依赖于Servlet的实现),代码如下:
1 | protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
编写Servlet只需要重写getLastModified()方法即可,如下代码:
1 |
|
第一次访问Response Header如下:
1 | HTTP/1.1 200 |
后面访问Request Header及Response Header如下:
1 | GET /servlet/cacheServlet HTTP/1.1 |
Servlet异常处理
Servlet在初始化(调用init()方法)和处理请求(调用service()方法)时,会抛出异常。
初始化时异常
抛出ServletException异常时会停止初始化,且以后也不会初始化;抛出UnavailableExceptioin异常时,容器会在不可用时间过后继续初始化Serlvet。也就是说当请求会被容器分发到其他Servlet中去处理,相当于没有该Servlet。
处理请求时异常
与初始化异常一样都可能抛出两种异常,且异常意义一样。但容器会继续都让该Servlet处理请求,抛出ServletException异常时会返回Http Response 500异常;抛出UnavailableException异常时会返回Http Response 503异常。
ServletContext
ServletContext(Servlet上下文)代表Servlet运行在Web应用的视图,它代表整个当前整个Web应用。ServletContext可以动态添加Servlet、Filter、Listener,可以获取当前Web应用下的所有资源,存放Servlet Attribute、Parameter属性以供所有Servlet访问。
每个部署到容器的Web应用都有一个与之对于的ServletContext,在分布式环境下每个JVM中的每个Web应用都有其对于的ServletContext。ServletContext是线程不安全的,所以所有的Servlet Parameter、Attribute都应该是线程安全的。
动态加载Servlet、Filter、Listener
Servlet3.0以后ServletContext支持动态添加Servlet、Filter、Listener,可以编程式的开发Web应用。其只能在实现ServletContextListener的contexInitialized方法中动态的添加,否则会抛异常,下面是其动态添加方法的API的注释:
1 | IllegalStateException if this ServletContext has already been initialized |
具体接口实例如下,其中返回Registartion对象可以配置UrlMapping等属性:
1 | public ServletRegistration.Dynamic addServlet(String servletName, Class <? extends Servlet> servletClass); |
Spring中的ServletRegistrationBean、FilterRegistrationBean等就是通过ServletContext编程式的动态添加Servlet、Filter
获取资源
ServletContext接口提供访问Web应用资源的方法。Web应用资源指的是该Web应用下的所有资源,包括WEB-INF、自定义静态资源文件等等。ServletContext获取资源API如下所示,其中path必须以”/“开头,表示该资源是相对于该Web应用下的资源:
1 | /** |