1. 程式人生 > >SpringMvc4.x-3(基本配置)

SpringMvc4.x-3(基本配置)

靜態資源配置:

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

我們將前面文章中的MyMvcConfig配置類繼承WebMvcConfigurerAdapter,以後如果不做特別說明,則關於配置的相關內容都在MyMvcConfig裡面編寫。

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

新增靜態資源

src/main/resources

下建立assets/js目錄,並複製一個jquery.js放置在該目錄下

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
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;

@Configuration
@EnableWebMvc// ①
@ComponentScan("org.light4j.springMvc4")
public class MyMvcConfig extends WebMvcConfigurerAdapter {// ②

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

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");// ③
    }
}

① @EnableWebMvc開啟SpringMvc支援,若無此句,重寫WebMvcConfigurerAdapter的方法無效。
② 繼承WebMvcConfigurerAdapter類,重寫其方法可對Spring MVC進行配置。
③ addResourceLocations指的是檔案放置的目錄,addResourceHandler指的是對外暴露的訪問路徑。

攔截器配置

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

可讓普通的Bean實現HandlerInterceptor介面或者繼承HandlerInterceptorAdapter

類來實現自定義攔截器。

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

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

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

/**
 * ① 繼承HandlerInterceptorAdapter類來實現自定義攔截器。
 * ② 重寫preHandle方法,在請求發生前執行。
 * ③ 重寫postHandle方法,在請求完成後執行。
 *
 */
public class DemoInterceptor extends HandlerInterceptorAdapter {//①

    @Override
    public boolean preHandle(HttpServletRequest request, //②
            HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, //③
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        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方法,註冊攔截器。
	 * @return
	 */
	@Bean // ①
	public DemoInterceptor demoInterceptor() {
		return new DemoInterceptor();
	}

	@Override
	public void addInterceptors(InterceptorRegistry registry) {// ②
		registry.addInterceptor(demoInterceptor());
	}

@ControllerAdvice註解

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

  1. @ExceptionHandler:用於全域性處理控制器裡面的異常。
  2. @InitBinder:用來設定WebDataBinderWebDataBinder用來自動繫結前臺請求引數到Model中。
  3. @ModelAttribute@ModelAttribute本來的作用是繫結鍵值對到Model裡,此處是讓全域性的@RequestMapping都能獲得在此處設定的鍵值對。

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

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;

@ControllerAdvice //①
public class ExceptionHandlerAdvice { 

    @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 //③
    public void addAttributes(Model model) {
        model.addAttribute("msg", "額外資訊"); //④
    }

    @InitBinder //④
    public void initBinder(WebDataBinder webDataBinder) {
        webDataBinder.setDisallowedFields("id"); //⑤
    }
}

① @ControllerAdvice宣告一個控制器建言,@ControllerAdvice組合了@Component註解,所以自動註冊為Spring的Bean
② @ExceptionHandler在此處定義全域性處理,通過@ExceptionHandlervalue屬性可過濾攔截的條件,在此處可以看出攔截的是所有的Exception
③ 此處使用@ModelAttribute註解將鍵值對新增到全域性,所有註解了@RequestMapping的方法可獲得此鍵值對。
④ 通過@InitBinder註解定製WebDataBinder

import org.light4j.springMvc4.domain.DemoObj;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class AdviceController {
    @RequestMapping("/advice")
    public String getSomething(@ModelAttribute("msg") String msg,DemoObj obj){//①

        throw new IllegalArgumentException("非常抱歉,引數有誤/"+"來自@ModelAttribute:"+ msg);
    }
}

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

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page isELIgnored="false" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>@ControllerAdvice Demo</title>
</head>
<body>
    ${errorMessage}
</body>
</html>

此處無任何的業務處理,只是簡單的頁面跳轉,寫了至少三行有效的程式碼,在實際的開發中會涉及大量這樣的頁面轉向,若都這樣寫會特別的麻煩,我們通過在配置類MyMvcConfig裡通過重寫addViewControllers來簡化配置:

@RequestMapping("/index")//②
    public  String hello(){

        return "index";
    }
@Override
public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/index").setViewName("/index");
}

Spring MVC中,路徑引數如果帶”.”的話,”.”後面的值將被忽略,例如,訪問http://localhost/springMvc4.x/anno/pathvar/xxx.yy,此時,”.”後面的yy被忽略;

通過重寫configurePathMatch方法可實現不忽略”.”後面的引數,在配置類MyMvcConfig中增加程式碼如下:

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

更多配置請檢視WebMvcConfigurerAdater類的API。因其是WebMvcConfigurerAdater介面的實現,所以WebMvcConfigurerAdaterAPI內的方法也可以用來配置MVC。下面列出WebMvcConfigurerAdaterWebMvcConfigurer的原始碼供參考。

1. WebMvcConfigurerAdater

public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation returns {@code null}
     */
    @Override
    public Validator getValidator() {
        return null;
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    }

}

 WebMvcConfigurer

