当前位置 : 首页 » 文章分类 :  开发  »  web.xml

web.xml

当我们去启动一个WEB项目时,容器(JBoss、Tomcat等)首先会读取项目web.xml配置文件里的配置,当这一步骤没有出错并且完成之后,项目才能正常地被启动起来。


概述

web.xml加载过程

当我们去启动一个WEB项目时,容器(JBoss、Tomcat等)首先会读取项目web.xml配置文件里的配置,当这一步骤没有出错并且完成之后,项目才能正常地被启动起来。

  • 启动WEB项目的时候,容器首先会去它的配置文件web.xml读取两个节点:<context-param></context-param><listener></listener>
  • 紧接着,容器创建一个ServletContext(application),这个WEB项目所有部分都将共享这个上下文。
  • 容器以<context-param></context-param><param-name>作为键,<param-value>作为值,将其转化为键值对,存入ServletContext。
  • 容器创建<listener></listener>中的类实例,根据配置的class类路径<listener-class>来创建监听,在监听中会有contextInitialized(ServletContextEvent event)初始化方法,启动Web应用时,系统调用Listener的该方法,在这个方法中获得配置参数:
    ServletContext application = ServletContextEvent.getServletContext();
    String param-value = application.getInitParameter(“param-name”);
    得到此context-param的值之后,你就可以做一些操作了。
  • 接着,容器会读取<filter></filter>,根据指定的类路径来实例化过滤器。
  • 以上都是在WEB项目还没有完全启动起来的时候就已经完成了的工作。如果系统中有Servlet,则Servlet是在第一次发起请求的时候被实例化的,而且一般不会被容器销毁,它可以服务于多个用户的请求。所以,Servlet的初始化都要比上面提到的那几个要迟。

总的来说,web.xml的加载顺序是: context-param -> listener -> filter -> servlet。其中,如果web.xml中出现了相同的元素,则按照在配置文件中出现的先后顺序来加载


context-param

<context-param>声明应用范围内的初始化参数。它用于向 ServletContext提供键值对,即应用程序上下文信息。我们的listener, filter等在初始化时会用到这些上下文中的信息。

context-param元素含有一对参数名和参数值,用作应用的Servlet上下文初始化参数,参数名在整个Web应用中必须是惟一的,在web应用的整个生命周期中上下文初始化参数都存在,任意的Servlet和jsp都可以随时随地访问它。<param-name>元素指定参数名,<param-value>元素指定参数值。作为选择,可用<description>子元素来描述参数。在servlet里面可以通过ServletContext.getInitParameter(“param-name”)得到参数值param-value。

Spring配置

配置spring,必须需要<listener>,而<context-param>可有可无,如果在web.xml中不写context-param配置信息,默认的路径是/WEB-INF/applicationContext.xml,在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:在param-value里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔,也可以使用通配符*。例如:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:config/applicationContext-*.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

listener

listener简介

<listener>为web应用程序定义监听器,监听器用来监听各种事件,比如:application和session事件,所有的监听器按照相同的方式定义,功能取决去它们各自实现的接口。

常用的Web事件接口有如下几个:

  • ServletContextListener:用于监听Web应用的启动和关闭;
  • ServletContextAttributeListener:用于监听ServletContext范围(application)内属性的改变;
  • ServletRequestListener:用于监听用户的请求;
  • ServletRequestAttributeListener:用于监听ServletRequest范围(request)内属性的改变;
  • HttpSessionListener:用于监听用户session的开始和结束;
  • HttpSessionAttributeListener:用于监听HttpSession范围(session)内属性的改变。

listener主要用于监听Web应用事件,其中有两个比较重要的WEB应用事件:应用的启动和停止(starting up or shutting down)和Session的创建和失效(created or destroyed)。应用启动事件发生在应用第一次被Servlet容器装载和启动的时候;停止事件发生在Web应用停止的时候。Session创建事件发生在每次一个新的session创建的时候,类似地Session失效事件发生在每次一个Session失效的时候。为了使用这些Web应用事件做些有用的事情,我们必须创建和使用一些特殊的“监听类”。它们是实现了以下两个接口中任何一个接口的简单java类:javax.servlet.ServletContextListener或javax.servlet.http.HttpSessionListener,如果想让你的类监听应用的启动和停止事件,你就得实现ServletContextListener接口;想让你的类去监听Session的创建和失效事件,那你就得实现HttpSessionListener接口。

listener配置

配置Listener只要向Web应用注册Listener实现类即可,无序配置参数之类的东西,因为Listener获取的是Web应用ServletContext(application)的配置参数。

