Servlet总结(四)

七、ServletContext

1、前言

ServletContext可以比喻为一座桥梁,连接着Servlet和Servlet容器。context直接翻译过来就是“背景,上下文”,也就是一个Servlet的运行环境。

我们知道,Servlet是为了构建Web应用而被发明出来的。一个Web应用想要对外提供服务,必然会包括很多个Servlet、HTML或JSP页面,XML配置文件以及各种多媒体资源。一个服务器上,可能会同时运行多个Web应用,那么各个Web应用之间必然要划分清楚各自的势力范围,不能各种资源混在一起,否则服务就乱套了。这个势力范围,表现在物理上,就是每个Web应用都使用一个独立的文件夹存放自己的所有资源。

文件夹不会自己运行然后被浏览器访问,而是借助Web容器和Servlet容器才可以被访问到。Web容器本质上只是处理请求发送响应,比如找到一个HTML页面返回给浏览器。但是Servlet不是静态页面啊,它是一个可执行的java文件,需要去执行才能生成HTML代码,然后再由Web容器返回给浏览器。所以,Tomcat这种所谓的Servlet容器,其实把Web容器的活儿也干了,是二者的合体;Apache和Ngnix才是纯正的Web容器。

对于存放Web应用的各种资源的文件夹,Servlet规范其实规定了这个文件夹的目录结构,其中最主要的是必须要存在一个WEB-INF目录,其他的我们以后详细说。现在关键的是,这个Web应用资源的“根目录”放在哪里呢?这就跟不同的Servlet容器实现有关了,以我们最熟悉的Tomcat为例,这个目录要放在/tomcat-home/webapps下面。有多少个Web应用这个webapps就有多少个对应的根目录,那么Tomcat如何去找到对应的Web应用呢?答案是Tomcat的主配置文件server.xml,其中有个Context标签,对应的就是具体的Web应用:

<Host name="localhost"  appBase="webapps"  unpackWARs="true" autoDeploy="true">
  <Context docBase="/home/m/comet" path="/comet" reloadable="true" />
</Host>

Context是Host标签的子元素,后者代表一台主机,前者是这个主机上运行的Web应用。如果有多个应用,就要在Host中写入多个Context标签。Context标签最重要的的两个属性是docBase和path,前者代表Web应用根目录在服务器文件系统中的绝对路径;后者是提供给浏览器的访问路径:也就是浏览器通过Tomcat访问"/comet"为根路径的URL时,Tomcat就会去docBase所指定的文件夹中找资源。所以Context标签本质上是一个映射。

2、ServletContext总论

从上面的叙述中可以看出,一个ServletContext其实就是代表着Servlet容器中的一个Web应用,Web应用中的Servlet通过ServletContext和Servlet容器打交道。用官方的话就是“servlet 容器必须强制 web 应用程序和 ServletContext 之间一对一进行通信,ServletContext 对象提供了一个servlet 和它的应用程序视图。” 每个Web应用可以包含多个Servlet,但是只能有一个ServletContext。

ServletContext中的API分为以下几种:

(1)获取环境信息和资源

public String getContextPath();
public ServletContext getContext(String uripath);
public Set<String> getResourcePaths(String path);
public URL getResource(String path) throws MalformedURLException;
public InputStream getResourceAsStream(String path);
public String getMimeType(String file);
public String getRealPath(String path);
public String getServerInfo();
public String getServletContextName();
public String getVirtualServerName();
public int getMajorVersion();
public int getMinorVersion();
public int getEffectiveMajorVersion();
public JspConfigDescriptor getJspConfigDescriptor();
public ClassLoader getClassLoader();
public int getEffectiveMinorVersion();
(2)参数及属性操作

public String getInitParameter(String name);
public Enumeration<String> getInitParameterNames();
public boolean setInitParameter(String name, String value);
public Object getAttribute(String name);
public Enumeration<String> getAttributeNames();
public void setAttribute(String name, Object object);
public void removeAttribute(String name);

(3)动态操作Servlet、Filter和Listener

public ServletRegistration.Dynamic addServlet(
        String servletName, String className);
public ServletRegistration.Dynamic addServlet(
        String servletName, Servlet servlet);
public ServletRegistration.Dynamic addServlet(String servletName,
        Class <? extends Servlet> servletClass);
public <T extends Servlet> T createServlet(Class<T> clazz)
        throws ServletException;
public ServletRegistration getServletRegistration(String servletName);
public Map<String, ? extends ServletRegistration> getServletRegistrations();
public FilterRegistration.Dynamic addFilter(
        String filterName, String className);
public FilterRegistration.Dynamic addFilter(
        String filterName, Filter filter);
public FilterRegistration.Dynamic addFilter(String filterName,
        Class <? extends Filter> filterClass);
public <T extends Filter> T createFilter(Class<T> clazz)
        throws ServletException;
public FilterRegistration getFilterRegistration(String filterName);
public Map<String, ? extends FilterRegistration> getFilterRegistrations();
public void addListener(String className);
public <T extends EventListener> void addListener(T t);
public void addListener(Class <? extends EventListener> listenerClass);
public <T extends EventListener> T createListener(Class<T> clazz)
        throws ServletException; 
(4)会话管理

public SessionCookieConfig getSessionCookieConfig();
public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes);
public Set<SessionTrackingMode> getDefaultSessionTrackingModes();
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes();

(5)其他

public void declareRoles(String... roleNames);

3、 获取环境信息和资源

