猿蜕变系列6——一文掌握springMVC必会技巧

时间:2022-07-22
本文章向大家介绍猿蜕变系列6——一文掌握springMVC必会技巧,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

看过之前的蜕变系列文章,相信你对springMVC有了一定的认识。对controller的花式编写,也有了一定的认识。今天我们来开启新的认识,讲一讲web开发中比较关键的东西,比如页面跳转,数据类型转换,以及数据校验。

随着前后端分离的套路大行其道,可能有的同学可能已经忘记了还有转发和重定向这两回事情了。我们先一起来回忆回忆这两个基础知识,面试常问的送分题。

转发:forward,是指服务器内部的跳转,转发的流程如下:客户端发起请求->服务端接受请求->调用内部的方法转发动作->将转发的资源返回给客户端。

重定向:redirect,是指服务器接受到客户端请求之后,响应302状态给客户端,让客户端对另一个资源(url)发起请求,再次请求的可以是服务器内部资源也可以是外部资源。

那么对于servlet来讲重定向就是调用HttpServletResponse的sendRedirect(“需要重定向的资源名”)方法,转发就是调用HttpServletRequest:request.getRequestDispatcher("需要转发的资源名").forword(request,response);

转发和重定向是两件不同的事情,他们的区别主要是:

1.转发对于客户端来讲是发起一次请求,而重定向则需要客户端发起两次请求。

2.转发时浏览器地址栏不会变,而重定向需要改变浏览器地址。

为了让大家有一个更加清楚的理解springMVC的转发和重定向,我们看下面这个例子:

编写两个新页面分别表示转发和重定向的页面:forward.jsp 和 redirect.jsp

forward.jsp:

<%@ page language="java" contentType="text/html;charset=utf-8"
    pageEncoding="utf-8"isELIgnored="false"%>
<html>
<head>
<title>forward样例</title>
</head>
<body>
   <h1>这是一个forward!浏览器地址不变!</h1>
</body>
</html>

redirect.jsp:

<%@ page language="java" contentType="text/html;charset=utf-8"
    pageEncoding="utf-8"isELIgnored="false"%>
<html>
<head>
<title>redirect样例</title>
</head>
<body>
   <h1>这是一个redirect!浏览器地址改变!</h1>
</body>
</html>
package com.pz.web.study.springmvc.controller;
 
import javax.servlet.http.HttpServletRequest;
impor tjavax.servlet.http.HttpServletResponse;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
@Controller
publicclass RedirectAndForwardDemo {
      
      
       @RequestMapping("/forward.do")
       public String forward(HttpServletRequestrequest,HttpServletResponse response) throws Exception {
             
              return"forward:forward.jsp";
        }
      
      
       @RequestMapping("/redirect.do")
       public String redirect(HttpServletRequest request,HttpServletResponse response) throws Exception{
             
              return"redirect:http://127.0.0.1/redirect.jsp";
             
        }
      
 
}

启动应用,分别访问

http://127.0.0.1/forward.do

http://127.0.0.1/redirect.do

在SpringMVC中使用转发:可以使用方法返回值为String的方法,return “forward:资源名称”,需要注意,一定要写资源相对于webapp目录的完整名称,因为使用转发,之前配置的视图解析器会失效。

在SpringMVC中使用重定向:可以使用方法返回值为String的方法,return “redirect:资源名称”,需要注意,重定向如果在内部可以写相对路径,但是大多数情况下重定向请求的是外部资源,所以例子用的是完整的url。

当然,你还可以使用HttpServetRequest,和HttpServletResponse来做也是可以的,只是这样做没有使用框架特性,就不一一列出了,有兴趣,自己做下体会下就好。

SpringMVC可以将请求里的参数会转化为对应的类型,是因为Spring MVC 提供了默认的类型转换器(converter)来做这些事情,但是默认的类型转换器并不是万能的,比如像日期类型(Date)就不能转换,为了解决个问题我们需要自定义类型转换器来做这些事情。

Spring MVC 提供了org.springframework.core.convert.converter.Converter<S,T>接口,用于用户自己实现数据类型转换的功能。我们看下接口定义:

public interface Converter<S, T> {
 
       /**
        *Convert the source of type S to target type T.
        * @param source the source object to convert, whichmust be an instance of S
        * @return the converted object, which must be aninstance of T
        * @throws IllegalArgumentException if the source could notbe converted to the desired target type
        */
       T convert(S source);
 
}