为Web应用配置Listener的两种方式:

  • 使用@WebListener修饰Listener实现类即可。
  • 在web.xml文档中使用<listener>进行配置。
    对于web.xml这种配置方式,只有一个元素<listener-class>指定Listener的实现类,如下所示:
    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

Spring加载

Spring加载可以利用ServletContextListener实现,也可以采用load-on-startup Servlet 实现,但是当filter需要用到bean时,由于加载顺序是:先加载filter后加载servlet,则filter中初始化操作中的bean为null;所以,如果过滤器中要使用到bean,此时就可以根据加载顺序listener -> filter -> servlet,将spring的加载改成Listener的方式。

  • 利用监听器加载Spring

    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    其中ContextLoaderListener的作用就是启动Web容器时,自动装配applicationContext.xml的配置信息,执行它所实现的方法。

  • 利用启动时初始化的Servlet加载Spring

    <servlet>
      <servlet-name>context</servlet-narne>
      <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
    </servlet>

filter

filter简介

<filter>可认为是<servlet>的一种“加强版”,主要用于对用户请求request进行预处理,也可以对Response进行后处理,是个典型的处理链。使用Filter的完整流程是:Filter对用户请求进行预处理,接着将请求HttpServletRequest交给Servlet进行处理并生成响应,最后Filter再对服务器响应HttpServletResponse进行后处理。Filter与Servlet具有完全相同的生命周期,且Filter也可以通过<init-param>来配置初始化参数,获取Filter的初始化参数则使用FilterConfig的getInitParameter()。

换种说法,Servlet里有request和response两个对象,Filter能够在一个request到达Servlet之前预处理request,也可以在离开Servlet时处理response,Filter其实是一个Servlet链。

以下是Filter的一些常见应用场合:
(1)认证Filter
(2)日志和审核Filter
(3)图片转换Filter
(4)数据压缩Filter
(5)密码Filter
(6)令牌Filter
(7)触发资源访问事件的Filter
(8)XSLT Filter
(9)媒体类型链Filter

filter与servlet的区别
Filter和servlet都可以对URL进行处理,Filter是一个链式处理,只要你想继续处理就可以传递下去;而Servlet则是一次处理并返回!适合简单逻辑处理。
filter就像”递归”,在web.xml配置中的顺序代表了filter的调用流程,而servlet被调用后不会继续调用其他的servlet!因此配置中的顺序不影响!

创建filter
Filter可负责拦截多个请求或响应;一个请求或响应也可被多个Filter拦截。创建一个Filter只需两步:

  • 创建Filter处理类
  • Web.xml文件中配置<filter>

filter配置

Filter可认为是Servlet的“增强版”,因此Filter配置与Servlet的配置非常相似,需要配置两部分:配置Filter名称和Filter拦截器URL模式。区别在于Servlet通常只配置一个URL,而Filter可以同时配置多个请求的URL。

配置Filter有两种方式:

  • 在Filter类中通过Annotation进行配置。
  • 在web.xml文件中通过配置文件进行配置。

常用的是web.xml这种配置方式,下面介绍相关配置元素。

filter

<filter>用于指定Web容器中的过滤器,可包含filter-name、filter-class、init-param、icon、display-name、description子元素。

  • <filter-name>,用来定义过滤器的名称,该名称在整个程序中都必须唯一。
  • <filter-class>,元素指定过滤器类的完全限定的名称,即Filter的实现类。
  • <init-param>,为Filter配置参数,与<context-param>具有相同的元素描述符<param-name><param-value>

filter-mapping

<filter-mapping> 元素用来声明Web应用中的过滤器映射,过滤器被映射到一个servlet或一个URL模式。这个过滤器的<filter><filter-mapping>必须具有相同的<filter-name>,指定该Filter所拦截的URL。过滤是按照部署描述符的<filter-mapping>出现的顺序执行的。
一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。

filter顺序
与filter相关的一个配置节是filter-mapping,这里一定要注意,对于拥有相同filter-name的filter和filter-mapping配置节而言,filter-mapping 必须出现在 filter 之后,否则当解析到 filter-mapping 时,它所对应的 filter-name 还未定义。
web容器启动时初始化每个filter时,是按照filter配置节出现的顺序来初始化的,当请求资源匹配多个filter-mapping时,filter拦截资源是按照filter-mapping配置节出现的顺序来依次调用doFilter()方法的。

url-pattern

