Servlet3.0

四、可插性支持

如果说3.0版本新增的注解支持是为了简化Servlet/过滤器/监听器的声明,从而使得web.xml变为可选配置,那么新增的可插性(pluggability)支持则将 Servlet配置的灵活性提升到了新的高度。熟悉Struts2的开发者都知道,Struts2通过插件的形式提供了对包括Spring在内的各种开发框架的支持,开发者甚至可以自己为Struts2开发插件,而Servlet的可插性支持正是基于这样的理念而产生的。使用该特性,现在我们可以在不修改已有Web应用的前提下,只需将按照一定格式打成的JAR包放到WEB-INF/lib目录下,即可实现新功能的扩充,不需要额外的配置。

Servlet 3.0引入了称之为“Web模块部署描述符片段”的web-fragment.xml 部署描述文件,该文件必须存放在JAR 文件的META-INF目录下,该部署描述文件可以包含一切可以在web.xml中定义的内容。JAR包通常放在WEB-INF/lib目录下,除此之外,所有该模块使用的资源,包括class文件、配置文件等,只需要能够被容器的类加载器链加载的路径上,比如classes目录等。

现在,为一个Web应用增加一个Servlet配置有如下三种方式(过滤器、监听器与Servlet三者的配置都是等价的,故在此以Servlet配置为例进行讲述,过滤器和监听器具有与之非常类似的特性):

方式一:

编写一个类继承自HttpServlet,将该类放在classes目录下的对应包结构中,修改web.xml,在其中增加一个Servlet声明。这是最原始的方式。

方式二:

编写一个类继承自HttpServlet,并且在该类上使用@WebServlet注解将该类声明为Servlet,将该类放在classes目录下的对应包结构中,无需修改web.xml文件。

方式三:

编写一个类继承自HttpServlet,将该类打成JAR包,并且在JAR包的 META-INF目录下放置一个web-fragment.xml文件,该文件中声明了相应的 Servlet配置。web-fragment.xml文件示例如下:

<?xml version="1.0" encoding="UTF-8"?>

<web-fragment

    xmlns=http://java.sun.com/xml/ns/javaee

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

    http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"

    metadata-complete="true">

    <servlet>

        <servlet-name>fragment</servlet-name>

        <servlet-class>com.atguigu.FragmentServlet</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>fragment</servlet-name>

        <url-pattern>/fragment</url-pattern>

    </servlet-mapping>

</web-fragment>

 

从上面的示例可以看出,web-fragment.xml与web.xml除了在头部声明的 XSD引用不同之外,其主体配置与web.xml是完全一致的。

由于一个Web应用中可以出现多个web-fragment.xml声明文件,加上一个 web.xml文件,加载顺序问题便成了不得不面对的问题。Servlet规范的专家组在设计的时候已经考虑到了这个问题,并定义了加载顺序的规则。

web-fragment.xml包含了两个可选的顶层标签,<name>和<ordering>,如果希望为当前的文件指定明确的加载顺序,通常需要使用这两个标签,<name>主要用于标识当前的文件,而<ordering>则用于指定先后顺序。一个简单的示例如下:

<web-fragment...>

    <name>FragmentA</name>

    <ordering>

        <after>

            <name>FragmentB</name>

            <name>FragmentC</name>

        </after>

    <before>

        <others/>

    </before>

    </ordering>

    ...

</web-fragment>

如上所示,<name>标签的取值通常是被其它web-fragment.xml文件在定义先后顺序时引用的,在当前文件中一般用不着,它起着标识当前文件的作用。

在<ordering>标签内部,我们可以定义当前web-fragment.xml文件与其他文件的相对位置关系,这主要通过<ordering>的<after>和<before>子标签来实现的。在这两个子标签内部可以通过<name>标签来指定相对应的文件。比如:

<after>

    <name>FragmentB</name>

    <name>FragmentC</name>

</after>

