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'  
}  
相關文章
相關標籤/搜索