第7章-Spring MVC的高级技术

Spring MVC的高级技术

学习内容

Spring MVC配置的替代方案

处理文件上传

在控制器中处理异常

使用flash属性

Spring MVC配置的替代方案

尽管对很多Spring应用来说,使用AbstractAnnotationConfigDispatcherServletInitializer基本能够满足我们的需求,但并不一定总能满足我们的要求,除了DispatcherServlet以外,我们可能还需要额外的ServletFilter;我们可能还需要对DispatcherServlet本身做一些额外的配置;或者,如果我们需要将应用部署到Servlet3.0之前的容器中,那么还需要将DispatcherServlet配置到传统的web.xml中。

1、自定义DispatcherServlet配置

AbstractAnnotationConfigDispatcherServletInitializer的方法之一customizeRegistration()。在AbstractAnnotationConfigDispatcherServletInitializerDispatcherServlet注册到Servlet容器中之后,就会调用customizeRegistration(),并将Servlet注册后得到的Registration.Dynamic传递进来。通过重载customizeRegistration()方法,我们可以对DispatcherServlet进行额外的配置。

Spring MVC中处理multipart请求和文件上传。如果计划使用Servlet3.0multipart配置的支持,那么需要使用DispatcherServletRegistration来启用multipart请求。我们可以重载customizeRegistration()方法来设置MultipartConfigElement

  @Override

  protected void customizeRegistration(Dynamic registration) {

    registration.setMultipartConfig(

        new MultipartConfigElement("/tmp/spittr/uploads", 2097152, 4194304, 0));

  }

借助customizeRegistration()方法中的ServletRegistration.Dynamic,我们能够完成多项任务,包括通过调用setLoadOnStartup()设置load-on-startup优先级,通过setInitParameter()设置初始化参数,通过调用setMutipartConfig()配置Servlet 2.0mutipart的支持。在前面的样例中,我们设置了对Multipart的支持,将上传文件的临时存储目录设置在“/tmp/spittr/uploads”中。

2、添加其他的Servlet和Filter

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

通过实现WebApplicationInitializer来注册Servlet

package spittr.config;

import javax.servlet.ServletContext;

import javax.servlet.ServletException;

import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.WebApplicationInitializer;

public class MyServletInitializer implements WebApplicationInitializer{

public void onStartup(ServletContext servletContext) throws ServletException {

Dynamic myServlet =

servletContext.addServlet("myServlet", MyServlet.class);

myServlet.addMapping("/custom/**");

}

}

他注册了一个Servlet并将其映射到一个路径上。

类似地,我们还可以创建新的WebApplicationInitializer实现来注册ListenerFilter

注册FilterWebApplicationInitializer

package spittr.config;

import javax.servlet.ServletContext;

import javax.servlet.ServletException;

import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.WebApplicationInitializer;

public class MyServletInitializer implements WebApplicationInitializer{

public void onStartup(ServletContext servletContext) throws ServletException {

Dynamic myServlet =

servletContext.addServlet("myServlet", MyServlet.class);

myServlet.addMapping("/custom/**");

javax.servlet.FilterRegistration.Dynamic filter =

servletContext.addFilter("myFilter", MyFilter.class);

filter.addMappingForUrlPatterns(null, false, "/custom/**");

}

}

如果要将应用部署到支持Servlet3.0的容器中,那么WebApplicationInitializer提供了一种通用的方式,实现在Java中注册ServletFilterListener。不过,如果你只是注册Filter,并且该Filter只会映射到DispatcherServlet上的话,那么在AbstractAnnotationConfigDispatcherServletInitializer中还有一种快捷方式。

为了注册Filter并将其映射到DispatcherServlet,所需要做的仅仅是重载AbstractAnnotationConfigDispatcherServletInitializergetServletFilters()方法。

@Override

protected Filter[] getServletFilters() {

return new Filter[] { new MyFilter() };

}

3、在web.xml中声明DispatcherServlet

如下是一个基本的web.xml文件,他按照传统的方式搭建了DispatcherServletContextLoaderListener

 

 

处理Multipart形式的数据

1、背景

web应用中,允许用户上传内容是很常见的需求。Multipart格式的数据会将一个表单拆分为多个部分(part),每个部分对应一个输入域。在一般的表单输入域中,他所对应的部分中会放置文本型数据,但是如果上传文件的话,他所对应的部分可以是二进制。

