springMVC高级篇

1 springMVC组件开发之拦截器

1.1自定义拦截器类css

public class RoleInterceptor implements HandlerInterceptor {  
    /** 
     * 拦截器处理器处理以前会先通过该方法:前置方法 
     * @return 若是返回true,会进入(放行)下一个拦截器(链) 
     */  
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        System.out.println("前置方法,返回true,进入后面的处理流程,返回false,完成处理,请求结束");  
        return true;  
    }  
  
    @Override  
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {  
        System.out.println("后置方法,处理器完成处理,视图渲染以前调用");  
    }  
  
    @Override  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {  
        System.out.println("完成方法:视图渲染完成");  
    }  
}

1.2 配置拦截器html

java配置:在springMVCConfig类上配置前端

package com.wise.tiger.config;  
  
//*************************import******************************//  
iguration  
@ComponentScan(basePackages = "com.wise.tiger.web")  
@EnableWebMvc  
public class WebConfig implements WebMvcConfigurer {  
    // 视图解析器  
    @Bean  
    public InternalResourceViewResolver viewResolver() {  
        var viewResolver = new InternalResourceViewResolver();  
        viewResolver.setPrefix("/WEB-INF/pages/");  
        viewResolver.setSuffix(".jsp");  
        return viewResolver;  
    }  

    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(new RoleInterceptor()).addPathPatterns("/control/**");  
        }  
  
    @Override // 静态资源不被前端过滤器  
    public void addResourceHandlers(ResourceHandlerRegistry registry) {  
        registry.addResourceHandler("/static/**")// 添加静态资源的url-pattern  
                .addResourceLocations("/static/");  
    }  
}  

xml配置:在spring-mvc.xml上配置java

<mvc:interceptors>  
        <mvc:interceptor>  
            <mvc:mapping path="/controll/**"/>  
            <bean class="com.wise.bbs.interceptor.RoleInterceptor"/>  
        </mvc:interceptor>  
 </mvc:interceptors>  

在xml配置中,用元素mvc:interceptors配置拦截器,在里面能够配置多个拦截器,path属性告诉拦截器拦截什么请求,它使用一个正则式的匹配。下面介绍下用java代码 配置:mysql

1.3 多个拦截器执行顺序git

多个拦截器执行顺序犹如嵌套的if语句。首先讨论preHandler方法返回为true的状况,先建三个角色拦截器,对其进行测试,能够看到结果是这样的 (责任链模式)web

preHandle1
       preHandle2
                preHandle3
                         postHadle3
                         postHadle2
                         postHadle1
                 afterCompletion3
       afterCompletion2
afterCompletion1正则表达式

有些时候前置方法可能返回false,咱们将RoleInterceptor2中的前置方法给为false进行测试后获得的结果:spring

preHandle1
          preHandle2
afterCompletion1sql

注意:当其中一个preHandle方法返回false后,按配置顺序,后面的preHandle方法都不会执行了,而控制器和后面的postHandle也不会再运行。
 

2 springMVC组件开发之表单校验器

        Spring  提供了对Bean的功能校验,经过注解@Valid标明哪一个Bean须要启用注解式的验证。在javax.validation.constrains.*中定义了一系列的JSR 303规范给出的注解:

限制

说明

@Null

限制只能为null

@NotNull

限制必须不为null

@AssertFalse

限制必须为false

@AssertTrue

限制必须为true

@DecimalMax(value)

限制必须为一个不大于指定值的数字

@DecimalMin(value)

限制必须为一个不小于指定值的数字

@Digits(integer,fraction)

限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction

@Future

限制必须是一个未来的日期

@Max(value)

限制必须为一个不大于指定值的数字

@Min(value)

限制必须为一个不小于指定值的数字

@Past

限制必须是一个过去的日期

@Pattern(value)

限制必须符合指定的正则表达式

@Size(max,min)

限制字符长度必须在min到max之间

@Email

邮箱类型

@NotEmpty

集合,不为空

@NotBlank

不为空字符串

@Positive

数字,正数

@PositiveOrZero

数字,正数或0

@NegativeOrZero

数字,负数

@NegativeOrZero

数字,负数或0

@PastOrPresent

过去或者如今日期

@FutrueOrPresent

未来或者如今日期

 
为了使用这些注解,假设要完成一个保存雇员表单:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
<!DOCTYPE html>  
<html>  
<head>  
    <title>添加员工</title>  
