天天看点

Servlet注解和可插拔性(第八篇)

文章目录

    • 8.1、注解和可插拔性
      • 8.1.1、@WebServlet 注解
      • 8.1.2、@WebFilter (web过滤器)
      • 8.1.3、@WebInitParam
      • 8.1.4、@WebListener
      • 8.1.5、@MultipartConfig
      • 8.1.6、其他注解和约定
    • 8.2、可插拔性
      • 8.2.1、模块化web.xml
      • 8.2.2、web.xml 和web-fragment.xml的顺序
      • 8.2.3、web.xml 、web-fragment.xml和注释组装描述符
      • 8.2.4、共享库/运行时可拔插性
    • 8.3、JSP容器插拔性
    • 8.4、处理注解和片段

8.1、注解和可插拔性

  • WEB-INF/classes 目录 编译java类
  • WEB-INF/lib jar库
  • 兼容Servlet 3.0的注解

8.1.1、@WebServlet 注解

  • 支持web请求,被注释类必须实现javax.servlet.http.HttpServlet
  • 例子
  • // 拦截/foo 的URI
    @WebServlet("/foo")
    public class CalculatorServlet extends HttpServlet{
      // ...
    }
    
    // 注解属性, 拦截/foo 或 /bar,  实现doGet方法
    @WebServlet(name="MyServlet", urlPatterns={"/foo", "/bar"})
    public class SampleUsingAnnotationAttributes extends HttpServlet{
      public void doGet(HttpServletRequest req, HttpServletResponse res)	{
        
      }
    }
               

8.1.2、@WebFilter (web过滤器)

  • 不能同时配置value和urlPatterns属性
  • 被@WebFilter 必须要实现javax.servlet.Filter接口
  • 例子
    • @WebFilter("/foo")
      public class MyFilter implements Filter{
        public void doFilter(HttpServletRequest req, HttpServletResponse res){
          // 实现doFilter方法
        }
      }
                 

8.1.3、@WebInitParam

  • 初始化参数,它是WebServlet 和WebFilter的参数

8.1.4、@WebListener

  • web监听器,监听某些特定事件
  • 被@WebListener修饰的类,必须实现其中一个接口
    • javax.servlet.ServletContextListener
    • javax.servlet.ServletContextAttributeListener
    • javax.servlet.ServletRequestListener
    • javax.servlet.ServletRequestAttributeListener
    • javax.servlet.http.HttpSessionListener
    • javax.servlet.http.HttpSessionAttributeListener
    • javax.servlet.http.HttpSessionIdListener
  • 例子
    • @WebListener
      public class MyListener implements ServletContextListener{
        public void contextInitialized(ServletContextEvent sce){
          ServletContext sc = sce.getServletContext();
          sc.addServlet("myServlet", "Sample servlet","foo.bar.MyServlet", null, -1);
          sc.addServletMapping("myServlet", new String[]{
            "/urlpattern/*"
          })
        }
      }
                 

8.1.5、@MultipartConfig

  • 如果用这个注解,需要支持mime/multipart 类型, 同时Servlet必须实现getParts和getPart方法
  • 配置中属性是绝对路径,如果配置相对路径,是相对于javax.servlet.context.tempdir, 这些路径都是需要通过java.io.File.isAbsolute的方法测试。

8.1.6、其他注解和约定

  • 其他全部注解都在15章
  • 每个应用都有默认的首页(index.htm(1) 和 index.jsp) ,这个配置在welcome-file-list中

8.2、可插拔性

8.2.1、模块化web.xml

  • 多个web-fragment.xml 组成一个web.xml 模块化
  • web-fragment.xml需要在jar的WEB-INF/lib目录下
  • 例子
  • <web-fragment> 
      <servlet>
    		<servlet-name>welcome</servlet-name>
       	 	<servlet-class>WelcomeServlet </servlet-class>
       </servlet>
       <listener>
    			<listener-class> RequestListener</listener-class> 
      </listener>
    </web-fragment>
    
               