尽管Multipart请求看起来很复杂,但在Spring MVC中处理他们却很容易。在编写控制器方法处理文件上传之前,我们必须要配置一个Multipart解析器,通过他来告诉DispatcherServlet该如何读取Multipart请求。

2、配置Multipart解析器

DispatcherServlet并没有实现任何解析Multipart请求数据的功能。他将该任务委托给了SpringMultipartResolver策略接口的实现,通过这个实现类来解析Multipart请求中的内容。从Spring3.1开始,Spring内置了两个MultipartResolver的实现供我们选择:

CommonsMultipartResolver:使用Jakarta Commons FileUpload解析Multipart请求;

StandardServletMultipartResolver:依赖于Servlet3.0Multipart请求的支持(始于Spring3.1)

一般来讲,StandardServletMultipartResolver可能会是优选的方案。

使用Servlet3.0解析Multipart请求

Spring应用上下文中,声明Bean

  @Bean

  public MultipartResolver multipartResolver() throws IOException {

    return new StandardServletMultipartResolver();

  }

要在Servlet中指定Multipart的配置

采用Servlet初始化类的方式来配置DispatcherServlet的话,实现了WebApplicationInitializer

public class MyServletInitializer implements WebApplicationInitializer{

public void onStartup(ServletContext servletContext) throws ServletException {

DispatcherServlet ds = new DispatcherServlet();

Dynamic myServlet =servletContext.addServlet("appServlet", ds);

myServlet.addMapping("/");

myServlet.setMultipartConfig(

new MultipartConfigElement("/tmp/spittr/uploades"));

}

}

如果我们配置DispatcherServletServlet初始化类继承了AbstractAnnotationConfigDispatcherServletInitializer的话:

  @Override

  protected void customizeRegistration(Dynamic registration) {

    registration.setMultipartConfig(

        new MultipartConfigElement("/tmp/spittr/uploads", 2097152, 4194304, 0));

  }

3、处理Multipart请求

Thymeleaf在很大程度上就是HTML文件,与JSP不同,他没有什么特殊的标签或标签库。Thymeleaf之所以能够发挥作用,是因为他通过自定义的命名空间,为标准的HTML标签集合添加Thymeleaf属性。

命名空间为:

<html xmlns="http://www.w3.org/1999/xhtml"

      xmlns:th="http://www.thymeleaf.org">

th:href属性的特殊之处在于他的值中可以包含Thymeleaf表达式,用来计算动态的值。他会渲染成一个标准的href属性,其中会包含在渲染时动态创建得到的值,这是Thymeleaf命名空间中很多属性的运行方式:他们对应标准的HTML属性,并且具有相同的名称,但是会渲染一些计算后得到的值。

借助Thymeleaf实现表单绑定

<form method="POST" th:object="${spitter}">

  <div class="errors" th:if="${#fields.hasErrors('*')}">

    <ul>

      <li th:each="err : ${#fields.errors('*')}" 

        th:text="${err}">Input is incorrect</li>

    </ul>

  </div>

  <label th:class="${#fields.hasErrors('firstName')}? 'error'">First Name</label>:

  <input type="text" th:field="*{firstName}"  

                 th:class="${#fields.hasErrors('firstName')}? 'error'" /><br/>

  <label th:class="${#fields.hasErrors('lastName')}? 'error'">Last  Name</label>:

  <input type="text" th:field="*{lastName}"

                 th:class="${#fields.hasErrors('lastName')}? 'error'" /><br/>

  <label th:class="${#fields.hasErrors('email')}? 'error'">Email     </label>:

  <input type="text" th:field="*{email}"

                 th:class="${#fields.hasErrors('email')}? 'error'" /><br/>

  

  <label th:class="${#fields.hasErrors('username')}? 'error'">Username  </label>:

  <input type="text" th:field="*{username}"

                 th:class="${#fields.hasErrors('username')}? 'error'" /><br/>

  

  <label th:class="${#fields.hasErrors('password')}? 'error'">Password  </label>:

  <input type="password" th:field="*{password}"  

                 th:class="${#fields.hasErrors('password')}? 'error'" /><br/>

  <input type="submit" value="Register" />

      </form>

${}表达式是变量表达式,

*{}表达式是选择表达式,

变量表达式是基于整个SpEL上下文计算的,而选择表达式是基于某一个选中对象计算的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值