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>
参考
Web.xml详解(介绍的很详细,很全面) http://blog.csdn.net/believejava/article/details/43229361
web.xml配置详解之filter http://blog.csdn.net/liaoxiaohua1981/article/details/6761053
Spring框架之Filter应用 http://blog.csdn.net/whuslei/article/details/8134848
使用Filter过滤ip禁止访问系统 http://blog.csdn.net/chenghui0317/article/details/9822981