webxml_web项目连接数据库 - CSDN
精华内容
参与话题
  • web.xml文件是什么?有什么用?--详解

    万次阅读 多人点赞 2018-10-08 10:41:31
    前言:一般的web工程中都会用到web.xml,web.xml主要用来配置,可以方便的开发web工程。web.xml主要用来配置Filter、Listener、Servlet等。但是要说明的是web.xml并不是必须的,一个web工程可以没有web.xml文件。...

     前言:一般的web工程中都会用到web.xml,web.xml主要用来配置,可以方便的开发web工程。web.xml主要用来配置Filter、Listener、Servlet等。但是要说明的是web.xml并不是必须的,一个web工程可以没有web.xml文件。

    1、WEB工程加载web.xml过程

      经过个人测试,WEB工程加载顺序与元素节点在文件中的配置顺序无关。即不会因为 filter 写在 listener 的前面而会先加载 filter。WEB容器的加载顺序是:ServletContext -> context-param -> listener -> filter -> servlet。并且这些元素可以配置在文件中的任意位置。

      加载过程顺序如下:

    1. 启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点。 
    2. 紧急着,容创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文。 
    3. 容器将<context-param>转换为键值对,并交给servletContext。 
    4. 容器创建<listener>中的类实例,创建监听器。 

    2、web.xml文件元素详解

      1、schema

      web.xml的模式文件是由Sun公司定义的,每个web.xml文件的根元素<web-app>中,都必须标明这个 web.xml使用的是哪个模式文件。其它的元素都放在<web-app></web-app>之中。

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4" 
        xmlns="http://java.sun.com/xml/ns/j2ee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
            http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    </web-app>

      2、<icon>Web应用图标

      指出IDE和GUI工具用来表示Web应用的大图标和小图标。

    <icon>
        <small-icon>/images/app_small.gif</small-icon>
        <large-icon>/images/app_large.gif</large-icon>
    </icon>

      3、<display-name>Web应用名称

      提供GUI工具可能会用来标记这个特定的Web应用的一个名称

    <display-name>Tomcat Example</display-name>

      4、<disciption>Web应用描述

      给出于此相关的说明性文本

    <disciption>Tomcat Example servlets and JSP pages.</disciption>

      5、<context-param>上下文参数

      声明应用范围内的初始化参数。它用于向 ServletContext提供键值对,即应用程序上下文信息。我们的listener, filter等在初始化时会用到这些上下文中的信息。在servlet里面可以通过getServletContext().getInitParameter("context/param")得到。

    <context-param>
        <param-name>ContextParameter</para-name>
        <param-value>test</param-value>
        <description>It is a test parameter.</description>
    </context-param>

      6、<filter>过滤器

      将一个名字与一个实现javaxs.servlet.Filter接口的类相关联。

    <filter>
        <filter-name>setCharacterEncoding</filter-name>
        <filter-class>com.myTest.setCharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>setCharacterEncoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

      7、<listener>监听器

    <listener> 
        <listerner-class>com.listener.SessionListener</listener-class> 
    </listener>

      8、<servlet>

      <servlet></servlet> 用来声明一个servlet的数据,主要有以下子元素:

    • <servlet-name></servlet-name> 指定servlet的名称
    • <servlet-class></servlet-class> 指定servlet的类名称
    • <jsp-file></jsp-file> 指定web站台中的某个JSP网页的完整路径
    • <init-param></init-param> 用来定义参数,可有多个init-param。在servlet类中通过getInitParamenter(String name)方法访问初始化参数
    • <load-on-startup></load-on-startup>指定当Web应用启动时,装载Servlet的次序。当值为正数或零时:Servlet容器先加载数值小的servlet,再依次加载其他数值大的servlet。当值为负或未定义:Servlet容器将在Web客户首次访问这个servlet时加载它。
    • <servlet-mapping></servlet-mapping> 用来定义servlet所对应的URL,包含两个子元素
    • <servlet-name></servlet-name> 指定servlet的名称
    • <url-pattern></url-pattern> 指定servlet所对应的URL
    <!-- 基本配置 -->
    <servlet>
        <servlet-name>snoop</servlet-name>
        <servlet-class>SnoopServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>snoop</servlet-name>
        <url-pattern>/snoop</url-pattern>
    </servlet-mapping>
    <!-- 高级配置 -->
    <servlet>
        <servlet-name>snoop</servlet-name>
        <servlet-class>SnoopServlet</servlet-class>
        <init-param>
            <param-name>foo</param-name>
            <param-value>bar</param-value>
        </init-param>
        <run-as>
            <description>Security role for anonymous access</description>
            <role-name>tomcat</role-name>
        </run-as>
    </servlet>
    <servlet-mapping>
        <servlet-name>snoop</servlet-name>
        <url-pattern>/snoop</url-pattern>
    </servlet-mapping>

      9、<session-config>会话超时配置

      单位为分钟。

    <session-config>
        <session-timeout>120</session-timeout>
    </session-config>

      10、<mime-mapping>

    <mime-mapping>
        <extension>htm</extension>
        <mime-type>text/html</mime-type>
    </mime-mapping>

      11、<welcome-file-list>欢迎文件页

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
    </welcome-file-list>

      12、<error-page>错误页面

    <!-- 1、通过错误码来配置error-page。当系统发生×××错误时,跳转到错误处理页面。 -->
    <error-page>
        <error-code>404</error-code>
        <location>/NotFound.jsp</location>
    </error-page>
    <!-- 2、通过异常的类型配置error-page。当系统发生java.lang.NullException(即空指针异常)时,跳转到错误处理页面。 -->
    <error-page>
        <exception-type>java.lang.NullException</exception-type>
        <location>/error.jsp</location>
    </error-page>

      13、<jsp-config>设置jsp

      <jsp-config> 包括 <taglib> 和 <jsp-property-group> 两个子元素。其中<taglib> 元素在JSP 1.2 时就已经存在;而<jsp-property-group> 是JSP 2.0 新增的元素。

      <jsp-property-group> 元素主要有八个子元素,它们分别为:

    • <description>:设定的说明 
    • <display-name>:设定名称 
    • <url-pattern>:设定值所影响的范围,如: /CH2 或 /*.jsp
    • <el-ignored>:若为 true,表示不支持 EL 语法 
    • <scripting-invalid>:若为 true,表示不支持 <% scripting %>语法 
    • <page-encoding>:设定 JSP 网页的编码 
    • <include-prelude>:设置 JSP 网页的抬头,扩展名为 .jspf
    • <include-coda>:设置 JSP 网页的结尾,扩展名为 .jspf
    <jsp-config>
        <taglib>
            <taglib-uri>Taglib</taglib-uri>
            <taglib-location>/WEB-INF/tlds/MyTaglib.tld</taglib-location>
        </taglib>
        <jsp-property-group>
            <description>Special property group for JSP Configuration JSP example.</description>
            <display-name>JSPConfiguration</display-name>
            <url-pattern>/jsp/* </url-pattern>
            <el-ignored>true</el-ignored>
            <page-encoding>GB2312</page-encoding>
            <scripting-invalid>true</scripting-invalid>
            <include-prelude>/include/prelude.jspf</include-prelude>
            <include-coda>/include/coda.jspf</include-coda>
        </jsp-property-group>
    </jsp-config>

      对于Web 应用程式来说,Scriptlet 是个不乐意被见到的东西,因为它会使得HTML 与Java 程式码交相混杂,对于程式的维护来说相当的麻烦,必要的时候,可以在web.xml 中加上<script-invalid> 标签,设定所有的JSP 网页都不可以使用Scriptlet。

    3、Mapping规则

      当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/test/aaa.html,我的应用上下文是test,容器会将http://localhost/test去掉,剩下的/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了。

      其匹配规则和顺序如下:

    1. 精确路径匹配。例子:比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为http://localhost/test ,这个时候容器就会先 进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。
    2. 最长路径匹配。例子:servletA的url-pattern为/test/*,而servletB的url-pattern为/test/a/*,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。
    3. 扩展匹配,如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action

      以”/’开头和以”/*”结尾的是用来做路径映射的。以前缀”*.”开头的是用来做扩展映射的。所以,为什么定义”/*.action”这样一个看起来很正常的匹配会错?因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断。

    展开全文
  • 更改web.xml默认欢迎页面。

    万次阅读 2016-08-19 18:04:58
    转载文章,参考地址如下: ... ... 我给更改为: ... /pages/template/login.jsp ...某个路径下的jsp文件,但是,想在jsp中使用后台查询的参数,并且想先执行方法,再加载jsp,于是找到对应的文章,链接:最上方。...

    转载文章,参考地址如下:

    http://simen-net.iteye.com/blog/1489928

    默认web.xml欢迎页面为index.html

    我给更改为:

    <welcome-file-list>
    	<welcome-file>/pages/template/login.jsp</welcome-file>
    </welcome-file-list>

    某个路径下的jsp文件,但是,想在jsp中使用后台查询的参数,并且想先执行方法,再加载jsp,于是找到对应的文章,链接:最上方。

    方法是,

    welcome-file-list可以转向到servlet,但是!!!前提是servlet不能有扩展名,否则就当成静态文件处理了

    于是配置如下:

    <welcome-file-list>
    	<welcome-file>index</welcome-file>
    </welcome-file-list>
    

    也需要对这个请求进行拦截,我是单独写了一个springmvc 来对这个请求进行拦截,没有测试过不写行不行。马上下班了,太饿了!!!

    <servlet>
    		<servlet-name>springmvc</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<init-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>
    				/WEB-INF/classes/springmvc-config.xml
    			</param-value>
    		</init-param>
    		<load-on-startup>2</load-on-startup>
    	</servlet>
    	<servlet-mapping>
    		<servlet-name>springmvc</servlet-name>
    		<url-pattern>/index</url-pattern>
    	</servlet-mapping>

    成功跳转。


    展开全文
  • Web.xml详解

    万次阅读 多人点赞 2019-04-04 16:05:16
    这篇文章主要是综合网上关于web.xml的一些介绍,希望对大家有所帮助,也欢迎大家一起讨论。 ---题记 1.web.xml加载过程(步骤) 首先简单讲一下,web.xml的加载过程。当启动一个WEB项目时,容器包括(JBoss、...

    这篇文章主要是综合网上关于web.xml的一些介绍,希望对大家有所帮助,也欢迎大家一起讨论。 ---题记

    1.web.xml加载过程(步骤)

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

    1. 启动WEB项目的时候,容器首先会去它的配置文件web.xml读取两个节点:  <listener></listener>和<context-param></context-param>。
    2. 紧接着,容器创建一个ServletContext(Application),这个WEB项目所有部分都将共享这个上下文。
    3. 容器以<context-param></context-param>的name作为键,value作为值,将其转化为键值对,存入ServletContext。
    4. 容器创建<listener></listener>中的类实例,根据配置的class类路径<listener-class>来创建监听,在监听中会有contextInitialized(ServletContextEvent args)初始化方法,启动Web应用时,系统调用Listener的该方法,在这个方法中获得:ServletContext application = ServletContextEvent.getServletContext(); context-param的值application.getInitParameter("context-param的键"); 得到这个context-param的值之后,你就可以做一些操作了。
    5. 举例:你可能想在项目启动之前就打开数据库,那么这里就可以在<context-param>中设置数据库的连接方式(驱动、url、user、password),在监听类中初始化数据库的连接。这个监听是自己写的一个类,除了初始化方法,它还有销毁方法,用于关闭应用前释放资源。比如:说数据库连接的关闭,此时,调用contextDestroyed(ServletContextEvent args),关闭Web应用时,系统调用Listener的该方法。
    6. 接着,容器会读取<filter></filter>,根据指定的类路径来实例化过滤器。
    7. 以上都是在WEB项目还没有完全启动起来的时候就已经完成了的工作。如果系统中有Servlet,则Servlet是在第一次发起请求的时候被实例化的,而且一般不会被容器销毁,它可以服务于多个用户的请求。所以,Servlet的初始化都要比上面提到的那几个要迟。
    8. 总的来说,web.xml的加载顺序是:<context-param>-><listener>-><filter>-><servlet>。其中,如果web.xml中出现了相同的元素,则按照在配置文件中出现的先后顺序来加载。
    9. 对于某类元素而言,与它们出现的顺序是有关的。以<filter>为例,web.xml中当然可以定义多个<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()方法的。<servlet>同<filter>类似,此处不再赘述。

    2.web.xml标签详解

    1. XML文档有效性检查

    <!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" >

    这段代码指定文件类型定义(DTD),可以通过它检查XML文档的有效性。下面显示的<!DOCTYPE>元素有几个特性,这些特性告诉我们关于DTD的信息: 

    • web-app定义该文档(部署描述符,不是DTD文件)的根元素 
    • PUBLIC意味着DTD文件可以被公开使用 
    • -//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”意味着DTD由Sun Microsystems, Inc.维护。该信息也表示它描述的文档类型是DTD Web Application 2.3,而且DTD是用英文书写的。 
    • URL“http://java.sun.com/dtd/web-app_2_3.dtd”表示D文件的位置。

    2. <web-app></web-app>

    部署描述符的根元素是<web-app>,DTD文件规定<web-app>元素的子元素的语法如下:

    <!ELEMENT web-app (icon?, display-name?, description?,distributable?, context-param*, filter*, filter-mapping*,listener*, servlet*, servlet-mapping*, session-config?,mime-mapping*, welcome-file-list?,error-page*, taglib*, resource-env-ref*, resource-ref*,security-constraint*, login-config?, security-role*,env-entry*,ejb-ref*, ejb-local-ref*)> 

    正如您所看到的,这个元素含有23个子元素,而且子元素都是可选的。问号(?)表示子元素是可选的,而且只能出现一次。星号(*)表示子元素可在部署描述符中出现零次或多次。有些子元素还可以有它们自己的子元素。web.xml文件中<web-app>元素声明的是下面每个子元素的声明。下面讲述部署描述符中可能包含的所有子元素。

    注意:在Servlet 2.3中,子元素必须按照DTD文件语法描述中指定的顺序出现。比如:如果部署描述符中的<web-app>元素有<servlet>和<servlet-mapping>两个子元素,则<servlet>子元素必须出现在<servlet-mapping>子元素之前。在Servlet2.4中,顺序并不重要。

    3. <display-name></display-name>

    <display-name>test-hwp-web-application</display-name>定义了web应用的名称,可以在http://localhost:8080/manager/html中显示。如下所示:

    4. <distributable/>

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

    5. <context-param></context-param>

    使用上下文初始化参数

    <!--****************************上下文初始化参数***************************-->
    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>business.root</param-value>
    </context-param>
    <!-- spring config -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-configuration/*.xml</param-value>
    </context-param>

    5.1<context-param>解释:

    <context-param>元素含有一对参数名和参数值,用作应用的Servlet上下文初始化参数,参数名在整个Web应用中必须是惟一的,web应用的整个生命周期中上下文初始化参数都存在,任意的Servletjsp都可以随时随地访问它。

    • <param-name>:参数名
    • <param-value>:参数值。作为选择,可用<description>来描述参数。

    5.2 什么情况下使用,为什么使用<context-param>:

    比如定义一个管理员email地址用来从程序发送错误,或者与你整个应用程序有关的其他设置。使用自己定义的设置文件需要额外的代码和管理;直接在你的程序中使用硬编码(Hard-coding)参数值会给你之后修改程序带来麻烦,更困难的是,要根据不同的部署使用不同的设置;通过这种办法,可以让其他开发人员更容易找到相关的参数,因为它是一个用于设置这种参数的标准位置。

    5.3 Spring配置文件:

    配置Spring,必须需要<listener>,而<context-param>可有可无,如果在web.xml中不写<context-param>配置信息,默认的路径是/WEB-INF/applicationContext.xml,在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:在<param-value></param-value>里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔,比如在business-client工程中,我们采用了自定义配置方式,<context-param>配置如下:

    <!-- spring config -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-configuration/*.xml</param-value>
    </context-param>
    <listener>
         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    对应工程目录结构如下所示:

    部署在同一容器中的多个Web项目,要配置不同的webAppRootKey,web.xml文件中最好定义webAppRootKey参数,如果不定义,将会缺省为“webapp.root”,如下:

    <!-- 应用路径  -->  
    <context-param>  
        <param-name>webAppRootKey</param-name>  
        <param-value>webapp.root</param-value>  
    </context-param> 

    当然也不能重复,否则报类似下面的错误:

    Web app root system property already set to different value: 'webapp.root' = [/home/user/tomcat/webapps/project1/] instead of [/home/user/tomcat/webapps/project2/] - Choose unique values for the 'webAppRootKey' context-param in your web.xml files!  

    意思是“webapp.root”这个key已经指向了项目1,不可以再指向项目2。多个项目要对webAppRootKey进行配置,我们工程主要是让log4j能将日志写到对应项目根目录下,比如我们的项目的webAppRootKey为

    <!—business-client应用路径  -->  
    <context-param>  
        <param-name>webAppRootKey</param-name>  
        <param-value>business.root</param-value>  
    </context-param>  
    <!—public-base应用路径  -->  
    <context-param>  
        <param-name>webAppRootKey</param-name>  
        <param-value>pubbase.root</param-value>  
    </context-param>    

    这样就不会出现冲突了。就可以在运行时动态地找到项目路径,在log4j.properties配置文件中可以按下面的方式使用${webapp.root}:log4j.appender.file.File=${webapp.root}/WEB-INF/logs/sample.log,就可以在运行时动态地找出项目的路径。

    5.4 多个配置文件引用处理:

    如果web.xml中有contextConfigLocation参数指定的Spring配置文件则会去加载相应的配置文件,而不会去加载/WEB-INF/下的applicationContext.xml。但是如果没有指定的话,默认会去/WEB-INF/下加载applicationContext.xml。在一个Spring的实际项目中,如果有多个Spring配置文件,多个配置文件可以在web.xml里用空格分隔写入,如:

    <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value> applicationContext-database.xml,applicationContext.xml</param-value>  
    <context-param>

    多个配置文件里的交叉引用可以用ref的external或bean解决,

    <bean id="userService" class="domain.user.service.impl.UserServiceImpl"> 
       <property name="dbbean">
          <ref bean="dbBean"/>
       </property> 
    </bean>

    5.5 在不同环境下如何获取,

    <context-param>
       <param-name>param_name</param-name>
       <param-value>param_value</param-value>
    </context-param>

    此所设定的参数,在JSP网页中可以使用下列方法来取得:${initParam.param_name}

    若在Servlet可以使用下列方法来获得:String param_name=getServletContext().getInitParamter("param_name");Servlet的ServletConfig对象拥有该ServletServletContext的一个引用,所以可这样取得上下文初始化参数:getServletConfig().getServletContext().getInitParameter()也可以在Servlet中直接调用getServletContext().getInitParameter(),两者是等价的。

    6. <session-config></session-config>

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

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

    7. <listener></listener>

    <!--****************************监听器配置*********************************-->
    <!-- Spring的log4j监听器 -->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 与CAS Single Sign Out Filter配合,注销登录信息  --> 
    <listener>
        <listener-class>com.yonyou.mcloud.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
    </listener>

    7.1 Listener介绍:

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

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

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

    7.2 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>

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

    •  采用load-on-startup Servlet 实现:
    <servlet>  
       <servlet-name>context</servlet-narne> 
       <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>  
       <load-on-startup>1</load-on-startup>  
    </servlet> 
    • 利用ServletContextListener实现:
    <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener> 

    我们选择了第二种方式,在J2EE工程中web服务器启动的时候最先调用web.xml,上面这段配置的意思是加载spring的监听器,其中ContextLoaderListener的作用就是启动Web容器时,自动装配applicationContext.xml的配置信息,执行它所实现的方法。

    8. <filter></filter>

    <!--****************************过滤器配置*********************************-->
    <!-- 字符集过滤器 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!-- 单点登出过滤器 -->
    <filter>
        <filter-name>CAS Single Sign Out Filter</filter-name>
        <filter-class>com.yonyou.mcloud.cas.client.session.SingleSignOutFilter</filter-class>
    </filter>
    <!-- 认证过滤器 -->
    <filter>
        <filter-name>CAS Authentication Filter</filter-name>
        <filter-class>com.yonyou.mcloud.cas.client.authentication.ExpandAuthenticationFilter</filter-class>
        <init-param>
            <param-name>casServerLoginUrl</param-name>
            <param-value>https://dev.yonyou.com:443/sso-server/login</param-value>
        </init-param>
        <init-param>
            <!--这里的server是服务端的IP -->
            <param-name>serverName</param-name>
            <param-value>http://10.1.215.40:80</param-value>
        </init-param>
    </filter>
    <!-- 验证ST/PT过滤器 -->
    <filter>
        <filter-name>CAS Validation Filter</filter-name>
        <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
        <init-param>
            <param-name>casServerUrlPrefix</param-name>
            <param-value>https://dev.yonyou.com:443/sso-server</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <param-value>http://10.1.215.40:80</param-value>
        </init-param>
        <init-param>
            <param-name>proxyCallbackUrl</param-name>
            <param-value>https://dev.yonyou.com:443/business/proxyCallback</param-value>
        </init-param>
        <init-param>
            <param-name>proxyReceptorUrl</param-name>
            <param-value>/proxyCallback</param-value>
        </init-param>
        <init-param>
            <param-name>proxyGrantingTicketStorageClass</param-name>
            <param-value>com.yonyou.mcloud.cas.client.proxy.MemcachedBackedProxyGrantingTicketStorageImpl</param-value>
        </init-param>
        <!-- 解决中文问题 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
    </filter>
    <filter>
        <filter-name>CAS Assertion Thread Local Filter</filter-name>
        <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
    </filter>
    <filter>
        <filter-name>NoCache Filter</filter-name>
        <filter-class>com.yonyou.mcloud.cas.client.authentication.NoCacheFilter</filter-class>
    </filter>
    <!--****************************映射关系配置********************************-->
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>NoCache Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS Single Sign Out Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/proxyCallback</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS Authentication Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS Assertion Thread Local Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    8.1 Filter介绍:

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

    换种说法,Servlet里有requestresponse两个对象,Filter能够在一个request到达Servlet之前预处理request,也可以在离开Servlet时处理responseFilter其实是一个Servlet链。以下是Filter的一些常见应用场合,

    • 认证Filter
    • 日志和审核Filter
    • 图片转换Filter
    • 数据压缩Filter
    • 密码Filter
    • 令牌Filter
    • 触发资源访问事件的Filter
    • XSLT Filter
    • 媒体类型链Filter

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

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

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

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

    8.2 Filter配置:

    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>:用来声明Web应用中的过滤器映射,过滤器被映射到一个servlet或一个URL模式。这个过滤器的<filter>和<filter-mapping>必须具有相同的<filter-name>,指定该Filter所拦截的URL。过滤是按照部署描述符的<filter-mapping>出现的顺序执行的。

    8.21 字符集过滤器

     <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    • CharacterEncodingFilter类可以通过简单配置来帮我们实现字符集转换的功能。
    • 参数encoding用于指定编码类型,参数forceEncoding设为true时,强制执行request.setCharacterEncoding(this.encoding)和reponse.setCharacterEncoding(this.encoding)中的方法。

    8.22 缓存控制

    <filter>
        <filter-name>NoCache Filter</filter-name>
        <filter-class>com.yonyou.mcloud.cas.client.authentication.NoCacheFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>NoCache Filter</filter-name>
        <!—表示对URL全部过滤-->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    8.23 登录认证

    <!-- 认证过滤器 -->
    <filter>
        <filter-name>CAS Authentication Filter</filter-name>
        <filter-class>com.yonyou.mcloud.cas.client.authentication.ExpandAuthenticationFilter</filter-class>
        <init-param>
            <param-name>casServerLoginUrl</param-name>
            <param-value>https://dev.yonyou.com:443/sso-server/login</param-value>
        </init-param>
        <init-param>
            <!--这里的server是服务端的IP -->
            <param-name>serverName</param-name>
            <param-value>http://10.1.215.40:80</param-value>
        </init-param>
    </filter>
    <filter-mapping>
         <filter-name>CAS Authentication Filter</filter-name>
         <url-pattern>/*</url-pattern>
    </filter-mapping>

    登录认证,未登录用户导向CAS Server进行认证。

    8.24 单点登出

    <filter>
          <filter-name>CAS Single Sign Out Filter</filter-name>
          <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
    </filter>
    <filter-mapping>
          <filter-name>CAS Single Sign Out Filter</filter-name>
          <url-pattern>/*</url-pattern>
    </filter-mapping>
    <listener>
          <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
    </listener>

    CAS Server通知CAS Client,删除session,注销登录信息。

    8.25 封装request

    <filter>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    封装request, 支持getUserPrincipal等方法。

    8.26 存放Assertion到ThreadLocal中

    <filter>
        <filter-name>CAS Assertion Thread Local Filter</filter-name>
        <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CAS Assertion Thread Local Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    8.27 禁用浏览器缓存

    <filter>
        <filter-name>NoCache Filter</filter-name>
        <filter-class>com.yonyou.mcloud.cas.client.authentication.NoCacheFilter</filter-class>
     </filter>
     <filter-mapping>
        <filter-name>NoCache Filter</filter-name>
        <url-pattern>/*</url-pattern>
     </filter-mapping>

    8.28 CAS Client向CAS Server进行ticket验证

    <!-- 验证ST/PT过滤器 -->
    <filter>
        <filter-name>CAS Validation Filter</filter-name>
        <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
        <init-param>
           <param-name>casServerUrlPrefix</param-name>
           <param-value>https://dev.yonyou.com:443/sso-server</param-value>
        </init-param>
        <init-param>
           <param-name>serverName</param-name>
           <param-value>http://10.1.215.40:80</param-value>
        </init-param>
        <init-param>
           <param-name>proxyCallbackUrl</param-name>
           <param-value>https://dev.yonyou.com:443/business/proxyCallback</param-value>
        </init-param>
        <init-param>
           <param-name>proxyReceptorUrl</param-name>
           <param-value>/proxyCallback</param-value>
        </init-param>
        <init-param>
            <param-name>proxyGrantingTicketStorageClass</param-name>
            <param-value>com.yonyou.mcloud.cas.client.proxy.MemcachedBackedProxyGrantingTicketStorageImpl</param-value>
        </init-param>
        <!-- 解决中文问题 -->
        <init-param>
           <param-name>encoding</param-name>
           <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/proxyCallback</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    9. <servlet></servlet>

    <!--****************************servlet配置******************************-->
    <!-- Spring view分发器  对所有的请求都由business对应的类来控制转发 -->
    <servlet>
        <servlet-name>business</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>publishContext</param-name>
          <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 用户登出 -->
    <servlet>
        <servlet-name>LogOutServlet</servlet-name>
        <servlet-class>com.yonyou.mcloud.cas.web.servlet.LogOutServlet</servlet-class>
        <init-param>
          <param-name>serverLogoutUrl</param-name>
          <param-value>https://dev.yonyou.com:443/sso-server/logout</param-value>
        </init-param>
        <init-param>
          <param-name>serverName</param-name>
          <param-value>http://10.1.215.40:80/business/</param-value>
        </init-param>
    </servlet>
    <!--****************************servlet映射关系配置*************************-->
    <servlet-mapping>
        <servlet-name>LogOutServlet</servlet-name>
        <url-pattern>/logout</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>business</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    9.1 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都是这种Servlet。
    • Web应用启动时立即创建Servlet实例,即load-on-start Servlet。

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

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

    9.2 Servlet配置:

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

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

    我们用web.xml文件来配置Servlet,需要配置<servlet>和<servlet-mapping>。<servlet>用来声明一个Servlet。<icon>、<display-name>和<description>元素的用法和<filter>的用法相同。<init-param>元素与<context-param>元素具有相同的元素描述符,可以使用<init-param>子元素将初始化参数名和参数值传递给Servlet,访问Servlet配置参数通过ServletConfig对象来完成,ServletConfig提供如下方法:java.lang.String.getInitParameter(java.lang.String name),用于获取初始化参数。ServletConfig获取配置参数的方法和ServletContext获取配置参数的方法完全一样,只是ServletConfig是取得当前Servlet的配置参数,而ServletContext是获取整个Web应用的配置参数。

    9.2.1 <description>、<display-name>和<icon>

    • <description>:为Servlet指定一个文本描述。
    • <display-name>:为Servlet提供一个简短的名字被某些工具显示。
    • <icon>:为Servlet指定一个图标,在图形管理工具中表示该Servlet。

    9.2.2 <servlet-name>、<servlet-class>和<jsp-file>元素

    <servlet>必须含有<servlet-name>和<servlet-class>,或者<servlet-name>和<jsp-file>。 描述如下:

    • <servlet-name>:用来定义servlet的名称,该名称在整个应用中必须是惟一的。
    • <servlet-class>:用来指定servlet的完全限定的名称。
    • <jsp-file>:用来指定应用中JSP文件的完整路径。这个完整路径必须由/开始。

    9.2.3 <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。

    9.2.4 <servlet-mapping>

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

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

    9.2.5 加载Servlet的过程 

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

    9.3 DispatcherServlet在web.xml中的配置:

    <!-- Spring view分发器  对所有的请求都由business对应的类来控制转发 -->
    <servlet>
        <servlet-name>business</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>publishContext</param-name>
          <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    配置Spring MVC,指定处理请求的Servlet,有两种方式:

    • 默认查找MVC配置文件的地址是:/WEB-INF/${servletName}-servlet.xml
    • 可以通过配置修改MVC配置文件的位置,需要在配置DispatcherServlet时指定MVC配置文件的位置。

    我们在平台项目两个工程中分别使用了不同的配置方式,介绍如下:

    • 在business-client工程中按照默认方式查找MVC的配置文件,配置文件目录为: /WEB-INF/business-servlet.xml。工程目录结构如下所示:

     

    • 在public-base-server工程中,通过第2种方式进行配置,把spring-servlet.xml放到src/main/resources/config/spring-servlet.xml,则需要在配置DispatcherServlet时指定<init-param>标签。具体代码如下:
    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
          	    <param-name>publishContext</param-name>
          	    <param-value>false</param-value>
            </init-param>
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>classpath:config/spring-servlet.xml</param-value>  
        </init-param> 
        <load-on-startup>1</load-on-startup>
    </servlet>

    工程目录结构如下:

    其中,classpath是web项目的类路径,可以理解为classes下面。因为无论这些配置文件放在哪,编译之后如果没有特殊情况的话都直接在classes下面。jar包的话虽然放在lib文件夹里,但实际上那些类可以直接引用,比如:com.test.ABC,仿佛也在classes下面一样。

    在我们的工程里,经过验证,maven工程这两个路径经过编译后生成的文件都位于classes目录下,即这两个路径相当于类路径,在下面创建config文件夹(folder),然后创建自定义的xml配置文件即可。

    classpath和classpath*区别:同名资源存在时,classpath只从第一个符合条件的classpath中加载资源,而classpath*会从所有的classpath中加载符合条件的资源。classpath*,需要遍历所有的classpath,效率肯定比不上classpath,因此在项目设计的初期就尽量规划好资源文件所在的路径,避免使用classpath*来加载。

    9.4 ContextLoaderListener和DispatcherServlet初始化上下文关系和区别:

    从上图可以看出,ContextLoaderListener初始化的上下文加载的Bean是对于整个应用程序共享的,一般如:DAO层、Service层Bean;DispatcherServlet初始化的上下文加载的Bean是只对Spring MVC有效的Bean,如:Controller、HandlerMapping、HandlerAdapter等,该初始化上下文只加载Web相关组件。

    注意:用户可以配置多个DispatcherServlet来分别处理不同的url请求,每个DispatcherServlet上下文都对应一个自己的子Spring容器,他们都拥有相同的父Spring容器(业务层,持久(dao)bean所在的容器)。

    10. <welcome-file-list></welcome-file-list>

    <!-- welcome page -->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <welcome-file-list>包含一个子元素<welcome-file>,<welcome-file>用来指定首页文件名称。<welcome-file-list>元素可以包含一个或多个<welcome-file>子元素。如果在第一个<welcome-file>元素中没有找到指定的文件,Web容器就会尝试显示第二个,以此类推。

    11. 参考文献:

    http://wiki.metawerx.net/wiki/Web.xml
    http://www.cnblogs.com/konbluesky/articles/1925295.html
    http://blog.csdn.net/sapphire_aling/article/details/6069764
    http://blog.csdn.net/zndxlxm/article/details/8711626
    http://blog.csdn.net/zhangliao613/article/details/6289114
    http://www.cnblogs.com/bukudekong/archive/2011/12/26/2302081.html
    http://blog.sina.com.cn/s/blog_92b93d6f0100ypp9.html
    http://blog.csdn.net/heidan2006/article/details/3075730
    http://zhidao.baidu.com/link?url=vBOBj5f2D1Zx3wSUJo-XphWrG6f7QPmfzk0UtS9Xk7p1SG_OdeCkiH6dT6eyHO-Pa6p4hLTEfvY7O9d_OM0Gua
    http://www.blogjava.net/dashi99/archive/2008/12/30/249207.html
    http://uule.iteye.com/blog/2051817
    http://blog.csdn.net/javaer617/article/details/6432654
    http://blog.csdn.net/seng3018/article/details/6758860
    http://groups.tianya.cn/tribe/showArticle.jsp?groupId=185385&articleId=2704257273118260804105385
    http://blog.csdn.net/qfs_v/article/details/2557128
    http://www.blogjava.net/fancydeepin/archive/2013/03/30/java-ee_web-xml.html
    http://wenku.baidu.com/link?url=P30DokIynD5zzRU2dtdkQhEwsHi-REKuBiHa_dK60bA6pQwggvX2mo9y9Mbb1tkYcsiRCaHBf-c
    4ZgIG5POmbbcRO_OxDJUaW15n300xJrq 
    http://fyq891014.blog.163.com/blog/static/200740191201233052531278/ 
    http://blog.163.com/sir_876/blog/static/11705223201111544523333/ 
    http://www.guoweiwei.com/archives/797 
    http://www.open-open.com/lib/view/open1402751642806.html 
    http://sishuok.com/forum/blogPost/list/5188.html;jsessionid=EBC2151611BEB99BDF390C5CADBA693A
    http://www.micmiu.com/j2ee/spring/spring-classpath-start/
    http://elf8848.iteye.com/blog/2008595
    http://blog.csdn.net/arvin_qx/article/details/6829873
    轻量级javaEE企业应用实战(第3版) ---李刚

     

     

     

    展开全文
  • 嵌入式tomcat的不使用web.xml原理分析

    千次阅读 2019-08-25 18:22:47
    前言 前面的章节讲了嵌入式tomcat使用web.xml启动,这章说说不使用web.xml启动tomcat。有了这个就明白Spring Boot是如何在没有web.xml的的情况下实现web能力的,以后的tomcat容器应用就可以实现jar运行,十分方便...

    前言

           前面的章节讲了嵌入式tomcat使用web.xml启动,这章说说不使用web.xml启动tomcat。有了这个就明白Spring Boot是如何在没有web.xml的的情况下实现web能力的,以后的tomcat容器应用就可以实现jar运行,十分方便。

    1. servlet标准

    首先看Oracle的javaEE文档Interface ServletContainerInitializer,上面详细讲述了这个接口的作用,使java编码web.xml成为可能,servlet3.0一下是不能实现的。look文档

    public interface ServletContainerInitializer

    Interface which allows a library/runtime to be notified of a web application's startup phase and perform any required programmatic registration of servlets, filters, and listeners in response to it.

    第一句就说可以lib jar或者web程序运行时实现servlet,filter,listener的能力

    Implementations of this interface may be annotated with HandlesTypes, in order to receive (at their onStartup(java.util.Set<java.lang.Class<?>>, javax.servlet.ServletContext) method) the Set of application classes that implement, extend, or have been annotated with the class types specified by the annotation.

    打上HandlesTypes注解,可以在onStartup的集合中拿到class的Set集合,application classes that implement说明HandlesType注解的value=xxx.class必须是接口或者父类才能生效,笔记觉得接口方式更好,Spring Web就是使用接口这种方式(体现面对接口编程)

    If an implementation of this interface does not use HandlesTypes annotation, or none of the application classes match the ones specified by the annotation, the container must pass a null Set of classes to onStartup(java.util.Set<java.lang.Class<?>>, javax.servlet.ServletContext).

    如何未设置null,不多说

    When examining the classes of an application to see if they match any of the criteria specified by the HandlesTypes annontation of a ServletContainerInitializer, the container may run into classloading problems if any of the application's optional JAR files are missing. Because the container is not in a position to decide whether these types of classloading failures will prevent the application from working correctly, it must ignore them, while at the same time providing a configuration option that would log them.

    这句意思是ServletContainerInitializer接口条件不满足,就忽略,有些情况会打印日志

    Implementations of this interface must be declared by a JAR file resource located inside the META-INF/services directory and named for the fully qualified class name of this interface, and will be discovered using the runtime's service provider lookup mechanism or a container specific mechanism that is semantically equivalent to it. In either case, ServletContainerInitializer services from web fragment JAR files excluded from an absolute ordering must be ignored, and the order in which these services are discovered must follow the application's classloading delegation model.

    必须配置放在classpath的META-INF/service下,JDK SPI的方式;在ServletContainerInitializer也叫SCI。

    /**
     * ServletContainerInitializers (SCIs) are registered via an entry in the
     * file META-INF/services/javax.servlet.ServletContainerInitializer that must be
     * included in the JAR file that contains the SCI implementation.
     * <p>
     * SCI processing is performed regardless of the setting of metadata-complete.
     * SCI processing can be controlled per JAR file via fragment ordering. If
     * absolute ordering is defined, then only the JARs included in the ordering
     * will be processed for SCIs. To disable SCI processing completely, an empty
     * absolute ordering may be defined.
     * <p>
     * SCIs register an interest in annotations (class, method or field) and/or
     * types via the {@link javax.servlet.annotation.HandlesTypes} annotation which
     * is added to the class.
     *
     * @since Servlet 3.0
     */
    public interface ServletContainerInitializer {
    
        /**
         * Receives notification during startup of a web application of the classes
         * within the web application that matched the criteria defined via the
         * {@link javax.servlet.annotation.HandlesTypes} annotation.
         *
         * @param c     The (possibly null) set of classes that met the specified
         *              criteria
         * @param ctx   The ServletContext of the web application in which the
         *              classes were discovered
         *
         * @throws ServletException If an error occurs
         */
        void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
    }

    2. demo尝试

    pom

            <properties>
            <!--<spring.version>4.3.24.RELEASE</spring.version>-->
            <embed.tomcat.version>9.0.21</embed.tomcat.version>
            </properties>
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-core</artifactId>
                <version>${embed.tomcat.version}</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
                <version>${embed.tomcat.version}</version>
                <!--<scope>provided</scope>-->
            </dependency>

    servlet

    public class HelloServlet extends HttpServlet {
        @Override
        public void init() throws ServletException {
            System.out.println("----------------------init----------------------");
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("hello world");
            response.setContentType("text/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            String res ="{\"hello\":\"world\",\"hi\":\"I`m a embed tomcat\"}";
            out.println(res);
            out.flush();
            out.close();
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    
    }

    ServletContainerInitializer 实现

    @HandlesTypes({HelloService.class})
    public class HelloServletContainerInitializer implements ServletContainerInitializer {
    
    
        @Override
        public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
            System.out.println("param class\t" + c);
            if (c != null) {
                for (Class<?> clazz : c ){
                    System.out.println(clazz.getCanonicalName());
                    //clazz.con
                }
            }
    
            ServletRegistration.Dynamic dynamic = ctx.addServlet("hello", new HelloServlet());
            dynamic.addMapping("/hello");
        }
    }

    @HandlesTypes支持,这里使用接口

    public interface HelloService {
    
        public void sayHello();
    }
    
    public class HelloServiceImpl implements HelloService {
        @Override
        public void sayHello() {
            System.out.println("ssssssssssssssss");
        }
    }
    

    配置

    main方法启动

    public class TomcatStarter {
    
        private static int port = 8080;
        private static String contextPath = "/";
    
        public static void start() throws LifecycleException, IOException, ServletException {
            Tomcat tomcat = new Tomcat();
            String baseDir = Thread.currentThread().getContextClassLoader().getResource("").getPath();
            tomcat.setBaseDir(baseDir);
            tomcat.setPort(port);
            Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
            connector.setPort(port);
            tomcat.setConnector(connector);
    
            tomcat.addWebapp(contextPath, baseDir);
            tomcat.enableNaming();
            //手动创建
            //tomcat.getConnector();
            tomcat.start();
            tomcat.getServer().await();
        }
    
        public static void main(String[] args) throws IOException, LifecycleException, ServletException {
            start();
        }
    }

    访问http://localhost:8080/hello

    3. 原理分析

    启动tomcat的过程中有一个重要的方法,webConfig,就是用来处理配置的,其中有web.xml

    /**
         * Scan the web.xml files that apply to the web application and merge them
         * using the rules defined in the spec. For the global web.xml files,
         * where there is duplicate configuration, the most specific level wins. ie
         * an application's web.xml takes precedence over the host level or global
         * web.xml file.
         */
        protected void webConfig() {
            /*
             * Anything and everything can override the global and host defaults.
             * This is implemented in two parts
             * - Handle as a web fragment that gets added after everything else so
             *   everything else takes priority
             * - Mark Servlets as overridable so SCI configuration can replace
             *   configuration from the defaults
             */
    
            /*
             * The rules for annotation scanning are not as clear-cut as one might
             * think. Tomcat implements the following process:
             * - As per SRV.1.6.2, Tomcat will scan for annotations regardless of
             *   which Servlet spec version is declared in web.xml. The EG has
             *   confirmed this is the expected behaviour.
             * - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main
             *   web.xml is marked as metadata-complete, JARs are still processed
             *   for SCIs.
             * - If metadata-complete=true and an absolute ordering is specified,
             *   JARs excluded from the ordering are also excluded from the SCI
             *   processing.
             * - If an SCI has a @HandlesType annotation then all classes (except
             *   those in JARs excluded from an absolute ordering) need to be
             *   scanned to check if they match.
             */
            WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                    context.getXmlValidation(), context.getXmlBlockExternal());
    
            Set<WebXml> defaults = new HashSet<>();
            defaults.add(getDefaultWebXmlFragment(webXmlParser));
    
            Set<WebXml> tomcatWebXml = new HashSet<>();
            tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser));
    
            WebXml webXml = createWebXml();
    
            // Parse context level web.xml
            InputSource contextWebXml = getContextWebXmlSource();
            if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
                ok = false;
            }
    
            ServletContext sContext = context.getServletContext();
    
            // Ordering is important here
    
            // Step 1. Identify all the JARs packaged with the application and those
            // provided by the container. If any of the application JARs have a
            // web-fragment.xml it will be parsed at this point. web-fragment.xml
            // files are ignored for container provided JARs.
            Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
    
            // Step 2. Order the fragments.
            Set<WebXml> orderedFragments = null;
            orderedFragments =
                    WebXml.orderWebFragments(webXml, fragments, sContext);
    
            // Step 3. Look for ServletContainerInitializer implementations
            if (ok) {
                processServletContainerInitializers();
            }
    
            if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
                // Steps 4 & 5.
                processClasses(webXml, orderedFragments);
            }
    
            if (!webXml.isMetadataComplete()) {
                // Step 6. Merge web-fragment.xml files into the main web.xml
                // file.
                if (ok) {
                    ok = webXml.merge(orderedFragments);
                }
    
                // Step 7a
                // merge tomcat-web.xml
                webXml.merge(tomcatWebXml);
    
                // Step 7b. Apply global defaults
                // Have to merge defaults before JSP conversion since defaults
                // provide JSP servlet definition.
                webXml.merge(defaults);
    
                // Step 8. Convert explicitly mentioned jsps to servlets
                if (ok) {
                    convertJsps(webXml);
                }
    
                // Step 9. Apply merged web.xml to Context
                if (ok) {
                    configureContext(webXml);
                }
            } else {
                webXml.merge(tomcatWebXml);
                webXml.merge(defaults);
                convertJsps(webXml);
                configureContext(webXml);
            }
    
            if (context.getLogEffectiveWebXml()) {
                log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
            }
    
            // Always need to look for static resources
            // Step 10. Look for static resources packaged in JARs
            if (ok) {
                // Spec does not define an order.
                // Use ordered JARs followed by remaining JARs
                Set<WebXml> resourceJars = new LinkedHashSet<>();
                for (WebXml fragment : orderedFragments) {
                    resourceJars.add(fragment);
                }
                for (WebXml fragment : fragments.values()) {
                    if (!resourceJars.contains(fragment)) {
                        resourceJars.add(fragment);
                    }
                }
                processResourceJARs(resourceJars);
                // See also StandardContext.resourcesStart() for
                // WEB-INF/classes/META-INF/resources configuration
            }
    
            // Step 11. Apply the ServletContainerInitializer config to the
            // context
            if (ok) {
                for (Map.Entry<ServletContainerInitializer,
                        Set<Class<?>>> entry :
                            initializerClassMap.entrySet()) {
                    if (entry.getValue().isEmpty()) {
                        context.addServletContainerInitializer(
                                entry.getKey(), null);
                    } else {
                        context.addServletContainerInitializer(
                                entry.getKey(), entry.getValue());
                    }
                }
            }
        }

    processServletContainerInitializers();//初始化ServletContainerInitializers

    protected void processServletContainerInitializers() {
    
            List<ServletContainerInitializer> detectedScis;
            try {
                WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
                //加载ServletContainerInitializer
                detectedScis = loader.load(ServletContainerInitializer.class);
            } catch (IOException e) {
                log.error(sm.getString(
                        "contextConfig.servletContainerInitializerFail",
                        context.getName()),
                    e);
                ok = false;
                return;
            }
    
            for (ServletContainerInitializer sci : detectedScis) {
                //初始化map
                initializerClassMap.put(sci, new HashSet<Class<?>>());
    
                HandlesTypes ht;
                try {
                    ht = sci.getClass().getAnnotation(HandlesTypes.class);
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.info(sm.getString("contextConfig.sci.debug",
                                sci.getClass().getName()),
                                e);
                    } else {
                        log.info(sm.getString("contextConfig.sci.info",
                                sci.getClass().getName()));
                    }
                    continue;
                }
                if (ht == null) {
                    continue;
                }
                Class<?>[] types = ht.value();
                if (types == null) {
                    continue;
                }
    
                //解析注释HandlesTypes,根据是不是注解分类
                for (Class<?> type : types) {
                    if (type.isAnnotation()) {
                        handlesTypesAnnotations = true;
                    } else {
                        handlesTypesNonAnnotations = true;
                    }
                    Set<ServletContainerInitializer> scis =
                            typeInitializerMap.get(type);
                    if (scis == null) {
                        scis = new HashSet<>();
                        typeInitializerMap.put(type, scis);
                    }
                    scis.add(sci);
                }
            }
        }

     跟踪loader.load(ServletContainerInitializer.class);

        private static final String LIB = "/WEB-INF/lib/";
        private static final String SERVICES = "META-INF/services/";
    
    /**
         * Load the providers for a service type.
         *
         * @param serviceType the type of service to load
         * @return an unmodifiable collection of service providers
         * @throws IOException if there was a problem loading any service
         */
        public List<T> load(Class<T> serviceType) throws IOException {
            String configFile = SERVICES + serviceType.getName();

    载入了META-INF/services/下的className文件

    然后加载classes文件,

    if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
                // Steps 4 & 5.
                processClasses(webXml, orderedFragments);
            }

    有jar包有file即class文件,笔者没打成jar,@HandlesTypes({HelloService.class})肯定是classes文件了。 

    protected void processAnnotationsUrl(URL url, WebXml fragment,
                boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) {
            if (url == null) {
                // Nothing to do.
                return;
            } else if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
                processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache);
            } else if ("file".equals(url.getProtocol())) {
                try {
                    processAnnotationsFile(
                            new File(url.toURI()), fragment, handlesTypesOnly, javaClassCache);
                } catch (URISyntaxException e) {
                    log.error(sm.getString("contextConfig.fileUrl", url), e);
                }
            } else {
                log.error(sm.getString("contextConfig.unknownUrlProtocol",
                        url.getProtocol(), url));
            }
    
        }

    继续跟踪

    /**
         * For classes packaged with the web application, the class and each
         * super class needs to be checked for a match with {@link HandlesTypes} or
         * for an annotation that matches {@link HandlesTypes}.
         * @param javaClass the class to check
         * @param javaClassCache a class cache
         */
        protected void checkHandlesTypes(JavaClass javaClass,
                Map<String,JavaClassCacheEntry> javaClassCache) {
    
            // Skip this if we can
            if (typeInitializerMap.size() == 0) {
                return;
            }
    
            if ((javaClass.getAccessFlags() &
                    org.apache.tomcat.util.bcel.Const.ACC_ANNOTATION) != 0) {
                // Skip annotations.
                return;
            }
    
            String className = javaClass.getClassName();
    
            Class<?> clazz = null;
            //HandlesTypes value不是注解类
            if (handlesTypesNonAnnotations) {
                // This *might* be match for a HandlesType.
                populateJavaClassCache(className, javaClass, javaClassCache);
                JavaClassCacheEntry entry = javaClassCache.get(className);
                if (entry.getSciSet() == null) {
                    try {
                        populateSCIsForCacheEntry(entry, javaClassCache);
                    } catch (StackOverflowError soe) {
                        throw new IllegalStateException(sm.getString(
                                "contextConfig.annotationsStackOverflow",
                                context.getName(),
                                classHierarchyToString(className, entry, javaClassCache)));
                    }
                }
                if (!entry.getSciSet().isEmpty()) {
                    // Need to try and load the class
                    clazz = Introspection.loadClass(context, className);
                    if (clazz == null) {
                        // Can't load the class so no point continuing
                        return;
                    }
    
                    for (ServletContainerInitializer sci : entry.getSciSet()) {
                        Set<Class<?>> classes = initializerClassMap.get(sci);
                        if (classes == null) {
                            classes = new HashSet<>();
                            initializerClassMap.put(sci, classes);
                        }
                        classes.add(clazz);
                    }
                }
            }
    
            //HandlesTypes的value是注解类
            //这种比较简单,解析注解,反射实例
            if (handlesTypesAnnotations) {
                AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries();
                if (annotationEntries != null) {
                    for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
                            typeInitializerMap.entrySet()) {
                        if (entry.getKey().isAnnotation()) {
                            String entryClassName = entry.getKey().getName();
                            for (AnnotationEntry annotationEntry : annotationEntries) {
                                if (entryClassName.equals(
                                        getClassName(annotationEntry.getAnnotationType()))) {
                                    if (clazz == null) {
                                        clazz = Introspection.loadClass(
                                                context, className);
                                        if (clazz == null) {
                                            // Can't load the class so no point
                                            // continuing
                                            return;
                                        }
                                    }
                                    for (ServletContainerInitializer sci : entry.getValue()) {
                                        initializerClassMap.get(sci).add(clazz);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

    HandlesType的value不是注解只能是接口或者父类,它们的实现类或者子类被注入Set集合

    private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry,
                Map<String,JavaClassCacheEntry> javaClassCache) {
            Set<ServletContainerInitializer> result = new HashSet<>();
    
            // Super class
            String superClassName = cacheEntry.getSuperclassName();
            JavaClassCacheEntry superClassCacheEntry =
                    javaClassCache.get(superClassName);
    
            // Avoid an infinite loop with java.lang.Object
            if (cacheEntry.equals(superClassCacheEntry)) {
                cacheEntry.setSciSet(EMPTY_SCI_SET);
                return;
            }
    
            //可以看出HandlesTypes注解父类可以解析
            // May be null of the class is not present or could not be loaded.
            if (superClassCacheEntry != null) {
                if (superClassCacheEntry.getSciSet() == null) {
                    populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache);
                }
                result.addAll(superClassCacheEntry.getSciSet());
            }
            result.addAll(getSCIsForClass(superClassName));
    
            //可以看出HandlesTypes注解接口可以解析
            // Interfaces
            for (String interfaceName : cacheEntry.getInterfaceNames()) {
                JavaClassCacheEntry interfaceEntry =
                        javaClassCache.get(interfaceName);
                // A null could mean that the class not present in application or
                // that there is nothing of interest. Either way, nothing to do here
                // so move along
                if (interfaceEntry != null) {
                    if (interfaceEntry.getSciSet() == null) {
                        populateSCIsForCacheEntry(interfaceEntry, javaClassCache);
                    }
                    result.addAll(interfaceEntry.getSciSet());
                }
                result.addAll(getSCIsForClass(interfaceName));
            }
    
            cacheEntry.setSciSet(result.isEmpty() ? EMPTY_SCI_SET : result);
        }

     核心方法

    private Set<ServletContainerInitializer> getSCIsForClass(String className) {
            for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
                    typeInitializerMap.entrySet()) {
                Class<?> clazz = entry.getKey();
                if (!clazz.isAnnotation()) {
                    if (clazz.getName().equals(className)) {
                        return entry.getValue();
                    }
                }
            }
            return EMPTY_SCI_SET;
        }

    可以如果HandlesTypes的值不是注解,这个值的封装cacheEntry使用superClassCacheEntry 或者cacheEntry.getInterfaceNames()来设置cacheEntry的SciSet即Set<ServletContainerInitializer>

    if (ok) {
                for (Map.Entry<ServletContainerInitializer,
                        Set<Class<?>>> entry :
                            initializerClassMap.entrySet()) {
                    if (entry.getValue().isEmpty()) {
                        context.addServletContainerInitializer(
                                entry.getKey(), null);
                    } else {
                        context.addServletContainerInitializer(
                                entry.getKey(), entry.getValue());
                    }
                }
            }

    这里将HandlesType的结果写进ServletContainerInitializer

    /**
         * Add a ServletContainerInitializer instance to this web application.
         *
         * @param sci       The instance to add
         * @param classes   The classes in which the initializer expressed an
         *                  interest
         */
        @Override
        public void addServletContainerInitializer(
                ServletContainerInitializer sci, Set<Class<?>> classes) {
            initializers.put(sci, classes);
        }

    上一章,我们知道tomcat是线程池启动child Container

    里面有代码片段

    // Call ServletContainerInitializers
                for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                    initializers.entrySet()) {
                    try {
                        entry.getKey().onStartup(entry.getValue(),
                                getServletContext());
                    } catch (ServletException e) {
                        log.error(sm.getString("standardContext.sciFail"), e);
                        ok = false;
                        break;
                    }
                }

     用于启动ServletContainerInitializer的onStartup方法

    总结

    嵌入式tomcat不使用web.xml是由于servlet 3.0的ServletContainerInitializer的引入。Spring Boot也是这样使用的,如下

     其中Spring-Web封装了实现,并在HandlesTypes做了一层代理

    /*
     * Copyright 2002-2018 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.web;
    
    import java.lang.reflect.Modifier;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.ServiceLoader;
    import java.util.Set;
    import javax.servlet.ServletContainerInitializer;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.HandlesTypes;
    
    import org.springframework.core.annotation.AnnotationAwareOrderComparator;
    import org.springframework.lang.Nullable;
    import org.springframework.util.ReflectionUtils;
    
    /**
     * Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based
     * configuration of the servlet container using Spring's {@link WebApplicationInitializer}
     * SPI as opposed to (or possibly in combination with) the traditional
     * {@code web.xml}-based approach.
     *
     * <h2>Mechanism of Operation</h2>
     * This class will be loaded and instantiated and have its {@link #onStartup}
     * method invoked by any Servlet 3.0-compliant container during container startup assuming
     * that the {@code spring-web} module JAR is present on the classpath. This occurs through
     * the JAR Services API {@link ServiceLoader#load(Class)} method detecting the
     * {@code spring-web} module's {@code META-INF/services/javax.servlet.ServletContainerInitializer}
     * service provider configuration file. See the
     * <a href="https://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider">
     * JAR Services API documentation</a> as well as section <em>8.2.4</em> of the Servlet 3.0
     * Final Draft specification for complete details.
     *
     * <h3>In combination with {@code web.xml}</h3>
     * A web application can choose to limit the amount of classpath scanning the Servlet
     * container does at startup either through the {@code metadata-complete} attribute in
     * {@code web.xml}, which controls scanning for Servlet annotations or through an
     * {@code <absolute-ordering>} element also in {@code web.xml}, which controls which
     * web fragments (i.e. jars) are allowed to perform a {@code ServletContainerInitializer}
     * scan. When using this feature, the {@link SpringServletContainerInitializer}
     * can be enabled by adding "spring_web" to the list of named web fragments in
     * {@code web.xml} as follows:
     *
     * <pre class="code">
     * &lt;absolute-ordering&gt;
     *   &lt;name>some_web_fragment&lt;/name&gt;
     *   &lt;name>spring_web&lt;/name&gt;
     * &lt;/absolute-ordering&gt;
     * </pre>
     *
     * <h2>Relationship to Spring's {@code WebApplicationInitializer}</h2>
     * Spring's {@code WebApplicationInitializer} SPI consists of just one method:
     * {@link WebApplicationInitializer#onStartup(ServletContext)}. The signature is intentionally
     * quite similar to {@link ServletContainerInitializer#onStartup(Set, ServletContext)}:
     * simply put, {@code SpringServletContainerInitializer} is responsible for instantiating
     * and delegating the {@code ServletContext} to any user-defined
     * {@code WebApplicationInitializer} implementations. It is then the responsibility of
     * each {@code WebApplicationInitializer} to do the actual work of initializing the
     * {@code ServletContext}. The exact process of delegation is described in detail in the
     * {@link #onStartup onStartup} documentation below.
     *
     * <h2>General Notes</h2>
     * In general, this class should be viewed as <em>supporting infrastructure</em> for
     * the more important and user-facing {@code WebApplicationInitializer} SPI. Taking
     * advantage of this container initializer is also completely <em>optional</em>: while
     * it is true that this initializer will be loaded and invoked under all Servlet 3.0+
     * runtimes, it remains the user's choice whether to make any
     * {@code WebApplicationInitializer} implementations available on the classpath. If no
     * {@code WebApplicationInitializer} types are detected, this container initializer will
     * have no effect.
     *
     * <p>Note that use of this container initializer and of {@code WebApplicationInitializer}
     * is not in any way "tied" to Spring MVC other than the fact that the types are shipped
     * in the {@code spring-web} module JAR. Rather, they can be considered general-purpose
     * in their ability to facilitate convenient code-based configuration of the
     * {@code ServletContext}. In other words, any servlet, listener, or filter may be
     * registered within a {@code WebApplicationInitializer}, not just Spring MVC-specific
     * components.
     *
     * <p>This class is neither designed for extension nor intended to be extended.
     * It should be considered an internal type, with {@code WebApplicationInitializer}
     * being the public-facing SPI.
     *
     * <h2>See Also</h2>
     * See {@link WebApplicationInitializer} Javadoc for examples and detailed usage
     * recommendations.<p>
     *
     * @author Chris Beams
     * @author Juergen Hoeller
     * @author Rossen Stoyanchev
     * @since 3.1
     * @see #onStartup(Set, ServletContext)
     * @see WebApplicationInitializer
     */
    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    
    	/**
    	 * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
    	 * implementations present on the application classpath.
    	 * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
    	 * Servlet 3.0+ containers will automatically scan the classpath for implementations
    	 * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
    	 * such types to the {@code webAppInitializerClasses} parameter of this method.
    	 * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
    	 * this method is effectively a no-op. An INFO-level log message will be issued notifying
    	 * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
    	 * no {@code WebApplicationInitializer} implementations were found.
    	 * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
    	 * they will be instantiated (and <em>sorted</em> if the @{@link
    	 * org.springframework.core.annotation.Order @Order} annotation is present or
    	 * the {@link org.springframework.core.Ordered Ordered} interface has been
    	 * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
    	 * method will be invoked on each instance, delegating the {@code ServletContext} such
    	 * that each instance may register and configure servlets such as Spring's
    	 * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
    	 * or any other Servlet API componentry such as filters.
    	 * @param webAppInitializerClasses all implementations of
    	 * {@link WebApplicationInitializer} found on the application classpath
    	 * @param servletContext the servlet context to be initialized
    	 * @see WebApplicationInitializer#onStartup(ServletContext)
    	 * @see AnnotationAwareOrderComparator
    	 */
    	@Override
    	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
    			throws ServletException {
    
    		List<WebApplicationInitializer> initializers = new LinkedList<>();
    
    		if (webAppInitializerClasses != null) {
    			for (Class<?> waiClass : webAppInitializerClasses) {
    				// Be defensive: Some servlet containers provide us with invalid classes,
    				// no matter what @HandlesTypes says...
                                    //去除接口与抽象类,仅用具体实现
    				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
    						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
    					try {
                                                    //spring web封装类初始化
    						initializers.add((WebApplicationInitializer)
    								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
    					}
    					catch (Throwable ex) {
    						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
    					}
    				}
    			}
    		}
    
    		if (initializers.isEmpty()) {
    			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
    			return;
    		}
    
    		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
    		AnnotationAwareOrderComparator.sort(initializers);
    		for (WebApplicationInitializer initializer : initializers) {
                             //逐一启动
    			initializer.onStartup(servletContext);
    		}
    	}
    
    }
    

    Spring Web的配置

    Spring Boot在Spring Web的基础上,下面的代码是spring boot使用war部署的启动模式,先启动servletContext后启动Spring ApplicationContext。当然Spring Boot embed tomcat是先启动Spring ApplicationContext后启动servletContext

    /*
     * Copyright 2012-2019 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.boot.web.servlet.support;
    
    import java.util.Collections;
    
    import javax.servlet.Filter;
    import javax.servlet.Servlet;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletException;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
    import org.springframework.boot.web.servlet.ServletContextInitializer;
    import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.util.Assert;
    import org.springframework.web.WebApplicationInitializer;
    import org.springframework.web.context.ConfigurableWebEnvironment;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.WebApplicationContext;
    
    /**
     * An opinionated {@link WebApplicationInitializer} to run a {@link SpringApplication}
     * from a traditional WAR deployment. Binds {@link Servlet}, {@link Filter} and
     * {@link ServletContextInitializer} beans from the application context to the server.
     * <p>
     * To configure the application either override the
     * {@link #configure(SpringApplicationBuilder)} method (calling
     * {@link SpringApplicationBuilder#sources(Class...)}) or make the initializer itself a
     * {@code @Configuration}. If you are using {@link SpringBootServletInitializer} in
     * combination with other {@link WebApplicationInitializer WebApplicationInitializers} you
     * might also want to add an {@code @Ordered} annotation to configure a specific startup
     * order.
     * <p>
     * Note that a WebApplicationInitializer is only needed if you are building a war file and
     * deploying it. If you prefer to run an embedded web server then you won't need this at
     * all.
     *
     * @author Dave Syer
     * @author Phillip Webb
     * @author Andy Wilkinson
     * @since 2.0.0
     * @see #configure(SpringApplicationBuilder)
     */
    public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
    
    	protected Log logger; // Don't initialize early
    
    	private boolean registerErrorPageFilter = true;
    
    	/**
    	 * Set if the {@link ErrorPageFilter} should be registered. Set to {@code false} if
    	 * error page mappings should be handled via the server and not Spring Boot.
    	 * @param registerErrorPageFilter if the {@link ErrorPageFilter} should be registered.
    	 */
    	protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) {
    		this.registerErrorPageFilter = registerErrorPageFilter;
    	}
    
    	@Override
    	public void onStartup(ServletContext servletContext) throws ServletException {
    		// Logger initialization is deferred in case an ordered
    		// LogServletContextInitializer is being used
    		this.logger = LogFactory.getLog(getClass());
                    //创建Spring context容器
    		WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
    		if (rootAppContext != null) {
                            //设置root listener
    			servletContext.addListener(new ContextLoaderListener(rootAppContext) {
    				@Override
    				public void contextInitialized(ServletContextEvent event) {
    					// no-op because the application context is already initialized
    				}
    			});
    		}
    		else {
    			this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not "
    					+ "return an application context");
    		}
    	}
    
    	protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
    		SpringApplicationBuilder builder = createSpringApplicationBuilder();
    		builder.main(getClass());
                    //有ApplicationContext,使用父容器
    		ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
    		if (parent != null) {
    			this.logger.info("Root context already created (using as parent).");
    			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
    			builder.initializers(new ParentContextApplicationContextInitializer(parent));
    		}
                    //这里很重要Spring的applicationContext就是这里埋点与servletContext设置的
    		builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
    		builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
    		builder = configure(builder);
                    //监听web环境资源初始化
    		builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
    		SpringApplication application = builder.build();
    		if (application.getAllSources().isEmpty()
    				&& AnnotationUtils.findAnnotation(getClass(), Configuration.class) != null) {
    			application.addPrimarySources(Collections.singleton(getClass()));
    		}
    		Assert.state(!application.getAllSources().isEmpty(),
    				"No SpringApplication sources have been defined. Either override the "
    						+ "configure method or add an @Configuration annotation");
    		// Ensure error pages are registered
    		if (this.registerErrorPageFilter) {
    			application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
    		}
            
                    //这里有意思了,构造器模式创建了SpringApplication对象,然后run,启动容器
    		return run(application);
    	}
    
    	/**
    	 * Returns the {@code SpringApplicationBuilder} that is used to configure and create
    	 * the {@link SpringApplication}. The default implementation returns a new
    	 * {@code SpringApplicationBuilder} in its default state.
    	 * @return the {@code SpringApplicationBuilder}.
    	 * @since 1.3.0
    	 */
    	protected SpringApplicationBuilder createSpringApplicationBuilder() {
    		return new SpringApplicationBuilder();
    	}
    
    	/**
    	 * Called to run a fully configured {@link SpringApplication}.
    	 * @param application the application to run
    	 * @return the {@link WebApplicationContext}
    	 */
    	protected WebApplicationContext run(SpringApplication application) {
    		return (WebApplicationContext) application.run();
    	}
    
    	private ApplicationContext getExistingRootWebApplicationContext(ServletContext servletContext) {
    		Object context = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    		if (context instanceof ApplicationContext) {
    			return (ApplicationContext) context;
    		}
    		return null;
    	}
    
    	/**
    	 * Configure the application. Normally all you would need to do is to add sources
    	 * (e.g. config classes) because other settings have sensible defaults. You might
    	 * choose (for instance) to add default command line arguments, or set an active
    	 * Spring profile.
    	 * @param builder a builder for the application context
    	 * @return the application builder
    	 * @see SpringApplicationBuilder
    	 */
    	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
    		return builder;
    	}
    
    	private static final class WebEnvironmentPropertySourceInitializer
    			implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
    
    		private final ServletContext servletContext;
    
    		private WebEnvironmentPropertySourceInitializer(ServletContext servletContext) {
    			this.servletContext = servletContext;
    		}
    
    		@Override
    		public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
    			ConfigurableEnvironment environment = event.getEnvironment();
    			if (environment instanceof ConfigurableWebEnvironment) {
    				((ConfigurableWebEnvironment) environment).initPropertySources(this.servletContext, null);
    			}
    		}
    
    		@Override
    		public int getOrder() {
    			return Ordered.HIGHEST_PRECEDENCE;
    		}
    
    	}
    
    }
    

    Spring Boot war部署设置方式设置servletContext的地方

    builder.initializers(new ServletContextApplicationContextInitializer(servletContext));

    在Spring Boot启动过程

     有初始化context的代码

    applyInitializers(context);

        /**
    	 * Apply any {@link ApplicationContextInitializer}s to the context before it is
    	 * refreshed.
    	 * @param context the configured ApplicationContext (not refreshed yet)
    	 * @see ConfigurableApplicationContext#refresh()
    	 */
    	@SuppressWarnings({ "rawtypes", "unchecked" })
    	protected void applyInitializers(ConfigurableApplicationContext context) {
    		for (ApplicationContextInitializer initializer : getInitializers()) {
    			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
    					ApplicationContextInitializer.class);
    			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
    			initializer.initialize(context);
    		}
    	}

    展开全文
  • <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> ......
  • web中常见web.xml标签,及初始化顺序

    千次阅读 2019-05-27 22:59:54
    1.Tip 大小写敏感;标签不嵌套,必须配对;web.xml不是必须的,可以不用再web.xml里面配置任何信息。 2.web.xml加载过程 启动一个web项目时,Web容器首先会读取项目web.xml配置文件里的配置信息,当加载web.xml...
  • webxml配置详解1

    2018-02-27 17:57:56
    一、web.xml配置文件常用元素及其意义预览转载路径:...lt;web-app&gt; 2 3 &lt;!--定义了WEB应用的名字--&gt; 4 &...display-name&...--声明WEB应用的描述信...
  • maven install web项目时报错:[ERROR] Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.2:war (default-war) on project spring2: Error assembling WAR: webxml attribute is required (or ...
  • webService公共开放接口大全

    千次阅读 2017-07-13 09:55:51
    天气预报Web服务,数据来源于...Endpoint :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx Disco :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?disco WSDL :http://www.webxml.com
  • ar (default-war) on project lmsportal-admin: Error assembling WAR: webxml attrib ute is required (or pre-existing WEB-INF/web.xml if executing in update mode) -> [Help 1] 有没有哪位出现过类似的...
  • Error assembling WAR: webxml attribute is required (or pre-existing WEB-INF/web.xml    springboot启动的项目报错。 解决方案:   &lt;!-- 没有web.xml文件的情况下构建WAR &lt;plugin&...
  • Webservice常用接口大全

    万次阅读 2013-12-18 17:44:32
    天气预报Web服务,数据来源于...Endpoint :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx Disco :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?disco WSDL :http://www.webxml
  • 常用WebService一览表(一)

    千次阅读 2013-04-24 15:43:31
    天气预报Web服务,数据来源于中国...Endpoint :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx Disco :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?disco WSDL :http://www.webxm
  • 天气预报Web服务,数据来源于...Endpoint :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx Disco :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?disco WSDL :http://www.webxml
  • 一些免费的WebService的服务网站

    千次阅读 2015-06-27 18:07:01
    http://fy.webxml.com.cn/webservices/EnglishChinese.asmx Disco: http://fy.webxml.com.cn/webservices/EnglishChinese.asmx?disco WSDL: http://fy.webxml.com.cn/webservices/EnglishChinese.
  • 常用的webservice接口

    万次阅读 热门讨论 2012-01-10 14:22:56
    Web Service 一些对外公开的网络服务接口 2011-10-29 14:12 ...1、股票行情数据 WEB 服务(支持香港、深圳、...Endpoint: http://webservice.webxml.com.cn/WebServices/StockInfoWS.asmx  Disco: http://web
  • 常用免费的WebService列表

    千次阅读 多人点赞 2014-01-23 10:00:26
    天气预报Web服务,数据来源于中国气象局 ...Endpoint : http://www.webxml.com.cn/WebServices/WeatherWebService.asmx Disco : http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?disco WSDL : h
  • webservice 在线测试(备用)

    万次阅读 2018-03-07 16:53:39
    腾讯QQ在线状态 WEB 服务Endpoint: ...
  • 天气预报Web服务,数据来源于中国气象局 Endpoint :http://www.webxml.com.cn/WebServices/WeatherWebService.asmxDisco :http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?discoWSDL :...
  • 网上常用免费的webservice

    千次阅读 2015-11-18 10:23:16
    http://www.webxml.com.cn/WebServices/WeatherWebService.asmx中国股票行情分时走势预览缩略图 http://www.webxml.com.cn/webservices/ChinaStockSmallImageWS.asmx中国股票行情数据 WEB 服务(支持深圳和上海股市...
1 2 3 4 5 ... 20
收藏数 12,570
精华内容 5,028
关键字:

webxml