</head>  
<body>  
    <form action="${pageContext.request.contextPath }/control/emp/save" method="post" >  
            用户名:<input type="text" name="username" placeholder="请输入用户名,最小4位,最大12位" required/>  
            <span></span>  
            <br/>  
            密码:<input type="password" name="password" placeholder="请输入登陆密码"/><br/>  
            电子邮箱:<input type="email" name="email" placeholder="请输入电子邮箱"/><br/>  
            员工生日:<input type="date" name="bornDate" /><br/>  
            薪水:<input type="number" name="salary" placeholder="请输入员工薪水" /><br/>  
            联系电话:<input type="text" name="phone" placeholder="请输入员工联系电话"  pattern="^1[358]\d{9}$"/><br/>  
            员工简介:  
        <textarea placeholder="请输入员工简介" name="intro"></textarea>   
        <br/>  
        <input type="submit" value="提交" /> <input type="reset">  
    </form:form>  
</body>  
</html>  
校验规则:
  • 户名,密码,电子邮箱,入职日期不能为空
  • 入职日期格式为yyyy-MM-dd,且只能大于今日
  • 生日日期格式为yyyy-MM-dd,且只能是一个过去日期
  • 邮箱须要符合格式:
  •  简介内容不得多于256个字符
  • 薪水最小值为2000,最大值为5万
  • 联系方式需为正确的手机号码(使用正则表达式匹配)

创建pojo,肯定校验规则:

public class Employee {  
    private Integer id;  
    /** 
     * 用户名,不容许为空,最小4位,最大12位 
     */  
    @NotBlank(message = "用户名不能为空")  
    @Size(min = 4,max = 12,message = "{employee.username.valid.size.message}")  
    private String username;  
    /** 
     * 密码:不能为空,最小6位 
     */  
    @NotBlank(message = "密码不能为空")  
    @Size(min = 6,max = 12,message = "密码最低6位")  
    private String password;  
      
    /** 
     * 电子邮箱,不能为空,要知足邮箱基本格式 
     */  
    @NotNull(message = "邮箱不能为空")  
    @Email(message="邮箱必须知足基本格式")  
    private String email;  
    /** 
     * 员工生日,格式为:yyyy-MM-dd 
     * 必须是一个过去的日期 
     */  
    @DateTimeFormat(iso = ISO.DATE)  
    @Past(message = "你不能录用还未出生的员工")  
    private LocalDate bornDate;  
      
    /** 
     * 入职日期,格式为:yyyy-MM-dd 
     * 必须是一个如今或者未来的日期 
     */  
    @DateTimeFormat(iso = ISO.DATE)  
    @FutureOrPresent  
    private LocalDate entryDate = LocalDate.now();  
    /** 
     * 员工联系(手机)电话 
     * 必须符合手机号码格式(正则表达式) 
     */  
    @Pattern(regexp = "^1[358]\\d{9}$",message = "请输入正确的手机号码")  
    private String phone;  
    /** 
     * 员工薪水,最低2000,最高5万 
     */  
    @Min(value = 2000,message="工资不能低于2000,不然麻烦了")  
    @Max(value=50000,message="工资不能大于50000,不然交奢侈税")  
    private float salary;  
      
    /** 
     * 员工简介,最大不超过200 
     */  
    @Size(min = 6,max = 12,message = "员工简介不能超过200字")  
    private String intro;  
  
       //***************setter and getter*************/  
}  

       这样就定义了一个pojo,用于接收表单的信息。字段上面的注解反映了对每个字段的验证要求,这样就能够加入对应校验,若是没有指定message属性,会生成默认的错误信息。message配置项用来定义当校验失败后的错误信息,这样就能启动Spring的检验规则来校验表单了。

 在post请求接收数据时,用控制器验证表单

@Controller  
@RequestMapping("/control/emp")  
public class EmployeeController {  
    @RequestMapping("/save")  
    public String save(@Valid Employee emp,BindingResult errors) {  
        if(errors.hasErrors()) {  
            return "emp/emp_save";  
        }  
        return "redirect:./list";  
    }  
}  

    使用了注解@Valid标明这个Bean将会被校验,另一个类型为BindingResult的参数(或者为Errors)是用于保存校验是否存在错误信息的,也就是当采用JSR 303规范进行校验后,它会将这个错误信息保存到这个参数中,在方法中判断是否有错误信息,若是有错误信息,跳转到填写表单页面告诉用户错误信息。

如何在jsp页面中显示错误信息呢?这时可使用jsp提供给咱们的标签库

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
<span style="background-color: #fafafa; font-family: monospace;"><%@ taglib prefix="form"  uri="http://www.springframework.org/tags/form"%></span>  
<!DOCTYPE html>  
<html>  
<head>  
    <title>添加员工</title>  
