ServletContext简介
如果想获取servlet运行时的环境信息,比如初始化参数、资源url等,那么就了解ServletContext吧。
在servlet规范中,ServletContext接口定义为servlet运行环境的信息,也就是说可以从这个接口的实现对象中获取一切关于运行环境的东西;
那么这个接口的实现在哪?
这个接口的实现当然是在servlet引擎中了,由servlet引擎的提供商负责维护。
ServletContext对象是什么时候生成的呢?
Servlet容器在启动时会加载Web应用,并为每个Web应用创建一个唯一的ServletContext对象;通过这个对象能够获得初始化参数、log事件、资源的URL和实现Web应用范围内的数据共享;
对于分布式的应用呢?
当web应用分布在多个虚拟机上时,web应用在所分布的每个虚拟机上都拥有一个ServletContext实例。缺省情况下,ServletContext不是分布式的,并且只存在于一个虚拟机上。
详细的介绍参见——《servlet规范》。
注意:ServletContext常常被翻译成“servlet上下文”,刚开始很难理解这个词,最后才明白就是ServletContext,坑爹啊,这种最基本的接口竟被翻译了一半。
ServletContext与应用访问路径
每一个ServletContext在web服务中都会有一个路径与之绑定,作为外部访问其所在web应用的根路径;
例如:如果路径http://localhost/myservlet/ 被绑定到某个ServletContext,那么所有以/myservlet/开头的请求都将路由到这个ServletContext所在的web应用中,且该路径为web应用的根路径。
通常这种路径的对应关系是在web服务器中配置的,例如tomcate在conf/server.xml中配置web应用的根路径,然后该路径被绑定到其运行生成的ServletContext。
ServletContext的应用举例
这里只列举了ServletContext两个比较常用的功能,目的是从实际代码的层面来加深对servletContext的理解。
1、获取初始化参数
接着上一篇的myservlet应用,在包com.myservlet.app.servlet下创建新的类ContextTestServlet.java,源码如下:
public class ContextTestServlet extends HttpServlet{ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext sc= getServletContext(); StringBuilder sb = new StringBuilder(); sb.append("InitParameterNames:"); Enumeration e = sc.getInitParameterNames(); String name; while(e.hasMoreElements()){ sb.append("<p/>"); name = e.nextElement().toString(); sb.append(name); sb.append(":"); sb.append(sc.getInitParameter(name)); } output(response, sb.toString()); } private void output(HttpServletResponse response,String msg) throws IOException{ // 设置输出 response.setContentType("text/html;charset=GBK"); PrintWriter printWriter = response.getWriter(); printWriter.println("<HTML><HEAD><TITLE>myFirstServlet</TITLE>"); printWriter.println("</HEAD><BODY>"); printWriter.println(msg); printWriter.println("</BODY></HTML>"); } }
在servlet的实例中,通过getServletContext()方法即可获得其所在环境的ServletContext对象;
通过sc.getInitParameterNames()获得初始化参数集合;
通过sc.getInitParameter(name)获得初始化参数的值。
修改web.xml,配置新建的servlet和初始化参数,如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app> <context-param> <param-name>webName</param-name> <param-value>myservlet</param-value> </context-param> <context-param> <param-name>webStatus</param-name> <param-value>test</param-value> </context-param> <!-- MyFirstServlet --> <servlet> <servlet-name>my_first_servlet</servlet-name> <servlet-class>com.myservlet.app.servlet.MyFirstServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>my_first_servlet</servlet-name> <url-pattern>/hello.htm</url-pattern> </servlet-mapping> <!-- GetContextServlet --> <servlet> <servlet-name>contextTestServlet</servlet-name> <servlet-class>com.myservlet.app.servlet.ContextTestServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>contextTestServlet</servlet-name> <url-pattern>/viewContext.htm</url-pattern> </servlet-mapping> </web-app>
启动tomcat,在浏览器中输入:http://localhost:8080/viewContext.htm,即可看到以下结果。
2、应用范围内共享数据
在ContextTestServlet.java中添加代码,如下:
public class ContextTestServlet extends HttpServlet{ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ...... sc.setAttribute("tag", "from ContextTestServlet"); } private void output(HttpServletResponse response,String msg) throws IOException{ ... ... } }
通过 sc.setAttribute("tag", "from ContextTestServlet")在servletContext中保存一个名为tag的属性。
修改MyFirstServlet.java的代码,如下:
public class MyFirstServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取用户的name String name = request.getParameter("name"); // 对name进行转码 if (name != null) { name = new String(name.getBytes("ISO-8859-1"), "GBK"); } else { name = "陌生人"; } ServletContext sc= getServletContext(); String tag = (String) sc.getAttribute("tag"); // 设置输出 response.setContentType("text/html;charset=GBK"); PrintWriter printWriter = response.getWriter(); printWriter.println("<HTML><HEAD><TITLE>myFirstServlet</TITLE>"); printWriter.println("</HEAD><BODY>"); printWriter.println("<h1>" + name + ":您好!"); printWriter.println("<p>" + tag); printWriter.println("</h1>"); printWriter.println("</BODY></HTML>"); } }
通过sc.getAttribute("tag")获取servletContext中的tag属性值
启动tomcat,在浏览器中输入:http://localhost:8080/hello.htm,得到如下结果:
然后访问http://localhost:8080/viewContext.htm,再访问http://localhost:8080/hello.htm,如下:
由此可见,在ContextTestServlet中设置的tag属性,通过servletContext在同一应用的其他Servlet中得到了共享,即实现了应用范围内的数据共享。
问题:
1、Servlet容器、servlet引擎、servlet程序,和web服务器,web应用程序它们到底有什么不同,有什么联系?
web服务器是网络上提供信息浏览的程序,同时也是信息的载体;
但是信息的组织和维护需要在web服务器上运行一个合适的web应用程序;
web应用程序可以使用多种技术来实现,目前使用最广泛的是servlet技术;
使用servlet技术需要编写servlet程序,而且要使用servlet引擎来运行这些程序,所以这就要求web服务器中内嵌servlet引擎,为了管理这些servlet程序和servlet引擎的动作,就抽象出一个servlet容器的概念。
总起来说,运行在web服务器上的程序就是web应用程序;servlet引擎,servlet程序都要在servlet容器上运行,有些web服务器可以直接作为servlet容器。
2、Tomcat启动时会对多个Web应用各自创建一个jvm还是只创建一个公用JVM?
Tomcat启动一次(一个进程)创建一个JVM,所有的Web应用在同一个JVM中跑。
如果一个应用弄挂了JVM,所有应用就都挂了。
所以,为了避免一个应用(war)搞垮jvm带来的麻烦,一般一个Tomcat只会放一个应用。