/* 所有资源
*.html 以html结尾的资源
/fold/* 指定目录
/abc.html 指定文件
/开头和以/*结尾的是用来做路径映射的,
以前缀*.开头的是用来做扩展映射的。

为什么定义/*.action样一个看起来很正常的匹配会错?
因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断。

filter配置示例,IP白名单

    <!-- ip白名单过滤 -->
    <filter>
        <filter-name>whiteIpFilter</filter-name>
        <filter-class>com.masikkk.service.filter.IpFilter</filter-class>
        <init-param>
            <param-name>whiteIps</param-name>
            <param-value>172.27.45.31,172.27.45.32,127.0.0.1</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>whiteIpFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

filter处理类

Filter处理类必须实现javax.servlet.Filter接口,在该接口中定义了三个方法:

  • void init(FilterConfig config):用于完成Filter的初始化。FilteConfig用于访问Filter的配置信息。利用FilterConfig类的getInitParameter(“param-name”)方法获取配置。
  • void destroy():用于Filter销毁前,完成某些资源的回收。
  • void doFilter(ServletRequest request,ServletResponse response,FilterChain chain):实现过滤功能的核心方法,该方法就是对每个请求及响应增加额外的处理。该方法实现对用户请求request进行预处理,也可以实现对服务器响应response进行后处理——它们的分界线为是否调用了chain.doFilter(request,response),执行该方法之前,即对用户请求request进行预处理,执行该方法之后,即对服务器响应response进行后处理。chain.doFilter(…)方法用于将request,response交给下一个filter。

filter处理类示例,IP白名单过滤

public class IpFilter implements Filter {

    private Logger logger = LoggerFactory.getLogger("rootLogger");

    String[] whiteIps = null;

    public void destroy() {
    }

    /**
     * 检查请求ip是否在IP白名单中
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest)request;
        String reqPath = req.getPathInfo();
        if(null == reqPath) {
            reqPath = "/";
        }
        logger.info("ReqPath: "+reqPath);

        String clientIp = getClientRealIp(req);
        logger.info("ClientIP: "+clientIp);

        boolean clientIPisWhite = false;
        if(null != reqPath && null != clientIp) {
            if(null != whiteIps) {
                for(int i=0; i<whiteIps.length; i++) {
                    if(whiteIps[i].equals(clientIp)) {
                        clientIPisWhite = true;
                        break;
                    }
                }
            }
        }

        if(clientIPisWhite) {
            logger.info("Allowed! IP: "+clientIp+" To: "+reqPath);
            filterChain.doFilter(request, response);
        } else {
            logger.info("Forbidden! IP: "+clientIp+" To: "+reqPath);
            ((HttpServletResponse)response).sendError(HttpStatus.FORBIDDEN.value());
            return;
        }

    }

    /**
     * 获取web.xml中的filter配置参数,ip白名单
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        String initParameter = filterConfig.getInitParameter("whiteIps");
        logger.info("whiteIps: " + initParameter);
        if(null != initParameter) {
            whiteIps = initParameter.split(",");
        }
    }

    /**
     * 获取ip地址
     */
    private String getClientRealIp(HttpServletRequest request){

        StringBuffer sb = new StringBuffer();
        sb.append("x-forwarded-for:").append(request.getHeader("x-forwarded-for")).append(",");
        sb.append("Proxy-Client-IP:").append(request.getHeader("Proxy-Client-IP")).append(",");
        sb.append("WL-Proxy-Client-IP:").append(request.getHeader("WL-Proxy-Client-IP")).append(",");
        sb.append("RemoteAddr:").append(request.getRemoteAddr()).append(".");
        logger.info(sb.toString());
        String ip = request.getHeader("x-forwarded-for");
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        String realIp = "";
        try {
            if(ip!=null){
                realIp = ip.split(",")[0];
            }
        } catch (Exception e) {
        }
        return realIp;
    }
}

servlet

servlet介绍

Servlet通常称为服务器端小程序,是运行在服务器端的程序,用于处理及响应客户的请求。Servlet是个特殊的java类,继承于HttpServlet。客户端通常只有GET和POST两种请求方式,Servlet为了响应则两种请求,必须重写doGet()和doPost()方法。大部分时候,Servlet对于所有的请求响应都是完全一样的,此时只需要重写service()方法即可响应客户端的所有请求。
另外HttpServlet有两个方法:
init(ServletConfig config):创建Servlet实例时,调用该方法的初始化Servlet资源。
destroy():销毁Servlet实例时,自动调用该方法的回收资源。
通常无需重写init()和destroy()两个方法,除非需要在初始化Servlet时,完成某些资源初始化的方法,才考虑重写init()方法,如果重写了init()方法,应在重写该方法的第一行调用super.init(config),该方法将调用HttpServlet的init()方法。如果需要在销毁Servlet之前,先完成某些资源的回收,比如关闭数据库连接,才需要重写destory方法()。