8.2.2、web.xml 和web-fragment.xml的顺序

  1. 绝对顺序 在web.xml 有且只有一个 元素
    • 通过name去确定web-fragment.xml, 就算name不存在,也会处理
    • 可以配置
    • 重复name异常
  2. 相对顺序:web-fragment.xml有且只有一个元素
    • web.xm. 和WEB-INF/classes 必须在web-fragment标识ordering元素之前执行
    • 重复的name异常,需要打印错误日志,报错,部署失败,提示修复,可以使用绝对路径排序
  3. 例子
    • //web-fragment.xml
      <web-fragment>
      	<name>MyFragment1</name>
        <ordering>
        	<after>
          	<name>MyFragment2</name>
          </after>
        </ordering>
        ....
      </web-fragment>
      
      
      // web-fragment.xml
      <web-fragment>
      	<name>MyFrament2</name>
        ...
      </web-fragment>
      
      // web-fragment.xml
      <web-fragment>
      	<name>MyFrament3</name>
        <ordering>
        	<before>
          	<others/>
          </before>
        </ordering>
      </web-fragment>
      
      // web.xml
      <web-app>
      	...
      </web-app>
                 
    • 上面处理过程
    • web.xml
      MyFrament3
      MyFrament2
      MyFrament1
                 
    • 这个例子阐释一些,但不是全部,还有一些规则
    • 在某个元素之前执行,如果others表示在web-fragment最新执行
    • 在某个元素之后执行
    • (可以包含在 和 )
      • 如果在包含 会排在所有元素之前,如果有多个包含,那么这些包含 顺序无法确定
      • 与刚刚相反, 如果包含会排到最后,但是如果有多个包含,那么这些包含 顺序无法确定
      • 在或元素中,如果存在元素,但不是其父元素中唯一的元素,则在排序过程中必须考虑该父元素中的其他元素
      • 如果元素直接出现在元素内,则运行时必须确保处理顺序中的该点包括未在部分中明确命名的任何Web片段。
    • 如果web-fragment.xml和web.xml 不存在或 表示元素没有顺序要求
    • 如果出现循环依赖时候,需要抛出异常,部署失败,可能需要提示用户配置绝对路径
    • 例子(absolute-ordering)
      • //例子1
        // web.xml
        <web-app>
        	<absolute-ordering>
          	<name>MyFragment3</name>
        		<name>MyFragment2</name>
          </absolute-ordering>
          ...
        </web-app>
        
        在这个例子中元素执行顺序
        web.xml
        MyFragment3
        MyFragment2
                   
    • 其他场景例子
      • //例子2
        片段A
        <after>
        	<others/>
          <name>C</name>
        </after>
        
        片段B
        <before>
        	<others/>
        </before>
        
        //片段C
        <after>
        	<others/>
        </after>
        // 片段D 、E 没有顺序
        //片段F
        <before>
        	<others/>
          <name>B</name>
        </before>
        
        最后顺序为  web.xml, F、B、D、E、C,A
                   
      • //例子3
        片段 <no id/>:
        <after>
        	<others/>
        </after>
        <before>
        	<name>
          	C
          </name>
        <before>
          
          片段B
          <before>
          	<others/>
          </before>
         片段 C : 没有顺序
         片段 D:
          <after>
          	<others/>
          </after>
         片段 E:
          <before>
          	<others/>
          </before>
          片段 F: 没有顺序
          
         // 结果可能如下其中一种 B和E顺序不稳定, C,D,<noid>不稳定, 只知道,<noid/>一定在C之前
        ■ B,E,F,<noid>,C,D
        ■ B,E,F,<noid>,D,C
        ■ E,B,F,<noid>,C,D 
        ■ E,B,F,<noid>,D,C
        ■ E,B,F,D,<noid>,C
                   
      • //例子4
        片段 A
        <after>
        	<name> B</name>
        </after>
        
        片段 B:没有顺序
        片段 C 
        <before>
        	<others/>
        </before>
        片段 D: 没有顺序
        
        //可以结果 C B D A     或 C D B A 或 C B A D
                   