以上片段则表示当前文件必须在FragmentB和FragmentC之后解析。<before> 的使用于此相同,它所表示的是当前文件必须早于<before>标签里所列出的 web-fragment.xml文件。

除了将所比较的文件通过<name>在<after>和<begin>中列出之外,Servlet 还提供了一个简化的标签<others/>。它表示除了当前文件之外的其他所有的 web-fragment.xml文件。该标签的优先级要低于使用<name>明确指定的相对位置关系。

五、ServletContext 的性能增强

除了以上的新特性之外,ServletContext对象的功能在新版本中也得到了增强。现在,该对象支持在运行时动态部署Servlet、过滤器、监听器,以及为Servlet 和过滤器增加URL映射等。以Servlet为例,过滤器与监听器与之类似。

ServletContext为动态配置Servlet增加了如下方法:

ServletRegistration.Dynamic addServlet(String servletName,Class<? extends Servlet> servletClass)

ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)

ServletRegistration.Dynamic addServlet(String servletName, String className)

<T extends Servlet> T createServlet(Class<T> clazz)

ServletRegistration.getServletRegistration(String servletName) Map<String,? extends ServletRegistration> getServletRegistrations()

 

其中前三个方法的作用是相同的,只是参数类型不同而已;通过 createServlet()方法创建的Servlet,通常需要做一些自定义的配置,然后使用 addServlet()方法来将其动态注册为一个可以用于服务的Servlet。两个 getServletRegistration()方法主要用于动态为Servlet增加映射信息,这等价于在 web.xml(抑或web-fragment.xml)中使用<servlet-mapping>标签为存在的Servlet 增加映射信息。

以上ServletContext新增的方法要么是在ServletContextListener的 contexInitialized方法中调用,要么是在ServletContainerInitializer的onStartup()方法中调用。

ServletContainerInitializer也是Servlet 3.0新增的一个接口,容器在启动时使用JAR服务API(JAR Service API)来发现ServletContainerInitializer的实现类,并且容器将WEB-INF/lib目录下JAR包中的类都交给该类的onStartup()方法处理,我们通常需要在该实现类上使用@HandlesTypes注解来指定希望被处理的类,过滤掉不希望给onStartup()处理的类。

 

六、HttpServletRequest 对文件上传的支持

此前,对于处理上传文件的操作一直是让开发者头疼的问题,因为Servlet 本身没有对此提供直接的支持,需要使用第三方框架来实现,而且使用起来也不够简单。如今这都成为了历史,Servlet 3.0已经提供了这个功能,而且使用也非常简单。为此,HttpServletRequest提供了两个方法用于从请求中解析出上传的文件:

Part getPart(String name)

Collection<Part> getParts()

前者用于获取请求中给定name的文件,后者用于获取所有的文件。每一个文件用一个javax.servlet.http.Part对象来表示。该接口提供了处理文件的简易方法,比如write()、delete()等。至此,结合HttpServletRequest和Part来保存上传的文件变得非常简单,如下所示:

Part photo = request.getPart("photo");

photo.write("/tmp/photo.jpg");

// 可以将两行代码简化为 request.getPart("photo").write("/tmp/photo.jpg") 一行。

另外,开发者可以配合前面提到的@MultipartConfig注解来对上传操作进行一些自定义的配置,比如限制上传文件的大小,以及保存文件的路径等。其用法非常简单,故不在此赘述了。

需要注意的是,如果请求的MIME类型不是 multipart/form-data,则不能使用上面的两个方法,否则将抛异常。

 

七、总结

Servlet 3.0 的众多新特性使得Servlet开发变得更加简单,尤其是异步处理特性和可插性支持的出现,必将对现有的MVC框架产生深远影响。虽然我们通常不会自己去用Servlet编写控制层代码,但是也许在下一个版本的Struts中,您就能切实感受到这些新特性带来的实质性改变。

 

本教程由尚硅谷教育大数据研究院出品,如需转载请注明来源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值