创建Servlet实例有两个时机

  • 客户端第一次请求某个Servlet时,系统创建该Servlet的实例,大部分Servlet都是这种Servlet。
  • Web应用启动时立即创建Servlet实例,即load-on-start Servlet。

加载Servlet的过程:
容器的Context对象对请求路径(URL)做出处理,去掉请求URL的上下文路径后,按路径映射规则和Servlet映射路径(url- pattern)做匹配,如果匹配成功,则调用这个Servlet处理请求。

每个Servlet的运行都遵循如下生命周期

  • 创建Servlet实例。
  • Web容器调用Servlet的init()方法,对Servlet进行初始化。
  • Servlet初始化后,将一直存在于容器中,用于响应客户端请求,如果客户端发送GET请求,容器调用Servlet的doGet()方法处理并响应请求;如果客户端发送POST请求,容器调用Servlet的doPost()方法处理并响应请求。或者统一使用service()方法处理来响应用户请求。
  • Web容器决定销毁Servlet时,先调用Servlet的destory()方法,通常在关闭Web应用时销毁Servlet实例。

servlet配置

为了让Servlet能响应用户请求,还必须将Servlet配置在web应用中,配置Servlet需要修改web.xml文件。
从Servlet3.0开始,配置Servlet有两种方式:

  • 在Servlet类中使用@WebServlet Annotation进行配置。
  • 在web.xml文件中进行配置。

用web.xml文件来配置Servlet,需要配置<servlet><servlet-mapping>

servlet

<servlet>用来声明一个Servlet。icon、display-name和description元素的用法和filter的用法相同。init-param元素与context-param元素具有相同的元素描述符,可以使用init-param子元素将初始化参数名和参数值传递给Servlet,访问Servlet配置参数通过ServletConfig对象来完成,ServletConfig提供方法ServletConfig.getInitParameter(“param-name”)来获取初始化参数。ServletConfig获取配置参数的方法和ServletContext获取配置参数的方法完全一样,只是ServletConfig是取得当前Servlet的配置参数,而ServletContext是获取整个Web应用的配置参数。
servlet必须含有servlet-name和servlet-class,或者servlet-name和jsp-file

  • <servlet-name>,用来定义servlet的名称,该名称在整个应用中必须是惟一的
  • `,用来指定servlet的完全限定的名称。
  • <jsp-file>,用来指定应用中JSP文件的完整路径。这个完整路径必须由/开始。
  • <init-param>,为servlet配置参数,与<context-param>具有相同的元素描述符<param-name><param-value>
  • <description>,为Servlet指定一个文本描述。
  • <display-name>,为Servlet提供一个简短的名字被某些工具显示。
  • <icon>,为Servlet指定一个图标,在图形管理工具中表示该Servlet。
  • <load-on-startup>,如果load-on-startup元素存在,而且也指定了jsp-file元素,则JSP文件会被重新编译成Servlet,同时产生的Servlet也被载入内存。load-on-startup的内容可以为空,或者是一个整数。这个值表示由Web容器载入内存的顺序。举个例子:如果有两个Servlet元素都含有load-on-startup子元素,则load-on-startup子元素值较小的Servlet将先被加载。如果load-on-startup子元素值为空或负值,则由Web容器决定什么时候加载Servlet。如果两个Servlet的load-on-startup子元素值相同,则由Web容器决定先加载哪一个Servlet。<load-on-startup>1</load-on-startup>表示启动容器时,初始化Servlet。

servlet-mapping

<servlet-mapping>含有servlet-name和url-pattern

  • <servlet-name>,Servlet的名字,唯一性和一致性,与<servlet>元素中声明的名字一致。
  • <url-pattern>,指定相对于Servlet的URL的路径。该路径相对于web应用程序上下文的根路径。servlet-mapping将URL模式映射到某个Servlet,即该Servlet处理的URL。

distributable

<distributable/>告诉servlet/JSP容器,Web容器中部署的应用程序适合在分布式环境下运行。


session-config

<session-config> 用于设置容器的session参数,比如:<session-timeout>用于指定http session的失效时间。默认时间设置在jakarta/conf/web.xml (30 minutes)。session-timeout用来指定默认的会话超时时间间隔,以分钟为单位。该元素值必须为整数。如果 session-timeout元素的值为零或负数,则表示会话将永远不会超时。

<!-- Set timeout to 120 minutes -->
<session-config>
    <session-timeout>120</session-timeout>
</session-config>

参考


上一篇 VPS

下一篇 Hexo博客(08)Hexo主题分析

阅读
评论
4.8k
阅读预计19分钟
创建日期 2016-06-15
修改日期 2018-06-11
类别
标签

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论