17、Spring MVC 的高级技术(1)(spring笔记)

一、Spring MVC 配置的替代方案

在之前我们通过扩展AbstractAnnotationConfigDispatcherServletInitializer快速搭建了Spring MVC环境,这个便利的基础类会帮我们创建需要的DispatcherServlet和ContextLoaderListener对象。但是在实际应用中,除了DispatcherServlet之外,可能还需要额外的ServletFilter;或者还需要对DispatcherServlet本身做一些额外的配置;又或者需要将应用部署到Servlet3.0之前的容器中,此时则需要将DispatcherServlet配置到传统的web.xml中。

1.1 自定义 DispatcherServlet配置

在之前使用Java配置方式中,我们编写SpittrWebApplicationInitializer配置类继承AbstractAnnotationConfigDispatcherServletInitializer,对其中的三个方法进行了重载,但是这三个方法只是必须要重载的抽象方法,实际上还有更多方法可以重载。比如customizeRegistration()方法,在AbstractAnnotationConfigDispatcherServletInitializerDispatcherServlet注册到Servlet容器之后,就会调用此方法,并将Servlet注册后得到的Registration.Dynamic对象传递进来,我们可以通过此对象对DispatcherServlet进行一些额外的配置。

1.2 添加其他的 Servlet和 Filter

如果应用中除了DispatcherServlet这个Servlet需要注册外,还有其他的ServletFilter的话,怎样将其注册到Servlet容器中?

基于Java的初始化器的一个好处就在于我们可以定义任意数量的初始化器类。因此,如果想往Web容器中注册其他组件的话,只需创建一个新的初始化器就可以了。最简单的方式就是实现WebApplicationInitializer接口(其中AbstractAnnotationConfigDispatcherServletInitializer就继承了此类。)

public class MyServletInitializer implements WebApplicationInitializer{

    public void onStartup(ServletContext servletContext) throws ServletException{
        javax.servlet.ServletRegistration.Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);
        myServlet.addMapping("/custom/**");
    }
}

说明:这个类中MyServlet类就是我们自定义的Servlet类,同时配置了其映射路径,当然我们也可以这样手动配置DispatcherServlet类,但是没有必要。同理,还可以使用这种方式添加其他的FilterListener类:

public void onStartup(ServletContext servletContext) throws ServletException{
    javax.servlet.FilterRegistration.Dynamic myFilter = servletContext.addFilter("myFilter", MyFilter.class);
    myFilter.addMappingForUrlPatterns(null, false, "/custom/**");
}

说明:这里的Dynamic类和之前的不一样。如果要将应用部署到支持Servlet3.0的容器中,那么这是一种通用的方式。而如果只是注册Filter,并且该Filter只会映射到DispatcherServlet上的话,那么AbstractAnnotationConfigDispatcherServletInitializer中还有一种快捷方式。只需要重载其getServletFilters()(其实是其父类中的方法)方法。

@Override
protected Filter[] getServletFilters(){
  return new Filter[] { new MyFilter() };
}

说明:这个方法返回一个javax.servlet.Filter的数组,在这里没有必要声明它的映射路径,此方法返回的所有Filter都会映射到DispatcherServlet上。

以上介绍的方式都是将应用部署到Servlet3.0容器中的情况,这种方式下不用创建web.xml文件。但是如果要部署到Servlet3.0之前的容器中,则需要配置web.xml文件了。

1.3 在web.xml 中声明 DispatcherServlet

这种方式配置下,我们需要自己来注册DispatcherServletContextLoaderListener。如下:


    contextConfigLocation
    /WEB-INF/spring/root-context.xml



    
        org.springframework.web.context.ContextLoaderListener
    



    appServlet
    org.springframework.web.servlet.DispatcherServlet
    1



    appServlet
    /

说明:上下文contextConfigLoaction中指定了初始化文件地址,这个文件定义了根应用上下文,会被ContextLoaderListener加载。而DispatcherServlet会根据Servlet的名字找到一个文件,并基于该文件加载应用上下文。这里Servlet名字是appServlet,因此对应的文件为“/WEB-INF/appServlet-context.xml”,当然我们也可以显示指定其路径:


    appServlet
    org.springframework.web.servlet.DispatcherServlet
    
        contextConfigLocation
        /WEB-INF/appServlet-context.xml
    
    1



    appServlet
    /

说明:以上的配置中DispatcherServletContextLoaderListener要加载的配置文件都是XML文件,我们也可以使用Java配置。


    contextClass
    
    org.springframework.web.context.support.AnnotationConfigWebApplicationContext



    contextConfigLocation
    
    spittr.config.RootConfig



    
        org.springframework.web.context.ContextLoaderListener
    



    appServlet
    org.springframework.web.servlet.DispatcherServlet
    
        contextClass
        
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    
    
    
        contextConfigLocation
        
        spittr.config.WebConfig
    
    1



    appServlet
    /

说明:这里的两个配置类在之前的文章中已经给出,这里只需要配置使用Java配置,在初始化的时候就会从带有@Configuration注解的类上加载配置,所以RootConfig.javaWebConfig.java中都需要使用这个注解。

二、处理 multipart 形式的数据

一般表单提交所形成的请求结果是很简单的,如GET方式中,就是以多个“&”符号分割多个name-value对。但是对于传送二进制文件,如图片上传,就不行了。而multipart格式的数据会将一个表单拆分为多个部分(part),每个部分对应一个输入域。在一般地表单输入域中,它所对应的部分中会放置文本型数据,但是如果是上传文件的话,它所对应的可以是二进制,如下:

------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="firstName"

Charles
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="lastName"

