目录

一、数据转换

1.ConversionService

2.自定义类型转换器

(1)案例一

(2)案例二

二、处理静态资源

三、关于 mvc:annotation-driven

四、@InitBinder

七、数据格式化

1.日期格式化

2.数值格式化

八、数据校验

1.JSR 303

2.Hibernate Validator拓展注解

3.Spring MVC数据校验

4.在目标方法中获取校验结果

5.在页面上显示错误

5.案例


一、数据转换

  • Spring MVC 上下文中内建了很多转换器,可完成大多数 Java 类型的转换工作。

1.ConversionService

  • ConversionService 是 Spring 类型转换体系的核心接口

  • 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC容器中定义一个 ConversionService. Spring 将自动识别出IOC 容器中的 ConversionService,并在 Bean 属性配置及Spring MVC 处理方法入参绑定等场合使用它进行数据的转换

  • 可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器

  • Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到ConversionServiceFactroyBean 中

    • Converter<S,T>:将 S 类型对象转为 T 类型对象

    • ConverterFactory:将相同系列多个 “同质” Converter 封装在一 起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类

    • GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换

2.自定义类型转换器

(1)案例一

  • 按照001,开发部这种格式将字符串转换为Department对象

Department.java

public class Departement {
    private Integer id;
    private  String name;
}

StringToDeptConverter.java

package com.itheima.converter;
​
import com.itheima.domain.Departement;
import org.springframework.core.convert.converter.Converter;
​
public class StringToDeptConverter implements Converter<String, Departement> {
​
    @Override
    public Departement convert(String s) {
        Departement departement = new Departement();
        //001,开发部
        String[] split = s.split(",");
        if (split != null && split.length != 0) {
            departement.setId(Integer.parseInt(split[0]));
            departement.setName(split[1]);
        }
        System.out.println("转换成功");
        return departement;
    }
}

dispatcher-servlet.xml

    <!-- 装配自定义的类型转换器 -->
    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
    <!-- 自定义的类型转换器 -->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="com.itheima.converter.StringToDeptConverter"></bean>
            </list>
        </property>
    </bean>

UserController.java

    @RequestMapping("/{formName}")
    public String loginForm(@PathVariable String formName) {
        //动态跳转页面
        return formName;
    }
​
    @RequestMapping(value = "/ConverterTest", method = RequestMethod.POST)
    public String ConverterTest(@RequestParam Departement department,Model model) {
        model.addAttribute("department",department);
        return "success";
    }

test.jsp

<form action="/ConverterTest" method="post">
  <!-- 输入xxx,xx格式的字符串 eg:001,开发部 -->
    请输入:<input  type="text" name="department"><br/>
    <input type="submit" value="登陆"/><br/>
</form>
  • 在dispatcher-servlet.xml配置文件中,使用了标签,该标签会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter两个bean,这是SpringMVC为@Controller分发请求所必须的

  • 除此之外,该标签还会注册一个默认的ConverService,即FormattingConversionServiceFactoryBean。如果需要使用自定义的ConverService转换类,需要显示定义一个ConverService来覆盖之前的默认实现类。

(2)案例二

  • 将String格式转换为Date格式

user.java

public class User {
    private String loginname;
    private Date birthday;
}

StringToDateConverter.java

package com.itheima.converter;
​
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
​
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
​
@Component
public class StringToDateConverter implements Converter<String, Date> {
    private String datePattern;
​
    public void setDatePattern(String datePattern) {
        this.datePattern = datePattern;
    }
​
    @Override
    public Date convert(String s) {
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat(this.datePattern);
            return dateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
            System.out.println("日期转换失败");
            return null;
        }
    }
}

dispatcher-servlet.xml

    <!-- 装配自定义的类型转换器 -->
    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
    <!-- 自定义的类型转换器 -->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="com.itheima.converter.StringToDateConverter">
                    <property name="datePattern" value="yyyy-MM-dd"></property>
                </bean>
                <bean class="com.itheima.converter.StringToDeptConverter"></bean>
            </list>
        </property>
    </bean>

