1. 程式人生 > >SpringBoot8-Spring MVC-Spring MVC基本配置

SpringBoot8-Spring MVC-Spring MVC基本配置

        Spring MVC的定製配置需要我們的配置類繼承一個WebMvcConfigurerAdapter類,並在此類使用@EnableWebMvc註解,並開啟對Spring MVC的配置支援,這樣我們就可以重寫這個類的方法,完成我們的常用配置。

        我們將前面的MyMvcConfig配置類繼承WebMvcConfigurerAdapter。下面沒有特別說明,關於配置的相關內容都在MyMvcConfig裡編寫。

一:靜態資源對映

    程式的靜態檔案(js,css,圖片)等需要直接訪問,這時我們可以在配置裡重寫addResourceHandlers方法來實現。

     示例:

     1,新增靜態資源

      我們在src/main/resource建立assets/js目錄,並複製一個jquery的js檔案放置在此目錄,如下:


   配置程式碼如下:

package jack.springmvc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * Created by jack on 2017/7/16.
 */
@Configuration
@EnableWebMvc   //開啟Spring MVC支援,若無此句,重寫WebMvcConfigurerAdapter方法無效
@ComponentScan("jack.springmvc")
//繼承WebMvcConfigurerAdapter類,重寫其方法可對Spring MVC進行配置
public class MyMvcConfig extends WebMvcConfigurerAdapter{
    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        //viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return  viewResolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //super.addResourceHandlers(registry);
        //addResourceLocations指的是檔案放置的目錄,addResourceHandler指的是對外暴露的訪問路徑
        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
    }
}


二:攔截器配置

      攔截器(Interceptor)實現對每一個請求前後進行相關的業務處理,類似於Servlet的Filter。

      可讓普通的Bean實現HanlderInterceptor介面或者繼承HandlerInterceptorAdapter類來實現自定義攔截器。

      通過重寫WebMvcConfigurerAdapter的addInterceptors方法來註冊自定義攔截器,下面演示一個簡單攔截器的開發和配置,業務含義為計算每一次的處理時間。

      示例:

編寫攔截器程式碼:

package jack.springmvc.interceptor;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by jack on 2017/7/24.
 */
//繼承HandlerInterceptorAdapter類來實現自定義攔截器
public class DemoInterceptor extends HandlerInterceptorAdapter{
    /**
     * 重寫preHandle方法,在請求發生之前執行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //return super.preHandle(request, response, handler);
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime",startTime);
        return true;
    }

    /**
     * 重寫postHandle方法,在請求完成之後執行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //super.postHandle(request, response, handler, modelAndView);
        long startTime = (long) request.getAttribute("startTime");
        request.removeAttribute("startTime");
        long endTime = System.currentTimeMillis();
        System.out.println("本次請求處理時間為:"+new Long(endTime-startTime)+"ms");
        request.setAttribute("handlingTime",endTime-startTime);
    }
}


配置攔截器,配置攔截器的Bean,重寫addInterceptors,註冊攔截器,程式碼如下:

package jack.springmvc.config;

import jack.springmvc.interceptor.DemoInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * Created by jack on 2017/7/16.
 */
@Configuration
@EnableWebMvc   //開啟Spring MVC支援,若無此句,重寫WebMvcConfigurerAdapter方法無效
@ComponentScan("jack.springmvc")
//繼承WebMvcConfigurerAdapter類,重寫其方法可對Spring MVC進行配置
public class MyMvcConfig extends WebMvcConfigurerAdapter{

    /**
     * 配置攔截器的Bean
     * @return
     */
    @Bean
    public DemoInterceptor demoInterceptor(){
        return new DemoInterceptor();
    }