public interface WebMvcConfigurer {

    /**
     * Add {@link Converter}s and {@link Formatter}s in addition to the ones
     * registered by default.
     */
    void addFormatters(FormatterRegistry registry);

    /**
     * Configure the {@link HttpMessageConverter}s to use for reading or writing
     * to the body of the request or response. If no converters are added, a
     * default list of converters is registered.
     * <p><strong>Note</strong> that adding converters to the list, turns off
     * default converter registration. To simply add a converter without impacting
     * default registration, consider using the method
     * {@link #extendMessageConverters(java.util.List)} instead.
     * @param converters initially an empty list of converters
     */
    void configureMessageConverters(List<HttpMessageConverter<?>> converters);

    /**
     * A hook for extending or modifying the list of converters after it has been
     * configured. This may be useful for example to allow default converters to
     * be registered and then insert a custom converter through this method.
     * @param converters the list of configured converters to extend.
     * @since 4.1.3
     */
    void extendMessageConverters(List<HttpMessageConverter<?>> converters);

    /**
     * Provide a custom {@link Validator} instead of the one created by default.
     * The default implementation, assuming JSR-303 is on the classpath, is:
     * {@link org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean}.
     * Leave the return value as {@code null} to keep the default.
     */
    Validator getValidator();

    /**
     * Configure content negotiation options.
     */
    void configureContentNegotiation(ContentNegotiationConfigurer configurer);

    /**
     * Configure asynchronous request handling options.
     */
    void configureAsyncSupport(AsyncSupportConfigurer configurer);

    /**
     * Helps with configuring HandlerMappings path matching options such as trailing slash match,
     * suffix registration, path matcher and path helper.
     * Configured path matcher and path helper instances are shared for:
     * <ul>
     * <li>RequestMappings</li>
     * <li>ViewControllerMappings</li>
     * <li>ResourcesMappings</li>
     * </ul>
     * @since 4.0.3
     */
    void configurePathMatch(PathMatchConfigurer configurer);

    /**
     * Add resolvers to support custom controller method argument types.
     * <p>This does not override the built-in support for resolving handler
     * method arguments. To customize the built-in support for argument
     * resolution, configure {@link RequestMappingHandlerAdapter} directly.
     * @param argumentResolvers initially an empty list
     */
    void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers);

    /**
     * Add handlers to support custom controller method return value types.
     * <p>Using this option does not override the built-in support for handling
     * return values. To customize the built-in support for handling return
     * values, configure RequestMappingHandlerAdapter directly.
     * @param returnValueHandlers initially an empty list
     */
    void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers);

    /**
     * Configure the {@link HandlerExceptionResolver}s to handle unresolved
     * controller exceptions. If no resolvers are added to the list, default
     * exception resolvers are added instead.
     * @param exceptionResolvers initially an empty list
     */
    void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers);

    /**
     * Add Spring MVC lifecycle interceptors for pre- and post-processing of
     * controller method invocations. Interceptors can be registered to apply
     * to all requests or be limited to a subset of URL patterns.
     * <p><strong>Note</strong> that interceptors registered here only apply to
     * controllers and not to resource handler requests. To intercept requests for
     * static resources either declare a
     * {@link org.springframework.web.servlet.handler.MappedInterceptor MappedInterceptor}
     * bean or switch to advanced configuration mode by extending
     * {@link org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
     * WebMvcConfigurationSupport} and then override {@code resourceHandlerMapping}.
     */
    void addInterceptors(InterceptorRegistry registry);

    /**
     * Provide a custom {@link MessageCodesResolver} for building message codes
     * from data binding and validation error codes. Leave the return value as
     * {@code null} to keep the default.
     */
    MessageCodesResolver getMessageCodesResolver();

    /**
     * Configure simple automated controllers pre-configured with the response
     * status code and/or a view to render the response body. This is useful in
     * cases where there is no need for custom controller logic -- e.g. render a
     * home page, perform simple site URL redirects, return a 404 status with
     * HTML content, a 204 with no content, and more.
     */
    void addViewControllers(ViewControllerRegistry registry);

    /**
     * Configure view resolvers to translate String-based view names returned from
     * controllers into concrete {@link org.springframework.web.servlet.View}
     * implementations to perform rendering with.
     */
    void configureViewResolvers(ViewResolverRegistry registry);

    /**
     * Add handlers to serve static resources such as images, js, and, css
     * files from specific locations under web application root, the classpath,
     * and others.
     */
    void addResourceHandlers(ResourceHandlerRegistry registry);

    /**
     * Configure a handler to delegate unhandled requests by forwarding to the
     * Servlet container's "default" servlet. A common use case for this is when
     * the {@link DispatcherServlet} is mapped to "/" thus overriding the
     * Servlet container's default handling of static resources.
     */
    void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);

}