UserController.java

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String register(@ModelAttribute User user, Model model) {
        System.out.println("register...");
        model.addAttribute("user", user);
        return "success";
    }

test.jsp

<form action="register" method="post">
    登录名:<input type="text" id="loginname" name="loginname" /><br/>
    生日:<input type="text" id="birthday" name="birthday"/><br/>
    <input type="submit" value="登陆"/><br/>
</form>

二、处理静态资源

  • 优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀

  • 若将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理,因找不到对应处理器将导致错误。

  • 可以在 SpringMVC 的配置文件中配置 的方式解决静态资源的问题:

    • 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由DispatcherServlet 继续处理

  • 一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定

    <!-- 默认使用基于注释的适配器和映射器 -->
    <mvc:annotation-driven/>
    <!-- 只把动态信息当做controller处理,忽略静态信息 -->
    <mvc:default-servlet-handler/>

三、关于 mvc:annotation-driven

  • <mvc:annotation-driven /> 会自动注册RequestMappingHandlerMapping

    、RequestMappingHandlerAdapter 与ExceptionHandlerExceptionResolver 三个bean。

  • 还将提供以下支持:

    • 支持使用 ConversionService 实例对表单参数进行类型转换

    • 支持使用 @NumberFormat annotation、@DateTimeFormat注解完成数据类型的格式化

    • 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证

    • 支持使用 @RequestBody 和 @ResponseBody 注解

四、@InitBinder

  • @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定

  • @InitBinder方法不能有返回值,它必须声明为void

  • @InitBinder方法的参数通常是是 WebDataBinder

@InitBinder
public void initBinder(WebDataBinder dataBinder){
  dataBinder.setDisallowedFields("roleSet");
}

七、数据格式化

  • Spring 在格式化模块中定义了一个实现ConversionService 接口的FormattingConversionService 实现类,该实现类扩展了 GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能

  • FormattingConversionService 拥有一个FormattingConversionServiceFactroyBean 工厂类,后者用于在 Spring 上下文中构造前者

  • FormattingConversionServiceFactroyBean内部已经注册了 :

    • NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用 @NumberFormat 注解

    • JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期– 类型的属性使用 @DateTimeFormat 注解

  • 装配了 FormattingConversionServiceFactroyBean 后,就可以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动了。 默认创建的ConversionService 实例即为FormattingConversionServiceFactroyBean

1.日期格式化

  • @DateTimeFormat 注解可对java.util.Date、java.util.Calendar、java.long.Long 时间类型进行标注

    • pattern 属性:类型为字符串。指定解析/格式化字段数据的模式,如:”yyyy-MM-dd hh:mm:ss”

    • iso 属性:类型为 DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式

    • style 属性:字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式:S:短日期/时间格式、M:中日期/时间格式、L:长日期/时间格式、F:完整日期/时间格式、-:忽略日期或时间格式

    @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date birth; 

2.数值格式化

  • @NumberFormat可对类似数字类型的属性进行标注,它拥有两个互斥的属性:

    • style:类型为 NumberFormat.Style。用于指定– 样式类型,包括三种:Style.NUMBER(正常数字类型)、Style.CURRENCY(货币类型)、 Style.PERCENT(百分数类型)

    • pattern:类型为 String,自定义样式,如patter="#,###";

    @NumberFormat(pattern="#,###,###.#")
    private Float salary;

八、数据校验

1.JSR 303

  • JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中

  • JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证

SpringMVC的数据转换、格式化和数据校验-LMLPHP

2.Hibernate Validator拓展注解

  • Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解

SpringMVC的数据转换、格式化和数据校验-LMLPHP

3.Spring MVC数据校验

  • Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR303 标准的校验框架

  • Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在 Spring MVC 中,可直接通过注解驱动的方式进行数据校验

  • Spring 的 LocalValidatorFactroyBean既实现了 Spring 的Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在 Spring 容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。

  • Spring 本身并没有提供 JSR303 的实现,所以必须将JSR303 的实现者的 jar 包放到类路径下。

  • 会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作

  • 在已经标注了 JSR303 注解的表单/命令对象前标注一个@Valid,Spring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验

  • Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或Errors 类型,这两个类都位于org.springframework.validation 包中

  • 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参

  • Errors 接口提供了获取错误信息的方法,如 getErrorCount()或getFieldErrors(String field)

  • BindingResult 扩展了 Errors 接口