    /**
     * c重寫addInterceptors方法,註冊攔截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //super.addInterceptors(registry);
        registry.addInterceptor(demoInterceptor());
    }

    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        //viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return  viewResolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //super.addResourceHandlers(registry);
        //addResourceLocations指的是檔案放置的目錄,addResourceHandler指的是對外暴露的訪問路徑
        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
    }
}

在瀏覽器輸入:http://localhost:8080/index

輸出如下:


三:@ControllerAdvice

      通過@ControllerAdvice,我們可以將對於控制器的全域性配置放置在同一個位置,註解了@ControllerAdvice的類的方法可使用@ExceptionHandler,@InitBinder,@ModelAttribute註解到方法上,這對所有註解了@RequestMapping的控制器內的方法有效。

     @ExceptionHandler:用於全域性處理控制器的異常

     @InitBinder:用來設定WebDataBinder,WebDataBinder用來自動繫結前臺請求引數到Model中。

     @ModelAttribute:@ModelAttribute本來的作用是繫結鍵值對到Model裡,此處是讓全域性的@RequestMapping都能獲得此處設定的鍵值對。

     下面演示使用@ExceptionHandler處理全域性異常,更人性化的將異常輸出給使用者。

    示例:

1)定製ControllerAdvice

package jack.springmvc.advice;

import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;


/**
 * Created by jack on 2017/7/24.
 */
//@ControllerAdvice宣告一個控制器建言,@ControllerAdvice組合了@Component註解,所以自動註冊為Spring的bean
@ControllerAdvice
public class ExceptionHandlerAdvice {
    /**
     * @ExceptionHandler在此處定義全域性處理,通過@ExceptionHandler的value屬性可過濾攔截的條件,
     * 再此我們可以看出我們攔截所有的Exception
     * @param exception
     * @param request
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public ModelAndView exception(Exception exception, WebRequest request){
        ModelAndView modelAndView = new ModelAndView("error");//error頁面
        modelAndView.addObject("errorMessage",exception.getMessage());
        return modelAndView;
    }


    /**
     * 此處使用@ModelAttribute註解將鍵值對新增到全域性,所有註解了@RequestMapping的方法可獲得此鍵值對
     * @param model
     */
    @ModelAttribute
    public void addAttributes(Model model){
        model.addAttribute("msg", "額外資訊");
    }

    /**
     * 通過@InitBinder註解定製WebDataBinder
     * @param webDataBinder
     */
    @InitBinder
    public void initBinder(WebDataBinder webDataBinder){
        //此處演示忽略request引數的id
        webDataBinder.setDisallowedFields("id");
    }

}

2)演示控制器
package jack.springmvc.controller;

import jack.springmvc.domain.DemoObj;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Created by jack on 2017/7/24.
 */
@Controller
public class AdviceController {
    @RequestMapping("/advice")
    public String getSomething(@ModelAttribute("msg") String msg, DemoObj obj){
        throw new IllegalArgumentException("非常抱歉,引數有誤/" + "來自@ModelAttribute:" + msg);
    }
}

3)異常展示頁面

    在src/main/resources/views下,新建error.jsp,內容如下:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head>
    <meta http-equiv="CONTENT-TYPE" content="text/html; charset=UTF-8" >
    <title>@ControllerAdvice Demo</title>
</head>
<body>
<pre>
    ${errorMessage}
</pre>
</body>
</html>

4)執行

 在瀏覽器輸入:http://localhost:8080/advice?id=1&name=xx,頁面顯示如下所示:


   其中DemoObj的id被過濾掉了。且獲得了@ModelAttribute的msg資訊

四:其他配置

1,快捷的ViewController

   在前面我們配置頁面轉向的時候使用了下面的程式碼:

   @RequestMapping(value = "/index")
    public String hello(){
        //通過MyMvcConfig裡面的配置,通過ViewResolver的Bean配置,返回值為index,
        // 說明我們的頁面存放的路徑為/classes/views/index.jsp
        return "index";
    }

  此處無任何業務處理,只是簡單的頁面轉向,寫了至少三行程式碼,在實際開發中會涉及大量的這樣頁面轉向,若都這樣寫會很麻煩,我們可以通過在配置中重寫addViewControllers來簡化配置:
package jack.springmvc.config;

import jack.springmvc.interceptor.DemoInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * Created by jack on 2017/7/16.
 */
@Configuration
@EnableWebMvc   //開啟Spring MVC支援,若無此句,重寫WebMvcConfigurerAdapter方法無效
@ComponentScan("jack.springmvc")
//繼承WebMvcConfigurerAdapter類,重寫其方法可對Spring MVC進行配置
public class MyMvcConfig extends WebMvcConfigurerAdapter{

    /**
     * 配置攔截器的Bean
     * @return
     */
    @Bean
    public DemoInterceptor demoInterceptor(){
        return new DemoInterceptor();
    }