</head>  
<body>  
    <form:form action="${pageContext.request.contextPath }/control/emp/save" method="post"  modelAttribute="employee">  
            用户名:<input type="text" name="username" placeholder="请输入用户名,最小4位,最大12位" required/>  
            <span><span style="background-color: #fafafa; font-family: monospace;"><form:errors path="username" cssStyle="color: red"/></span></span>  
            <br/>  
            密码:<input type="password" name="password" placeholder="请输入登陆密码"/><br/>  
            电子邮箱:<input type="email" name="email" placeholder="请输入电子邮箱"/><br/>  
            员工生日:<input type="date" name="bornDate" /><br/>  
            薪水:<input type="number" name="salary" placeholder="请输入员工薪水" /><br/>  
            联系电话:<input type="text" name="phone" placeholder="请输入员工联系电话"  pattern="^1[358]\d{9}$"/><br/>  
            员工简介:  
        <textarea placeholder="请输入员工简介" name="intro"></textarea>   
        <br/>  
        <input type="submit" value="提交" /> <input type="reset">  
    </form:form>  
</body>  
</html>  

 看起来彷佛很是不错,可是咱们的message错误提示信息是硬编码在pojo身上,为了不其硬编码而实现可配置,咱们在src/main/resource下新建messageSource.properties文件:

employee.username.valid.notnull.message=用户名不能为空  
employee.username.valid.size.message=用户名不能少于4位且不能超过12位  

上面简单罗列了两个错误信息配置,其它道理同样,哪怎么应用(读取)指定的错误信息呢?在pojo验证规则之上:

/** 
* 用户名,不容许为空,最小4位,最大12位 
*/  
@NotBlank(message = "{employee.username.valid.notnull.message}")  
@Size(min = 4,max = 12,message = "{employee.username.valid.size.message}")  
private String username;  

 能够采用{}表达式对配置文件的key加以读取其对应的value。其它字段同理,到这里还不能读取指定配置文件,须要告诉spring的检验器加载什么样的配置文件,在springMVCConfig配置类配置:

@Configuration  
@ComponentScan(basePackages = "com.wise.tiger.web")  
@EnableWebMvc  
public class WebConfig implements WebMvcConfigurer{  
    @Bean  
    public InternalResourceViewResolver viewResolver() {  
        var viewResolver = new InternalResourceViewResolver();  
        viewResolver.setPrefix("/WEB-INF/pages/");  
        viewResolver.setSuffix(".jsp");  
        return viewResolver;  
    }  
    @Override //静态资源不被前端过滤器过滤  
    public void addResourceHandlers(ResourceHandlerRegistry registry) {  
        registry.addResourceHandler("/static/**")//添加静态资源的url-pattern  
            .addResourceLocations("/static/");  
    }  
    @Bean  
    public ReloadableResourceBundleMessageSource messageResource() {  
        var messageResource = new ReloadableResourceBundleMessageSource();  
        //若是不指定,默认会去classpath下的messages.properties配置文件  
        messageResource.setBasename("classpath:resourceMessage");  
        return messageResource;  
    }  
    /**  
     * 添加表单校验器  
     */  
    @Override  
    public Validator getValidator() {  
        var validator = new LocalValidatorFactoryBean();  
        validator.setProviderClass(HibernateValidator.class);  
        validator.setValidationMessageSource(messageResource());  
        return validator;  
    }  
}  
 分组校验

1.定义分组:能够采用标识接口来进行分组区分

//分组校验1  
public interface ValidationGroup1 {  
}  
  
//分组校验2  
public interface ValidationGroup2 {  
2.在检验规则上添加分组
/** 
* 用户名,不容许为空,最小4位,最大12位 
*/  
@NotBlank(message = "{employee.username.valid.notnull.message}",groups = ValidationGroup1.class)  
@Size(min = 4,max = 12,message = "{employee.username.valid.size.message}",groups = ValidationGroup2.class)  
private String username;
3.在conroller中指定使用的分组校验
@Controller  
@RequestMapping("/control/emp")  
public class EmployeeController {  
    @RequestMapping("/save")  
    public String save( @Validated(ValidationGroup2.class) @ModelAttribute Employee emp,BindingResult errors,Model model) {  
        if(errors.hasErrors()) {  
            for (var i = 0; i < errors.getErrorCount();i++) {  
                System.out.println(errors.getObjectName() + ":" + errors.getFieldError().getDefaultMessage());  
            }  
            return "emp/emp_save";  
        }  
        return "redirect:./list";  
    }  
}  
有时候除了简单的输入格式、非空性等校验,也须要必定的业务校验,Spring提供了Validator接口来实现校验,它将在进入控制器逻辑以前对参数的合法性进行校验。核心代码以下:
public interface Validator {  
    /** 
     * 判断当前校验器是否用于检验clazz类型的pojo 
     * @param clazz -- pojo类型 
     * @return true 启动校验,false 不校验 
     */  
    @Override  
    public boolean supports(Class<?> clazz) {  
        return false;  
    }  
    /** 
     * 检验pojo的合法性 
     * @param target 请求对象 
     * @param errors 错误信息 
     */  
    @Override  
    public void validate(Object target, Errors errors) {       
    }  
}  

、Validator接口是SpringMvc校验表单的核心接口,它只是一个验证器,在Spring中最终被注册到验证器的列表中,这样就能够提供给各个控制器去定义,而后经过supports方法断定是否会启用验证器去验证数据。对于校验的过程,则是经过validate方法去实现的。

package com.wise.tiger.pojo;  
  
import org.springframework.validation.Errors;  
import org.springframework.validation.Validator;  
  
/** 
 * @Description: 员工校验器 
 * @author: <a href="mailto:1020zhaodan@163.com">Adan</a>  
 * @date: 2019年5月28日  下午7:23:40 
 * @version:1.0-snapshot 
 */  
public class EmployeeValidator implements Validator {  
    /** 
     * 判断当前校验器是否用于检验clazz类型的pojo 
     * @param clazz -- pojo类型 
     * @return true 启动校验,false 不校验 
     */  
    @Override  
    public boolean supports(Class<?> clazz) {  
        //判断验证是否为Employee,若是是则进行校验  
        return Employee.class.equals(clazz);  
    }  
  