4.在目标方法中获取校验结果

  • 在表单/命令对象类的属性中标注校验注解,在处理方法对应的入参前添加 @Valid,Spring MVC 就会实施校验并将校验结果保存在被校验入参对象之后的 BindingResult 或Errors 入参中。

  • 常用方法:

    • FieldError getFieldError(String field)

    • List<FieldError> getFieldErrors()

    • Object getFieldValue(String field)

    • Int getErrorCount()

5.在页面上显示错误

  • Spring MVC 除了会将表单/命令对象的校验结果保存到对应的 BindingResultErrors 对象中外,还会将所有校验结果保存到 “隐含模型”

  • 即使处理方法的签名中没有对应于表单/命令对象的结果入参,校验结果也会保存在 “隐含对象” 中

  • 隐含模型中的所有数据最终将通过 HttpServletRequest的属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取错误信息

  • 在 JSP 页面上可通过 <form:errors path=“userName”>显示错误消息

5.案例

导入相关jar包

SpringMVC的数据转换、格式化和数据校验-LMLPHP

user.java

public class User {
    @NotBlank(message="登录名不能为空")
    private String loginname;
    @Length(min=6,max=8,message = "密码长度必须在6-8位")
    private String password;
    @NotBlank(message = "用户名不能为空")
    private String username;
    @Range(min=10,max = 100,message = "年龄必须在10-100岁之间")
    private Integer age;
​
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Past(message = "生日必须是一个过去的日期")
    private Date birthday;
    @Email(message = "必须是合法的邮箱地址")
    private  String email;
    @Pattern(regexp = "[1][3,8][3,6,9][0-9]{8}",message = "无效的电话号码")
    private String phone;

UserController.java

@Controller
public class UserController {
​
    @RequestMapping("/{formName}")
    public String loginForm(@PathVariable String formName,Model model) {
        model.addAttribute("user",new User1());
        //动态跳转页面
        return formName;
    }
    @RequestMapping(value="/login", method=RequestMethod.POST)
    public String save(@Valid @ModelAttribute User user, Errors result,
                       Map<String, Object> map){
        System.out.println("save: " + user);
​
        if(result.getErrorCount() > 0){
            System.out.println("出错了!");
​
            for(FieldError error:result.getFieldErrors()){
                System.out.println(error.getField() + ":" + error.getDefaultMessage());
            }
​
            //若验证出错, 则转向定制的页面
            map.put("user", user);
            return "registerForm";
        }
​
        return "success";
    }
}

registerForm.jsp

<form:form modelAttribute="user" method="post" action="login">
    帐号:<form:input path="loginname"/>
    <form:errors path="loginname" cssStyle="color:red"/>
    <br/>
    密码:<form:password path="password"/>
    <form:errors path="password" cssStyle="color:red"/>
    <br/>
    用户名:<form:input path="username"/>
    <form:errors path="username" cssStyle="color:red"/>
    <br/>
    年龄:<form:input path="age"/>
    <form:errors path="age" cssStyle="color:red"/>
    <br/>
    邮箱:<form:input path="email"/>
    <form:errors path="email" cssStyle="color:red"/>
    <br/>
    生日:<form:input path="birthday"/>
    <form:errors path="birthday" cssStyle="color:red"/>
    <br/>
    电话:<form:input path="phone"/>
    <form:errors path="phone" cssStyle="color:red"/>
    <br/>
    <input type="submit" value="提交">
</form:form>

success.jsp

<h3>测试JSR 303</h3>
登录名:${requestScope.user.loginname}<br/>
密码:${requestScope.user.password}<br/>
用户名:${requestScope.user.username}<br/>
年龄:${requestScope.user.age}<br/>
邮箱:${requestScope.user.email}<br/>
生日:${requestScope.user.birthday}<br/>
电话:${requestScope.user.phone}<br/>

SpringMVC的数据转换、格式化和数据校验-LMLPHP

10-05 10:32