最开始的时候,我并没有系统地学过Servlet,因为第一次看的时候觉得它好简单,然后就直接学了Struts 2。但问题也就因为我没有系统地学过Servlet而产生了,在Struts 2中很多东西都被封装好了,但实现的机制还是用的Servlet,而我就有些东西无法理解了,于是决定回过头来好好学了下Servlet。

说实话,目前市面上好多讲Servlet的书都讲得相当浅,一般都是随便讲一下应用就带过了,然后就是搞几个所谓的实际项目来凑下字数。很幸运,我找到一 本很不错的书,孙鑫老师写的《Servlet/JSP深入详解》,个人感觉讲得非常不错,看完之后,收获还是蛮大的。于是,决定把一些一般的书中很少讲到 但很重要的细节拉出来总结一下。

第一点被我拉出来的就是Servlet的线程安全问题了,明确说明一下:Servlet不是线程安全的。在Servlet规范定义中,在默认情况下 (Servlet不是在分布式的环境中部署),Servlet容器对声明的每一个Servlet只创建一个实例。如果有多个客户请求同时访问这个 Servlet,Servlet采用多线程的方式来处理这些请求,Servlet容器维护了一个线程池来服务请求。线程池实际上是等待执行代码的一组线 程,这些线程叫做工作者线程(WorkerThread),Servlet窗口使用一个调度者线程(Dispatcher Thread)来管理工作者线程。当容器收到一个访问Servlet的请求时,调度者线程从线程池中选取一个工作者线程,将请求传递给该线程,然后由这个 线程执行Serlvet的service()方法。

Servlet的线程安全问题也就因此产生,假设一个Servlet中有一个实例变量name,当两个用户同时访问一个Servlet时,Servlet 中name这个实例变量还是安全的吗?答案是否定的。问题出在name是个实例变量,原因我想大家都明白了。解决的办法有两个:一个就是把name这个实 例变量定义为本地变量,这个每一个线程就都将拥有这个变量的拷贝,线程对自己栈中的本地变量的改变不会影响到其他线程的本地变量的拷贝,因此,在请求处理 过程中,变量name的值也就不会被别的线程所改变。在Servlet的开发中,本地变量总是线程安全的。第二种解决办法就是把方法(如 doGet(),doPost()等)进行同步处理,这样肯定也是可以解决问题的,但产生的负使用就是访问同一个Servlet的请求将排队,一个线程处 理完请求后,才能执行另一个线程,带来的性能问题也就不好怎么说了。

同样的思考方法,可以得到下面的结论:ServletContext属性的访问也不是线程安全的,HttpSession由于其本身的特性(只能在处理属 于同一个Session的请求的线程中被访问)决定了它是线程安全的,ServletRequest对象只在一个线程中被访问,故而也是线程安全的。


第二点就是Servlet的异常处理机制,较少看到有书专门讲这个的,有兴趣的朋友可以找点资料看一下下,我没怎么弄懂,也不太清楚有哪些地方要使用。


第三点是Servlet的会话跟踪技术,在Servlet规范中,有三种机制用于会话跟踪:1.SSL(安全套接字层)会话;2.Cookies;3.URL重写。

    SSL是一种运行在TCP/IP之上的像HTTP这种应用协议之下的加密技术,SSL可以让采用SSL的服务器认证采用SSL的客户端,并且在客户端和服 务器之间保持一种加密的连接。Cookies是在响应报头和请求报头中被传送的,不与传送的内容混淆在一起,所以Cookies的使用对于用户来说是透明 的,如果用户禁用Cookies,那么Web服务器就无法利用Cookies来跟踪用户的会话了,而要采用URL重写机制。URL重写就是在URL中附加 标识用户的SessionID,Servlet容器解释URL时取出SessionID,根据SessionID将请求与特定的Session关联。


第四点是Cooikes与Session的对比,两者的最大区别在于:Session在服务器端保存信息,Cookies在客户端保存信息。为了跟踪用户 的会话,服务器端在创建Session后,需要将SessionID交给客户端,在客户端下次请求时,将这个ID随请求一起发送回来,可以采用 Cookies或URL重写的方式,将SessionID发送给客户端。另外,保存SessionID的Cookies在关闭浏览器后就删除了,不能在多 个浏览器进程间共享。如果需要对Session进行持久化,则需要将内存中Session对象保存到持久存储设备中,可通过对象序列化的技术来实现,当需 要重新加载Session对象时,通过对象反序列化的技术在内存中重新构造Session对象。这就要求HttpSession的实现类要实现 Serializable接口,同时Session中保存的对象所属的类也要实现该接口。


学到目前,虽然自己没有学出个什么名堂,但我终于明白了所谓的MVC框架的意义,像Struts、Struts 2、SpringMVC其实并不难,而且谁也无法保证它们会火多久,自己以后会不会用到它们,但无论怎样,基础是永远都不会变的。所以,不论这些东西学得 怎么样,Servlet跟JSP这些基础一定要学好,有了这些基础,感觉学这些框架还是蛮好上手的,至少我个人现在是这种想法。