Session 机制和 Cookie 机制
HTTP协议是无状态的, 而Cookie和Session都是在无状态的基础上希望实现有状态的效果, 两者是在客户端或者是服务端使用缓存等手段来实现状态的维护.
Session : 意指从某个操作的行为开始到结束之间持续的时间, 希望在该时间内对某一会话是有状态的.
Cookie的做法是在客户端内部做缓存, 需要用的时候就从cookie里面取, 并跟其他信息一样发回服务端, 做到状态的保存. 但是如果是客户端不支持cookie, 就无法使用这种机制.
session在服务端实现的关键点在于生成一个session id, 一般是以http中信息做一个类似散列表或者本来就是散列表的结构, 给每一个请求做一个散列, 如果本来已经在散列表中就取出session id, 如果没有就重新分配一个session id, 并存入散列表以便下次使用.
Servlet 3.0 Doc
和Servlet 2.5 的区别
- Servlet2.5中Listener的调用顺序是随机的, 现在不是
- 如果没定义metadata-complete=true, 就会对配置的注解进行扫描, 之前所有的模块都要定义在web.xml上, 现在可以在各自的模块里面定义.
Servlet是一个interface, 在Java API中有两个类实现了该接口, 分别是GenericServlet, HttpServlet, 一般是直接拓展HttpServlet类.
public interface Servlet {
// Life Cycle Function
public void init(ServletConfig config) throws ServletException;
public void destroy();
// Service Function
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
// Info Fucntion
public String getServletInfo();
public ServletConfig getServletConfig();
}
生命周期函数从Web Server中初始化和销毁, 所有的request和response都走service函数, 其他函数是提供信息的.
GenericServlet是Servlet一个协议无关的实现, 有简单的生命周期管理,还实现了ServletConfig和java.io.Serializable. 如果需要使用GenericServlet仅仅需要重写service()方法.
HttpServlet是Servlet的一个关于Http协议的实现, 同样也实现了ServletConfig和java.io.Serializable. 一般的Servlet类应当继承于HttpServlet, 并重写以下方法:
doGet, if the servlet supports HTTP GET requests
doPost, for HTTP POST requests
doPut, for HTTP PUT requests
doDelete, for HTTP DELETE requests
init and destroy, to manage resources that are held for the life of the servlet
getServletInfo, which the servlet uses to provide information about itself
但是一般不推荐重写service方法, service方法中将每一个Http Request dispatch 到每一个http request type的handler, 也不要重写doOptions和doTrace方法.
需要注意多线程的问题, 访问静态资源或者是共享资源的时候需要注意.
service函数就是一个分发函数, 对get有lastModified的优化, 主要针对浏览器的缓存和Proxy.
一般的服务器上应该只有某个声明的servlet的一个实例, 但是如果一个servlet实现了SingleThreadModel的接口, Servlet Container会针对每一个线程保有一个servlet对象.但是这个接口并不推荐, 以后也会被废弃.
Servlet的生命周期
Loading And Instantiation
Servlet的容器需要负责ervlet的Loading And Instantiation, 可以在容器启动的时候或者延迟到request到来的时候, Servlet的容器启动时, 必须locate到所有注册的servlet class, 可以通过本地/远程文件系统, 或者网络服务.
Initialization
在Servlet实例建立后, 还需要进行init(), 主要工作是确定Servlet Config和Servlet Context是否都可以访问到. Servlet Config可以通过Web Applciation的Configuration得到.
当init()出错时, container应该保证servlet不能放入容器, 且这里不会调用servletg的destory(), container会进行再次尝试并抛出异常.
静态方法中不应该包括调用JDBC等资源, 因为并不能保证静态方法在init()之后调用.
Service
在Init之后, Servlet可以service每一个Request
Asynchronous processing
如果在Servlet中有一些需要等待的过程, 如数据库连接, 网络消息返回等等, 可以进行异步操作, 先返回到container并释放线程, 后面当资源可用时在根据AsyncContext.dispatch来重新分配, 由另一个线程或者callback来给出返回.
一个典型的异步处理的过程如下:
- 收到请求, 通过filter或者是验证, 传递给一个servlet对象.
- servlet进行处理.
- the servlet issues request发生, 出现耗时操作.
- Servlet返回并未生成任何response.
- 当资源可用时, 线程继续在当前线程中处理或者是将资源通过AsyncContext调度到其他线程或者回调处理
从asyncSupported=true的Servlet Dispatch 到一个asyncSupported=false的Servlet, Response会在不支持异步的Servlet的service method(如doGet, doPost)执行完时返回, 但是servlet容器需要保证调用AsyncContext中的complete函数来通知AsyncListener, 而AsyncListener.onComplete会负责释放资源
但是不能从同步的Servlet Dispatch到异步的Servlet, 异步的任务会在接受Request的Servlet中直接返回Waiting, 而有可能会经过一个或者多个filter,将response包装, 最终传递到另一个Servlet, 中间会对Response加wrapper, 并将信息写入或读出wrapper.
在ServletContext中增加了AsyncContext的内容:
有关AsyncContext, 是真正在不同线程中可以共享访问的内容(或者线程传递的内容), 当使用了异步之后, 对Response真正的返回可以认为是Commited, 保存在AsyncContext中, 接受Request的线程不会把Response Commited, 而只会在AsyncContext.complete被调用, 或者是异步时间Timeout, 且针对这个Timeout没有handle.
AsyncContext是被ServletRequest.startAsync调用之后创建并且初始化的.
End of Service
如果servlet容器需要停止一个Servlet实例, 不管是需要交换内存或者关闭, 容器本身都需要保证当前所有由servlet的service处理的线程都退出或者是由一个默认的servlet timeout.