8.2.3、web.xml 、web-fragment.xml和注释组装描述符

  • 在定义监听器、servlet、过滤器时候,需要定义加载顺序,如果没有定义将使用如下规则
  1. 需要指定web-fragment.xm.或web.xml顺序
  2. 如果在web.xml和web-fragment.xml存在
    • Filters链 匹配的顺序是按照web.xml的配置顺序
    • servlet可以延迟初始化或立即初始化,如果立即初始化,那么这个初始化的顺序是按照load-on-startup元素配置的
    • 监听器在web.xml配置的调用顺序
      1. 监听器实现javax.servlet.ServletContextListener的contextInitialized方法和销毁方法contextDestroyed方法
      2. 监听器实现javax.servlet.ServletRequestLIstener的requestInitialized方法和requestDestroyed方法
      3. 实现javax.servlet.http.HttpSessionListener的sessionCreated 和销毁方法sessionDestroyed方法
      4. 实现javax.servlet.ServletContextAttributeListener, javax.servlet.ServletRequestAttributeListener, javax.servlet.HttpSessionAttributeListener 调用它对应事件将会触发
  3. 如果一个servlet配置enabled元素是禁用状态,那么这个servlet对应url-pattern将不可用
  4. 当web.xml 和 web-fragment.xml 出现冲突的时候, web.xml中元素具有最高优先级(最终有效)
  5. 如果没有配置metadata-complete元数据, 或者在描述符设置它为false,然后只能通过合并注解和描述符获取元数据, 合并规则如下:
    • web-fragment.xml可以配置参数作为web.xml配置使用(也就可以将web.xml部分配置移动某个web-fragment.xml中)
    • web-fragment具体配置参考8.2.2
    • 如果在web.xml中设置metadata-complete = true, 那么web-fragment.xml配置将会被忽略,如果在某一个web-fragment.xml 的metadata-complete 设置为true, 那么将会忽略absolute-ordering和ordering元素,只会扫描特定的jar的注解
    • 在metadata-complete设置true之前,web 片段都会合并到web.xml文件
    • 在合并web片段时候,以下是认为冲突了
      • 多个 元素, 有相同的 但是不同
      • 多个 元素,有相同 但是有不同
    • 冲突解决原则
      • 如果在web.xml 和web-fragment.xml 发生冲突,那么web.xml具有更高的优先级
      • 如果两个web-fragment.xml发生冲突,需要解决,部署失败
    • 解决冲突之后应该采用如下附加规则
      • 在不同web-fragment.xml 可以添加不同
      • 元素可以声明多次,最终还是以web.xml为准
      • 如果某个Web片段中存在一个最小出现次数为零且最大出现为1的元素,而在主web.xml中丢失了,则主web.xml将从该Web片段继承设置。 如果主web.xml和Web片段中都存在该元素,则优先于主web.xml中的配置设置。 例如,如果主web.xml和一个Web片段都声明了相同的servlet,并且该Web片段中的servlet声明指定了元素,而主web.xml中的那个则没有, 那么来自Web片段的元素将在合并的web.xml中使用。
      • 如果web.xml定义一个servlet,并没有配置 ,在两个web-fragement分别定义一个相同的servlet,但是它们配置的是不同的,这个时候将会有一个错误抛出,不能抉择
      • 是附加的
      • 在web.xml 和web-fragment.xml都声明了相同的, 那么最终都以web.xml
      • 如果在web.xml和web-fragment.xml有多个相同 最后以web.xml的f为准
      • 如果使用同一配置多个元素,被作为一个listener对待
      • 合并产生的web.xml仅当其所有Web片段也都标记为时,才被视为。
      • web-fragment中配置的顶级的 和它包含内部元素 和 都会被忽略
      • jsp-property-group 额外添加的,在配置静态资源(META-INF/resources目录下),推荐使用jsp-config 使用url-pattern元素,其web-fragment.xml建一个子目录,防止jsp-property-group影响根目录的jsp
    • 对于(env-entry, ejb-ref, ejb-local-ref, service-ref, resource-ref, resource-env-ref, message-destination-ref, persistence-context-ref 和 persistence-unit-ref) 引用遵循如下规则:
      • 简单来在web.xml和web-fragment.xml相同元素,以web.xml为主,例如web.xml和web-fragment.xml都声明 使用相同, 那么最终生效是web.xml中的的元素
      • 如果两个web-fragment.xml定义相同元素,只能抛出异常,不能确定那个生效
      • 对于web-fragment.xml的元素,这些元素会合并到web.xml
    • 在web-fragment.xml的其他注解合并规则(@Resource, @Resources, @EJB, @EJBs, @WebServiceRef, @WebServiceRefs, @PersistenceContext, @PersistenceContexts, @PersistenceUnit, 和 @PersistenceUnits)
      • 如果将资源引用注释应用于类,则它等同于定义资源,但是不等同于定义注入目标。 在这种情况下,以上规则适用于注入目标元素。
      • 如果在字段上使用资源引用注释,则等效于在web.xml中定义注入目标元素。 但是,如果描述符中没有注入目标元素,则来自片段的注入目标仍将合并到上述的web.xml中。
      • 另一方面,如果主web.xml中有一个注入目标,并且有一个具有相同资源名称的资源引用注释,则将其视为资源引用注释的替代。 在这种情况下,由于在描述符中指定了注入目标,因此除了覆盖资源引用注释的值之外,还将应用上面定义的规则。
    • 如果data-source 元素被定义到两个fragment中,不存在于web.xml中,这个时候抛出异常、部署失败。
      • 例子
      • web.xml 没有资源引用定义
        
        web-fragment.xml
        <resource-ref>
        	<resource-ref-name="foo">
          	...
            <injection-target>
            	<injection-target-class>
              	com.foo.Bar.class
              </injection-target-class>
              <injection-target-name>
              	baz
              </injection-target-name>
            </injection-target>
          </resource-ref-name>
        </resource-ref>
        
        // 最终结果
        <resource-ref>
        	<resource-ref-name="foo">
          	...
            <injection-target>
            	<injection-target-class>
              	com.foo.Bar.class
              </injection-target-class>
              <injection-target-name>
              	baz
              </injection-target-name>
            </injection-target>
          </resource-ref-name>
        </resource-ref>
        
        、
                   
      • web.xml
        <resource-ref>
        	<resource-ref-name="foo">
          </resource-ref-name>
          ...
        </resource-ref>
        
        片段1(web-fragment.xml)
        <resource-ref>
        	<resource-ref-name="foo">
          	
          </resource-ref-name>
          <injection-target>
          	<injection-target-class>
            	com.foo.Bar.class
            </injection-target-class>
            <injection-target-name>
            	baz
            </injection-target-name>
          </injection-target>
        </resource-ref>
        
        片段2(web-fragment.xml)
        <resource-ref>
        	<resource-ref-name="foo"></resource-ref-name>
          <injection-target>
          	<injection-target-class>
            	com.foo.Bar2.class
            </injection-target-class>
            <injection-target-name>
            	baz2
            </injection-target-name>
          </injection-target>
        </resource-ref>
        
        //最终结果
        <resource-ref>
        	<resource-ref-name="foo"></resource-ref-name>
          <injection-target>
          	<injection-target-class>
            	com.foo.Bar2.class
            </injection-target-class>
            <injection-target-name>
            	baz2
            </injection-target-name>
          </injection-target>
           <injection-target>
          	<injection-target-class>
            	com.foo.Bar.class
            </injection-target-class>
            <injection-target-name>
            	baz
            </injection-target-name>
          </injection-target>
        </resource-ref>
        
        
                   
  • 例子
    • @WebServlet(urlPatterns="/MyPattern", initParams={@WebInitParam(name="ccc", value="333")})
      public class com.acme.Foo extends HttpServlet{
        ....
      }
      
      
      web.xml 
      <servlet>
      	<servlet-class>com.acme.Foo</servlet-class>
        <servlet-name>Foo</servlet-name>
        <init-param>
        	<param-name>aaa</param-name>
          <param-value>111</param-value>
        </init-param>
      </servlet>
      
      <servlet>
      	<servlet-class>com.acme.Foo</servlet-class>
        <servlet-name>Fum</servlet-name>
        <init-param>
        	<param-name>bbb</param-name>
          <param-value>222</param-value>
        </init-param>
      </servlet>
      <servlet-mapping>
      	<servlet-name>Foo</servlet-name>
        <url-pattern>/foo/*</url-pattern>
      </servlet-mapping>
      <servlet-mapping>
      	<servlet-name>Fum</servlet-name>
        <url-pattern>/fum/*</url-pattern>
      </servlet-mapping>
      //因为使用注解没有与web.xml的servlet-name 的名称相同,相等于新增一个servlet,如下xml格式
      <servlet>
      	<servlet-class>com.acme.Foo</servlet-class>
        <servlet-name>com.acme.Foo</servlet-name>
        
       	<init-param>
        	<param-name>ccc</param-name>
          <param-value>333</param-value>
        </init-param>
      </servlet>
      
      
      //如果上面web.xml用如下替换
      <servlet>
      	<servlet-class>com.acme.Foo</servlet-class>
        <servlet-name>com.acme.Foo</servlet-name>
        <init-param>
        	<param-name>aaa</param-name>
          <param-value>111</param-value>
        </init-param>
      </servlet>
      <servlet-mapping>
      	<servlet-name>com.acme.Foo</servlet-name>
        <url-pattern>/foo/*</url-pattern>
      </servlet-mapping>
      与注解class WebServlet 组成最终有效的文件
      
      <servlet>
      	<servlet-class>com.acme.Foo</servlet-class>
        <servlet-name>com.acme.Foo</servlet-name>
        <init-param>
        	<param-name>aaa</param-name>
          <param-value>111</param-value>
        </init-param>
        <init-param>
        	<param-name>ccc</param-name>
          <param-value>333</param-value>
        </init-param>
      </servlet>
      <servlet-mapping>
      	<servlet-name>com.acme.Foo</servlet-name>
        <url-pattern>/foo/*</url-pattern>
      </servlet-mapping>
      
                 

8.2.4、共享库/运行时可拔插性

  • HandlesTypes
  • ServletContainerInitializer

8.3、JSP容器插拔性

  • JSP容器处理jsp数据和TLD资源
  • servlet处理请求

8.4、处理注解和片段