可以看出,这个接口是一个泛型接口,S代表转换前的数据类型,T代表转换后的数据类型。接下来我们编写代码实现自定义日期类型转换器:

package com.pz.web.study.springmvc.util;
 
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
import org.springframework.core.convert.converter.Converter;
 
/**
 * 自定义日期类型转换器
 * 日期格式:yyyy-MM-dd
 * @author pangzi
 *
 */
public class SimpleDateConverter implements Converter<String,Date>{
 
       @Override
       public Date convert(String dateStr) {
              if (dateStr != null && !"".equals(dateStr)) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            try {
                return sdf.parse(dateStr);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
 
      
 
}

日期转换器编写好了,我们怎么使用它呢?修改配置文件spring-servlet.xml文件,增加以下内容(注意:下面给出的是完整配置,请观察项目的配置文件):

  <!--配置类型转换器-->
   <bean id="dateConverter"class="com.pz.web.study.springmvc.util.SimpleDateConverter"/>
<!--配置类型转换服务bean-->
  <bean id="conversionService"class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters"ref="dateConverter"/>
 </bean>
 
 
<!-- 让Spring启用对annotation的支持-->
    <mvc:annotation-driven conversion-service="conversionService">
    <mvc:message-converters register-defaults="true">
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json</value>
                        <value>application/xml;charset=UTF-8</value>
                    </list>
                </property>
                <property name="features">
                    <list>
                    <!-- 默认的意思就是不配置这个属性,配置了就不是默认了 -->
                       <!-- 是否输出值为null的字段 ,默认是false-->
                        
                        <value>WriteMapNullValue</value>
                        
                        <value>WriteNullNumberAsZero</value>
                        <value>WriteNullListAsEmpty</value>
                        <value>WriteNullStringAsEmpty</value>
                        <value>WriteNullBooleanAsFalse</value>
                        <value>WriteDateUseDateFormat</value>
                    
                    </list>
                </property>
            </bean>
               
     </mvc:message-converters>
    </mvc:annotation-driven>

编写个Controller看看效果

package com.pz.web.study.springmvc.controller;
 
import java.util.Date;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
 
 
@Controller
public class DateConverterControllerDemo {
      
       @RequestMapping("/convertDate.do")
       public String convertDate(String name, String phone,int age,Date birthDay,Model model) throws Exception {
               model.addAttribute("name", name);
               model.addAttribute("phone", phone);
               model.addAttribute("age", age);
               model.addAttribute("birthDay", birthDay);
           return"showDateConvertForm";
        }
      
      
}

编写两个页面

dateConvertForm.jsp

<%@ page language="java" contentType="text/html;charset=utf-8"
    pageEncoding="utf-8"isELIgnored="false"%>
<html>
<head>
<title>spring form表单样例使用对象接收参数</title>
</head>
<body>
   <form action="convertDate.do"method=post>
    <lable>姓名:</lable>
      <input type="text"name="name" id="name"/><br />
    <lable>电话:</lable>
     <input type="text"name="phone" id="phone"/><br />
     <lable>年龄:</lable>
       <input type="text"name="age" id="age"/><br />
      
       <lable>年龄:</lable>
       <input type="text"name="birthDay" id="birthDay"/><br />
    
      <input type="submit"value="提交"id="submit" /><br />
   </form>
</body>
</html>

showDateConvertForm.jsp

<%@ page language="java" contentType="text/html;charset=utf-8"
    pageEncoding="utf-8"isELIgnored="false"%>
<html>
<head>
<title>spring form表单样例</title>
</head>
<body>
    <lable>姓名:</lable>
      ${name} <br />
    <lable>电话:</lable>
      ${phone}<br />
    <lable>年龄:</lable>
      ${age}<br />
     <lable>生日:</lable>
      ${birthDay}<br />
</body>
</html>

启动应用访问

http://127.0.0.1/dateConvertForm.jsp

录入页面信息,点击提交看效果。

当然,Spring MVC 也提供了注解的方式——@DateTimeFormat来解决日期的问题.只不过需要依赖joda time(当然joda time被JDK8版本以后纳入了JDK,如果你是使用JDK8以上版本,可以不依赖joda time)。

接下来,我们使用annotation的方式来处理日期问题:

在pom.xml中增加依赖:

    <!-- 支持日期注解 -->
              <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.9</version>
        </dependency>

编写Controller代码在DateConverterControllerDemo中新增方法:

@RequestMapping("/convertDateByAnnotation.do")
       public String convertDateByAnnotation(String name,String phone ,int age,@DateTimeFormat(pattern = "yyyy-MM-dd")Date birthDay,Model model) throws Exception {
               model.addAttribute("name", name);
               model.addAttribute("phone", phone);
               model.addAttribute("age", age);
               model.addAttribute("birthDay", birthDay);
           return"showDateConvertForm";
        }

编写页面代码:

<%@ page language="java" contentType="text/html;charset=utf-8"
    pageEncoding="utf-8"isELIgnored="false"%>
<html>
<head>
<title>使用annotation转换日期</title>
</head>
<body>
   <form action="convertDateByAnnotation.do"method=post>
    <lable>姓名:</lable>
      <input type="text"name="name" id="name"/><br />
    <lable>电话:</lable>
     <input type="text"name="phone" id="phone"/><br />
     <lable>年龄:</lable>
       <input type="text"name="age" id="age"/><br />
      
       <lable>生日:</lable>
       <input type="text"name="birthDay" id="birthDay"/><br />
    
      <input type="submit"value="提交"id="submit" /><br />
   </form>
</body>
</html>

http://127.0.0.1/dateConvertByAnnotationtForm.jsp

录入页面信息,点击提交看效果。

web程序开发嘛,有很多需要做数据校验的地方,比如对页面form表单的校验,我们经常使用JavaScript对form表单的数据进行校验。但是用户通过url的方式拼接或者使用工具随意传入非法参数怎么办(我们之前开发的例子中也有不少)?所以,在后端我们同样需要校验参数。用户的参数校验是一个比较复杂的问题,除了数据格式,还有业务数据校验,这里我们先说说数据格式的问题。给你安利一个叫做hibernate validator的家伙。

Java在JSR303 规范中提出了Bean Validation 规范,这个规范提出主要使用annotation的方式来实现对 Java Bean 的验证功能,这个规范的是实现者很多,其中hibernate的validator实现得比较好,也应用得比较广泛,这里我们主要讲解hibernatevalidator在Spring MVC中的使用。

我们先修改pom.xml增加hibernate validator的依赖:

<!-- hibernate-validator用于数据校验 -->

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-validator</artifactId>

<version>5.0.2.Final</version>

</dependency>

我们修改下User类增加hibernate-validator的annotation给属性增加一些约束:

package com.pz.web.study.springmvc.domain;
 
importj avax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
 
publicclass User {
      
       @NotNull(message = "请输入姓名")
    @Size(min = 4,max =20,message = "姓名长度必须在{min}-{max}之间")
       private String userName;
      
       @NotNull(message = "请输入手机号码")
    @Size(min = 4,max =20,message = "手机号码长度必须在{min}-{max}之间")
       @Pattern(regexp = "^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$", message = "手机号码格式不正确")
       private String cellPhone;
      
       @NotNull(message = "请输入年龄")
       @Min(value = 0,message = "年龄不能小于{value}")
       @Max(value =120,message = "年龄不能大于{value}")
       privateintage;
      
       private Address address;
 
       public String getUserName() {
              returnuserName;
       }
 
       publicvoidsetUserName(String userName) {
              this.userName = userName;
       }
 
       public String getCellPhone() {
              returncellPhone;
       }
 
       publicvoidsetCellPhone(String cellPhone) {
              this.cellPhone = cellPhone;
       }
 
       publicint getAge() {
              returnage;
       }
 
       publicvoid setAge(int age) {
              this.age = age;
       }
 
       public Address getAddress() {
              returnaddress;
       }
 
       publicvoidsetAddress(Address address) {
              this.address = address;
       }
      
      
      
      
 
}
 
 

修改spring-servlet.xml配置文件,增加hibernate-validator验证器相关配置:

<!--验证器-->
<bean id="hibernateValidator"class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass"value="org.hibernate.validator.HibernateValidator"/>
</bean>
 
 
 
<!-- 让Spring启用对annotation的支持-->
    <mvc:annotation-driven conversion-service="conversionService"validator="hibernateValidator">
    <mvc:message-converters register-defaults="true">
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json</value>
                        <value>application/xml;charset=UTF-8</value>
                    </list>
                </property>
                <property name="features">
                    <list>
                    <!-- 默认的意思就是不配置这个属性,配置了就不是默认了 -->
                       <!-- 是否输出值为null的字段 ,默认是false-->
                        
                        <value>WriteMapNullValue</value>
                        
                        <value>WriteNullNumberAsZero</value>
                        <value>WriteNullListAsEmpty</value>
                        <value>WriteNullStringAsEmpty</value>
                        <value>WriteNullBooleanAsFalse</value>
                        <value>WriteDateUseDateFormat</value>
                    
                    </list>
                </property>
            </bean>
               
     </mvc:message-converters>
    </mvc:annotation-driven>

编写Controller,方法中需要User和BindingResult,在需要校验的User前增加@Validaed,我们可以通过BindingResult获取验证错误信息,并用于页面输出:

package com.pz.web.study.springmvc.controller;
 
import java.util.List;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.pz.web.study.springmvc.domain.User;
 
@Controller
public class HibernateValidatorControllerDemo {
      
      
       @RequestMapping("/validateDataForm.do")
       public String validateDataForm(@Validated User user,BindingResult bingResult,Modelmodel) throws Exception {
             
              List<ObjectError> allErrors= bingResult.getAllErrors();
        if (allErrors != null && allErrors.size() > 0) {
            FieldError nameError =bingResult.getFieldError("userName");
            FieldError ageError =bingResult.getFieldError("age");
            FieldError phoneError =bingResult.getFieldError("cellPhone");
        
            if (nameError != null) {
                model.addAttribute("nameError", nameError.getDefaultMessage());
            }
            if (ageError != null) {
                model.addAttribute("ageError", ageError.getDefaultMessage());
            }
            if (phoneError != null) {
                model.addAttribute("phoneError", phoneError.getDefaultMessage());
            }
           
           return"../validateDataForm";
        }
 
               model.addAttribute("user", user);
           return"showDateConvertForm";
        }
 
}

编写两个jsp页面

validateDataForm.jsp

<%@ page language="java" contentType="text/html;charset=utf-8"
    pageEncoding="utf-8"isELIgnored="false"%>
<html>
<head>
<title>spring form Hibernate-Validator校验表单</title>
</head>
<body>
   <form action="/validateDataForm.do"method=post>
    <lable>姓名:</lable>
      <input type="text"name="userName" id="userName"/> ${nameError}<br />
    <lable>电话:</lable>
     <input type="text"name="cellPhone" id="cellPhone"/> ${phoneError}<br />
     <lable>年龄:</lable>
       <input type="text"name="age" id="age"/> ${ageError}<br />
      <input type="submit"value="提交" id="submit"/><br />
     
   </form>
</body>
</html>

showValidateDataForm.jsp

<%@ page language="java" contentType="text/html;charset=utf-8"
    pageEncoding="utf-8"isELIgnored="false"%>
<html>
<head>
<title>spring form Hibernate-Validator校验表单</title>
</head>
<body>
   <lable>姓名:</lable>
      ${user.userName} <br />
    <lable>电话:</lable>
      ${user.cellPhone}<br />
    <lable>年龄:</lable>
      ${user.age}<br />
      <lable>省:</lable>
      ${user.address.provice}
      <br /> 
</body>
</html>

启动应用输入

http://127.0.0.1/validateDataForm.jsp

查看页面效果。

HibernateValidator 中的常用验证注解

@Null被注释的元素必须为 null

@NotNull 被注释的元素必须不为 null

@AssertTrue 被注释的元素必须为 true

@AssertFalse 被注释的元素必须为 false

@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Size(max,min) 被注释的元素的大小必须在指定的范围内

@Digits(integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内

@Past 被注释的元素必须是一个过去的日期

@Future 被注释的元素必须是一个将来的日期

@Pattern(value) 被注释的元素必须符合指定的正则表达式

@Email

被注释的元素必须是电子邮箱地址

@Length(min=, max=)

被注释的字符串的大小必须在指定的范围内

@NotEmpty

被注释的字符串的必须非空

@Range(min=, max=)

被注释的元素必须在合适的范围内