Xavier
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="email"

[email protected]
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="username"

professorx
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="password"

letmein01
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="profilePicture" filename="me.jpg"
Content-Type: image/jpeg

    [[ Binary image data goes here]]
------WebKitFormBoundaryqgkaBn8IHJCuNmiW------

说明:这个multipart请求中,可以看到profilePicture部分与其他部分明显不同,指明自己的Content-Type头为一个JPEG的图片。此时请求的就是二进制数据了。虽然看起来很复杂,但是在Spring MVC中处理却很简单,只需要配置一个multipart解析器即可,通过它来告诉DispatcherServlet该如何读取这种请求。

2.1 配置multipart 解析器

DispatcherServlet并没有实现任何解析multipart请求数据的功能,而是将其委托给了Spring中的MultipartResolver策略接口的实例。从Spring3.1开始,Spring内置了两个MultipartResolver实现类供我们选择:

  • CommonsMultipartResolver:使用Jakarta Commons FileUpload(即Apache的一个项目)解析multipart请求。
  • StandardServletMultipartResolver:依赖于Servlet3.0multipart请求的支持(始于Spring3.1)。

一般来讲,后者可能会是优选方案,但是要注意各自使用的范围。

2.1.1 使用 Servlet3.0 解析 multipart 请求

兼容Servlet3.0StandardServletMultipartResolver没有构造器参数,也没有要设置的属性。这样,在Spring应用上下文中,将其声明为bean就简单了。

@Bean
public MultipartResolver multipartResolver() throws IOException{
  return new StandardMultipartResolver();
}

说明:将其配置为bean很简单,那如何限制上传文件的大小,如何指定文件上传时临时写入的目录?其实是有办法配置相关限制条件的,只是不是在Spring中配置,而是要在Servlet中指定multipart的配置。这里必须要指定临时写入的目录,否则无法正常工作。具体来讲,必须要在web.xmlServlet初始化类中,将multipart的具体限制条件作为DispatcherServlet配置的一部分。之前讲过,可以对DispatcherServlet进行一些额外的配置,所以这里可以这样配置:

DispatcherServlet ds = new DispatcherServlet();
javax.servlet.ServletRegistration.Dynamic registration = servletContext.addServlet("appServlet", ds);
registration.addMapping("/");
registration.setMultipartConfigElement(new MultipartConfigElement("/tmp/spittr/uploads"));

说明:这里将临时文件设置为了"/tmp/spittr/uploads",这是一种很基本的方式,如果我们配置DispatcherServlet的自定义Servlet继承了AbstractAnnotationConfigDispatcherServletInitializer或其直接父类的话,可以直接重载customizeRegistration()方法进行配置:

@Override
protected void customizeRegistration(Dynamic registration){
  registration.setMultipartConfigElement(new MultipartConfigElement("/tmp/spittr/uploads"));
}

但目前为止,使用的是只有一个参数的MultipartConfigElement构造器,这个参数指定的是文件系统中的一个绝对目录,上传文件将会写入临时写入该目录中。但是还可以通过其他构造器来限制上传文件的大小。其他参数有:

  • 上传文件的最大容量(即每个文件的大小最大值,以字节为单位)。默认是没有限制的。
  • 整个multipart请求的最大容量(字节),不会关心有多少个part以及每个part的大小。默认是没有限制的。
  • 在上传的过程中,如果文件大小达到了一个指定最大容量(字节),将会写入到临时文件路径中。默认值为零,也就是所有上传文件都会写入到磁盘。是不是可以这样理解,默认就是直接写入到磁盘,而不是使用内存缓存一定大小之后再写入到磁盘?

例如,我们想限制文件的大小不超过2MB,整个请求不超过4MB,而且所有的文件都要写到磁盘中,配置如下:

@Override
protected void customizeRegistration(Dynamic registration){
  registration.setMultipartConfigElement(new MultipartConfigElement("/tmp/spittr/uploads", 2097152, 4194304, 0));
}

当然也可以在web.xml中配置:


    appServlet
    org.springframework.web.servlet.DispatcherServlet
    1
    
        /tmp/spittr/uploads
        2097152
        4194304
    

注意:必须要配置路径。

2.1.2 配置Jakarta Commons FileUpload multipart 解析器

如果我们需要将应用部署到Servlet3.0之前的容器中,则可以使用Jakarta Commons FileUpload multipart来替代,当然也可以自己编写MultipartResolver实现,但是没哟必要。Spring内置了CommonsMultipartResolver,可以作为StandardServletMultipartResolver的替代方案。

CommonsMultipartResolver声明为Spring bean的最简单方式如下:

@Bean
public MulitipartResolver multipartResolver(){
  return new CommonsMultipartResolver();
}

说明:StandardServletMultipartResolver不同,CommonsMultipartResolver不会强制要求设置临时文件路径。默认情况下,这个路径就是Servlet容器的临时目录。当然也可以手动指定目录。

@Bean
public MulitipartResolver multipartResolver() throws IOException{
  CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
  multipartResolver .setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));
  return multipartResolver ;
}

说明:实际上,还可以设置更多细节,如文件大小之类的。

@Bean
public MulitipartResolver multipartResolver() throws IOException{
  CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
  multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));
  multipartResolver.setMaxUploadSize(2097152);
  multipartResolver.setMaxInMemorySize(0);
  return multipartResolver ;
}

说明:这里将最大的文件大小为2MB,最大内存大小为0,分别对应MultipartConfigElement的第二个和第四个构造器参数。无法设定总共上传文件大小之和。

你可能感兴趣的:(17、Spring MVC 的高级技术(1)(spring笔记))