    /** 
     * 检验pojo的合法性 
     * @param target 请求对象 
     * @param errors 错误信息 
     */  
    @Override  
    public void validate(Object target, Errors errors) {  
        var employee = (Employee)target;  
        //对employee pojo类中年薪计算规则为薪水 * 16  
        var yearlySalary = employee.getSalary() * 16;  
        //若是年薪小于1,则认为业务错误  
        if(yearlySalary < 1)  
            //加入错误信息  
            errors.rejectValue("yearlySalary", null, "年薪和月薪不匹配,月薪输入错误");  
    }  
}  

 须要将该验证器捆绑到对应的控制器中,Spring MVC提供了注解@InitBinder,经过该注解就能够将验证器和控制器捆绑在一块儿,这样就能对请求表单进行验证了。

@Controller  
@RequestMapping("/control/emp")  
public class EmployeeController {  
    @InitBinder  
    public void initBinder(DataBinder binder){  
        //数据绑定器加入验证器  
        binder.setValidator(new EmployeeValidator());  
    }  
    @RequestMapping("/save")  
    public String save( @Validated(ValidationGroup2.class) @ModelAttribute Employee emp,BindingResult errors,Model model) {  
        if(errors.hasErrors()) {  
            for (var i = 0; i < errors.getErrorCount();i++) {  
                System.out.println(errors.getObjectName() + ":" + errors.getFieldError().getDefaultMessage());  
            }  
            return "emp/emp_save";  
        }  
        return "redirect:./list";  
    }  
}  

这样就能对比较复杂的逻辑关系进行校验了。

3 springMVC组件开发之异常处理器

    springMVC自带HandlerExceptionResolver异常处理器,定义一个异常处理器,实现HandlerExceptionResolver接口,重写方法中的handler是指放生错误时的处理对象,ex指发生的错误,加上@Component并打开注解开关后会自动生效。

@Component
public class ExceptionResolver implements HandlerExceptionResolver{
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        var mv = new ModelAndView();
        mv.addObject("message","哎呀,一不当心出错了呀!");
        mv.setViewName("message");
        return mv;
    }
}

3 springMVC组件开发之类型转换器

springMVC自带Converter转换类,定义一个转换器实现Converter类,并在Converter后面写上源类型和目标类型,重写convert方法加上@Component并打开注解开关后会自动生效。

@Component
public class PrivilegeConverter implements Converter<String[], Set<Privilege>> {
    @Override
    public Set<Privilege> convert(String[] source) {
        if(source == null) return null;
        var ret = new HashSet<Privilege>();
        for(String dest : source)
            ret.add(new Privilege(dest.split("\\_")[0],dest.split("\\_")[1]));
        return ret;
    }
}

 

附依赖:

dependencies {  
    compile group: 'org.springframework', name: 'spring-context', version: '5.1.7.RELEASE'  
    compile group: 'org.springframework', name: 'spring-webmvc', version: '5.1.7.RELEASE'  
    compile group: 'org.springframework', name: 'spring-test', version: '5.1.7.RELEASE'  
    providedCompile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.3'  
    providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'  
    compile group: 'taglibs', name: 'standard', version: '1.1.2'  
    compile group: 'javax.servlet', name: 'jstl', version: '1.2'  
    compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'  
    compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'  
    compile group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.1'  
    compile group: 'org.mybatis', name: 'mybatis', version: '3.5.1'  
    compile group: 'com.alibaba', name: 'druid', version: '1.1.16'  
    compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.16'  
    compile group: 'org.springframework', name: 'spring-jdbc', version: '5.1.7.RELEASE'  
    compile group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'  
    compile group: 'org.hibernate', name: 'hibernate-validator', version: '6.0.16.Final'  
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9'  
}  
相关文章
相关标签/搜索