    /**
     * c重寫addInterceptors方法,註冊攔截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //super.addInterceptors(registry);
        registry.addInterceptor(demoInterceptor());
    }

    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        //viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return  viewResolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //super.addResourceHandlers(registry);
        //addResourceLocations指的是檔案放置的目錄,addResourceHandler指的是對外暴露的訪問路徑
        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
    }

    /**
     * 統一處理沒啥業務邏輯處理的controller請求,實現程式碼的簡潔
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //super.addViewControllers(registry);
        registry.addViewController("/index").setViewName("/index");
    }
}


註釋掉之前處理/index的controller,重啟伺服器,輸入http://localhost:8080/index進行訪問,發現和之前是一樣的結果。把沒有業務的controller統一進行處理,程式碼根據簡潔,管理更加集中了。

2,路徑匹配引數配置

       在spring mvc中,路徑引數如果帶“.”的話,“.”後面的值將被忽略,例如訪問:http://localhost:8080/anno/pathvar/xx.yy,此時“.”後面的yy被忽略了,如下所示:


通過重寫configurePathMatch方法可忽略"."後面的引數,程式碼如下:

@Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        //super.configurePathMatch(configurer);
        configurer.setUseSuffixPatternMatch(false);
    }


目前spring mvc配置的完整程式碼如下:

package jack.springmvc.config;

import jack.springmvc.interceptor.DemoInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * Created by jack on 2017/7/16.
 */
@Configuration
@EnableWebMvc   //開啟Spring MVC支援,若無此句,重寫WebMvcConfigurerAdapter方法無效
@ComponentScan("jack.springmvc")
//繼承WebMvcConfigurerAdapter類,重寫其方法可對Spring MVC進行配置
public class MyMvcConfig extends WebMvcConfigurerAdapter{

    /**
     * 配置攔截器的Bean
     * @return
     */
    @Bean
    public DemoInterceptor demoInterceptor(){
        return new DemoInterceptor();
    }

    /**
     * c重寫addInterceptors方法,註冊攔截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //super.addInterceptors(registry);
        registry.addInterceptor(demoInterceptor());
    }

    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        //viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setPrefix("/WEB-INF/classes/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return  viewResolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //super.addResourceHandlers(registry);
        //addResourceLocations指的是檔案放置的目錄,addResourceHandler指的是對外暴露的訪問路徑
        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
    }

    /**
     * 統一處理沒啥業務邏輯處理的controller請求,實現程式碼的簡潔
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //super.addViewControllers(registry);
        registry.addViewController("/index").setViewName("/index");
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        //super.configurePathMatch(configurer);
        configurer.setUseSuffixPatternMatch(false);
    }
}


重啟tomcat,在訪問http://localhost:8080/anno/pathvar/xx.yy,就可以接受"."後面的yy了,如下:


3,更多配置

    更多配置可以檢視WebMvcConfigurerAdapter類的api。因其是WebMvcConfigurer介面的實現,所以WebMvcConfigurer的api內的也可以用來MVC。下面看看WebMvcConfigurerAdapterWebMvcConfigurer的原始碼。

WebMvcConfigurerAdapter

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet.config.annotation;

import java.util.List;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
    public WebMvcConfigurerAdapter() {
    }

    public void configurePathMatch(PathMatchConfigurer configurer) {
    }

    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    }

    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    }

    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    }

    public void addFormatters(FormatterRegistry registry) {
    }

    public void addInterceptors(InterceptorRegistry registry) {
    }

    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    }

    public void addCorsMappings(CorsRegistry registry) {
    }

    public void addViewControllers(ViewControllerRegistry registry) {
    }

    public void configureViewResolvers(ViewResolverRegistry registry) {
    }

    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    }

    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
    }

    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    }

    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    }

    public Validator getValidator() {
        return null;
    }

    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}


WebMvcConfigurer

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet.config.annotation;

import java.util.List;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;

public interface WebMvcConfigurer {
    void configurePathMatch(PathMatchConfigurer var1);

    void configureContentNegotiation(ContentNegotiationConfigurer var1);

    void configureAsyncSupport(AsyncSupportConfigurer var1);

    void configureDefaultServletHandling(DefaultServletHandlerConfigurer var1);

    void addFormatters(FormatterRegistry var1);

    void addInterceptors(InterceptorRegistry var1);

    void addResourceHandlers(ResourceHandlerRegistry var1);

    void addCorsMappings(CorsRegistry var1);

    void addViewControllers(ViewControllerRegistry var1);

    void configureViewResolvers(ViewResolverRegistry var1);

    void addArgumentResolvers(List<HandlerMethodArgumentResolver> var1);

    void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> var1);

    void configureMessageConverters(List<HttpMessageConverter<?>> var1);

    void extendMessageConverters(List<HttpMessageConverter<?>> var1);

    void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);

    void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);

    Validator getValidator();

    MessageCodesResolver getMessageCodesResolver();
}