先上测试代码:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = getServletConfig().getServletContext();

        System.out.println("contextPath: " + servletContext.getContextPath());
        ServletContext servletContext2 = servletContext.getContext("/ddd");
        System.out.println("Is servletContext same with servletContext2: " + servletContext.equals(servletContext2));


        System.out.println("resource paths: " + servletContext.getResourcePaths("/"));
        System.out.println("resources: " + servletContext.getResource("/index.jsp"));
        System.out.println("realPath: " + servletContext.getRealPath("/index.jsp"));
        System.out.println("MIME type:" + servletContext.getMimeType("/home/m/program/tomcat_base1/conf/web.xml"));

        System.out.println("servletInfo: " + servletContext.getServerInfo());
        System.out.println("servletContextName: " + servletContext.getServletContextName());
        try {
            System.out.println("virtualServerName: " + servletContext.getVirtualServerName());
        } catch (NoSuchMethodError e) {
            System.out.println("getVirtualServerName not supported!");
        }

        System.out.println("majorVersion: " + servletContext.getMajorVersion());
        System.out.println("minorVersion: " + servletContext.getMinorVersion());
        System.out.println("effectiveMajorVersion: " + servletContext.getEffectiveMajorVersion());
        System.out.println("effectiveMinorVersion: " + servletContext.getEffectiveMinorVersion());

        System.out.println("classLoader: " + servletContext.getClassLoader().getClass().getName());
        System.out.println("jspConfigDescriptor: " + servletContext.getJspConfigDescriptor());
    }
运行结果如下:

contextPath: /ddd
Is servletContext same with servletContext2: true
resource paths: [/index.jsp, /WEB-INF/, /META-INF/]
resources: file:/home/m/IdeaProjects/learn/target/learn/index.jsp
realPath: /home/m/IdeaProjects/learn/target/learn/index.jsp
MIME type:application/xml
servletInfo: Apache Tomcat/8.5.14
servletContextName: Archetype Created Web Application
virtualServerName: Catalina/localhost
majorVersion: 3
minorVersion: 1
effectiveMajorVersion: 3
effectiveMinorVersion: 1
classLoader: org.apache.catalina.loader.ParallelWebappClassLoader
jspConfigDescriptor: null

首先getContext和getContextPath应该是一对逆函数,第二行打印结果也印证了这一点。当然也可以传给getContext一个不同的参数,前提是那个Context确实存在,那时第二行打印的结果就是false了。

getResourcePaths接收一个字符串表示的路径,并返回该路径下的所有目录和文件,如果是目录则在尾部追加一个“/”;该方法不会递归的返回子目录的内容。需要说明的有点:一是传入的参数必须是文件系统中的真实存在的路径,而不是URL中的路径;二是路径必须以“/”开头,而这个“/”代表的就是应用的根目录,这意味着getResourcePaths只能获取对应Web应用根目录下的东西;三是想要获取某个子目录的内容时,传入的参数必须把其父目录的路径也加上。

利用getResourcePaths可以得到Web应用根目录下到底都有哪些文件,但是只是一个字符串表示的文件名;而想要获取到某个文件具体的内容,还要靠getResource方法。把某个文件的相对于根目录的路径传递给getResource方法,就可以获取一个代表该文件的URL对象,利用这个对象就能获取到内容。同样的,把这个文件相对路径传递给getMimeType方法能知道该文件的MIME类型。

getServerInfo方法返回有关容器的信息,包括名称和版本号,这里返回的是“Apache Tomcat/8.5.14”,即Tomcat8。

getServletContextName返回Web应用的名称,这个名称是定义在web.xml中的,即<display-name>标签的值。这个值既不是Web应用根目录的名称,也不是URL中的访问名称。

getVirtualServerName方法返回的是主机名,也就是Servlet容器中Host的名称。对于Tomcat,这个名称定义在server.xml中,如下:

<Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true">

Host标签是Context标签的父元素,Host的父元素是Engine,一个Tomcat实例只会存在一个Engine,而一个Engine下面可以有多个Host。关于Host的讨论,以后再说。

但是实际的返回值是“Catalina/localhost”,我目前还不清楚Tomcat是如何实现这个方法的。而且Tomcat7实际没有实现这个方法,而Tomcat8才实现,所以用了try来包裹这行代码,因为在Tomcat7中会报错。

接下来是关于版本的,getMajorVersion返回的是容器所支持的版本,这个返回值可以通过容器的官方文档知道,是固定的;而getEffectiveMajorVersion返回的是Web应用实际使用的版本,那么这个版本体现或者定义在哪里呢?经过本大侠苦苦的追寻,总算找到了答案:在web.xml中!!而且是在根元素中,代码如下:

<web-app version="3.1" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:web="http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee">
最终的值是由version属性决定的,平时大家其实都不关注这个东西,而且这几个API也是基本不用的,所以比较冷门。我是通过 这几个 网页了解到的,感谢它们。

getClassLoader方法返回Web应用所使用的class loader,这个暂时先不多讨论。

getJspConfigDescriptor返回的是跟JSP有关的配置,以一个JspConfigDescriptor对象封装。实际上,这个返回值是从配置文件web.xml中取的,具体是jsp-config标签,它有两个子元素 , taglib 和jsp-property-group。这个方法先不多讨论。


4、参数及属性操作

这几个方法其实没啥好说的,跟前几篇文章中名字相同的方法功能也基本相同。唯一需要注意的是ServletContext中竟然有setInitParameter方法,但是仔细看说明的话,这个方法并不会覆盖配置文件中已有的参数。


5、(待续)

相关文章
相关标签/搜索