SpringMVC 3.0後的實現

SpringMVC概述

什麼是MVC,我們常說的Model+View+Controller(數據模型+視圖+控制器)和三層架構有什麼關係呢?MVC只存在三層架構的表現層,M實際上是數據模型,是包含數據的對象,在SpringMVC中有一個專門的類叫Model,用來和V之間數據交互、傳值;V值得是視圖,包含JSP,freeMarker,Velocity,Thymeleaf,Tile等,C當然就是控制器,springMVC的controller。

SpringMVC項目的快速搭建

SpringMVC的核心是一個DispatcherServlet,用它來開發web應用,Servlet2.5及以下,只要在web.xml中配置<servlet>元素,這裏不做介紹,我們使用的是Servlet3.0+的無web.xml配置的方式,即通過在springMVC中實現WebApplicationInitializer接口便可實現等同於web.xml的配置

1.首先SpringMVC配置類,配置jsp的內部資源視圖解析器,@EnableWebMvc會開啓一些默認配置,如ViewResolver和MessageConverter

@Configuration
@EnableWebMvc //開啓一些默認配置
@ComponentScan("com.pingan.haofang.mvc")
public class WebMvcConfig  {
	
	
	/**
	 * 定義內部資源視圖解析器
	 * 
	 */
	@Bean
	public InternalResourceViewResolver viewResolver() {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");
		viewResolver.setViewClass(JstlView.class);
		return viewResolver;
	}
}	

2.web配置,實現WebApplicationInitializer接口,等同於一個web.xml

/**
 * WebApplicationInitializer 是spring提供用來配置Servlet3.0+配置的接口,從而實現了替代web.xml的位置
 * 實現此接口將會自動被SpringServletContainerInitalizer(用來啓動Servlet3.0+容器)獲取到。
 *
 */
public class WebMvcInitializer implements WebApplicationInitializer {

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		//新建WebApplicationContext,註冊配置類,將其和當前ServletContext關聯
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		context.register(WebMvcConfig.class);
		context.setServletContext(servletContext);//關聯
		//註冊dispatherServlet
		Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
		servlet.addMapping("/");
		servlet.setLoadOnStartup(1);
	}
}

基於以上的springMVC的配置和Web的配置,我們就可以實現簡單的springMVC的基本配置了,寫一個controller測試:

@Controller
@RequestMapping("/mvc")
public class WebController {
	
	@RequestMapping("/hello")
	public String hello() {
		return "index";
	}

配置的是解析jsp的內部資源視圖解析器,這個controller會返回跳轉到index.jsp頁面

值得注意的是@RequestMapping()註解的produces屬性,可以定製返回response的媒體類型和字符集.

如果需要返回值是json對象,則設置produces="application/json;charset=UTF-8"(不在需要@ResponseBody),返回xml,則produces="application/xml;charset=UTF-8"

SpringMVC的基本配置

SpringMVC 的定製配置需要我們的配置類繼承一個WebMvcConfigurerAdapter類,並在此類使用@EnableWebMvc註解,開啓對Spring MVC的配置支持,這樣我們可以重寫這個類的方法,完成我們的常用配置。以前面的配置爲基礎做添加修改。

我們添加如下的常用配置:

靜態資源映射配置

攔截器配置

ControllerAdvice配置

1.靜態資源文件的引入:重寫addResouceHandlers方法

@Configuration
@EnableWebMvc //開啓對spring MVC支持,沒有此註解,重寫Spring mvc配置方法無效
@ComponentScan("com.pingan.haofang.mvc")
//繼承WebMvcConfigurerAdapter,重寫方法對spring MVC進行配置
public class WebMvcConfig extends WebMvcConfigurerAdapter { 
	
	
	/**
	 * 定義內部資源視圖解析器
	 * 
	 */
	@Bean
	public InternalResourceViewResolver viewResolver() {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");
		viewResolver.setViewClass(JstlView.class);
		return viewResolver;
	}

	
	/* 
	 * 引入外部靜態資源
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
              //addResourceHandler :文件存放的位置,addReourceLocations:文件對外暴露的訪問路徑
		registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
		super.addResourceHandlers(registry);
	}

2.配置自定義攔截器(首先自定義攔截器,然後重寫 WebMvcConfigurerAdapter的addInterceptors()方法)

自定義攔截器的2種方式 :a.實現HandlerInterceptor接口,b.繼承HandlerInterceptorAdapter類

//自定義攔截器,並交給spring
public class LoginInterceptor extends HandlerInterceptorAdapter {

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

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		long startTime = (Long) request.getAttribute("start");
		long endTime = System.currentTimeMillis();
		System.out.println("cost:"+(endTime - startTime));
		request.removeAttribute("start");
		request.setAttribute("cost", endTime - startTime);
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// TODO Auto-generated method stub
		super.afterCompletion(request, response, handler, ex);
	}

	@Override
	public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// TODO Auto-generated method stub
		super.afterConcurrentHandlingStarted(request, response, handler);
	}
	
}

配置攔截器:

@Configuration
@EnableWebMvc
@ComponentScan("com.pingan.haofang.mvc")
public class WebMvcConfig extends WebMvcConfigurerAdapter {
	
	
	/**
	 * 定義內部資源視圖解析器
	 * 
	 */
	@Bean
	public InternalResourceViewResolver viewResolver() {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");
		viewResolver.setViewClass(JstlView.class);
		return viewResolver;
	}

	
	/* 
	 * 引入外部靜態資源
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
		super.addResourceHandlers(registry);
	}
	
	/*
	 * 把攔截器配置給spring
	 */
	@Bean
	public LoginInterceptor rigLoginInterceptor() {
		return new LoginInterceptor();	
	}

	/* 
	 * 註冊攔截器
	 */
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(rigLoginInterceptor());
	}

3.ControllerAdvice的使用

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

@ControllerAdvice
public class ExceptionHandlerAdvice {
    
    /** 
     * 全局異常處理,跳轉到錯誤頁面
     */
    @ExceptionHandler(value =Exception.class) //全局處理控制器裏的異常
    public ModelAndView toError(Exception exception,WebRequest request){
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("exceptionInfo", exception.getMessage());
        return mv;
    }

    @InitBinder ////用來設置WebDataBinder,WebDataBinder用來自動綁定前臺請求參數到Model
    public void initBinder(WebDataBinder dataBinder){
        dataBinder.setAllowedFields("id");
    }
    
    @ModelAttribute //綁定鍵值對到Model裏面,全局的@RequestMapping都能獲取此處設置的鍵值對
    public void addAttr(Model model){
        model.addAttribute("key",1);
    }
    


}

比較常用的是@ExceptionHandler

4.其他的配置

無邏輯的頁面跳轉

每一個無邏輯的頁面跳轉都需要寫一個Controller,這樣不做邏輯處理的Controller我們可以直接通過spring MVC的配置類來完成跳轉。(重寫 WebMvcConfigurerAdapter的addViewControllers方法來簡化配置

	/* 
	 * 通用頁面跳轉
	 */
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
        //第一個代表路徑,相當於@RequestMapping("/index"),
        //第二個相當於視圖名稱,return index;
		registry.addViewController("/index").setViewName("/index");
		registry.addViewController("/hello").setViewName("/hello");
	}

路徑匹配參數配置

在spring MVC中,路徑參數帶"."的話,"."後面的值將被忽略,例如,訪問http://localhost:8080/mvc/index.yy,此時"."後面的yy將被忽略。

通過重寫configurePathMatch()方法可不忽略"."後面的參數

/* 
	 * 不忽略.後面的參數
	 */
	@Override
	public void configurePathMatch(PathMatchConfigurer configurer) {
		configurer.setUseSuffixPatternMatch(false);
	}

更多的springMVC的配置可以參考WebMvcConfigurerAdapter類的API(在新版的springboot 2.0 中WebMvcConfigurerAdapter類已經過時,推薦我們使用它的接口WebMvcConfigurer).

/**
 * An implementation of {@link WebMvcConfigurer} with empty methods allowing
 * subclasses to override only the methods they're interested in.
 *
 * @author Rossen Stoyanchev
 * @since 3.1
 * @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
 * possible by a Java 8 baseline) and can be implemented directly without the
 * need for this adapter
    意思是Java8給出了新的特性,使得接口方法可以擁有默認實現。所以你現在可以直接實現  
      WebMvcConfigurer而不用像以前那樣通過繼承它的實現類來達到目的。 
 */
 @Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
...
}

 

springMVC配置的解析:

在配置springMVC中我們使用到了@EnableWebMvc 、WebMvcConfigurationAdapter和WebMvcConfigurationSupport,他們到底有什麼區別?

使用@EnableWebMvc,開啓對springMVC配置的支持爲什麼配置就能開啓springMVC的支持?

繼承WebMvcConfigurationAdapter,重寫方法實現對springMVC的配置

總結關於配置和註解的使用如下:

  1. @EnableWebMvc+extends WebMvcConfigurationAdapter,在擴展的類中重寫父類的方法即可,這種方式會屏蔽springboot的@EnableAutoConfiguration中的設置

  2. extends WebMvcConfigurationSupport,在擴展的類中重寫父類的方法即可,這種方式會屏蔽springboot的@EnableAutoConfiguration中的設置

  3. extends WebMvcConfigurationAdapter,在擴展的類中重寫父類的方法即可,這種方式依舊使用springboot的@EnableAutoConfiguration中的設置

  4. @EnableWebMvc=WebMvcConfigurationSupport,

    使用了@EnableWebMvc註解等於擴展了WebMvcConfigurationSupport但是沒有重寫任何方

解析以上:

關於@EnableAutoConfiguration中自動裝配的原理,關於@EnableAutoConfiguration的配置

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

 可以看到,只有當WebMvcConfigurationSupport類不存在的時候,該自動裝配類纔會創建出來,也就是說,如果我們使用@EnableWebMvc,就相當於導入了WebMvcConfigurationSupport類,這個時候,spring boot的@EnableAutoConfiguration的自動裝配也不會發生了,這個時候,我們能用的,只有WebMvcConfigurationSupport提供的若干個配置,所以無論是使用@EnableWebMvc還是擴展WebMvcConfigurationSupport類,spring都會創建一個WebMvcConfigurationSupport的類,進而屏蔽掉自動裝配類WebMvcAutoConfiguration

那麼爲何擴展WebMvcConfigurationAdapter(Java8 有接口默認方法後,該類被廢棄(Spring boot 2.0+),轉爲使用WebMvcConfigurer接口,下文我們統一用WebMvcConfigurer)不會屏蔽掉spring boot的自動裝配呢?

實際上,使用@EnableWebMvc引入的類不是WebMvcConfigurationSupport,而是DelegatingWebMvcConfiguration。 
DelegatingWebMvcConfiguration繼承了WebMvcConfigurationSupport,並且DelegatingWebMvcConfiguration通過依賴注入,持有Spring 容器所有WebMvcConfigurer實現類的一個List,所有對spring mvc的自定義的WebMvcConfigurer,都會掛到DelegatingWebMvcConfiguration上去。而WebMvcAutoConfiguration內部,創建了一個繼承了DelegatingWebMvcConfiguration的內部類EnableWebMvcConfiguration,這個類,一方面,提供了缺失的WebMvcConfigurationSupport的功能,另一方面,就起到了收集所有WebMvcConfigurer,調用它們的方法的作用。
 

總結一下:
    1. 無論是使用@EnableWebMvc還是WebMvcConfigurationSupport,都會禁止Spring boot的自動裝配,只有使用WebMvcConfigurer接口纔不會 
    2. 雖然禁止了Spring boot的自動裝配,但是WebMvcConfigurationSupport本身,還是會註冊一系列的MVC相關的bean的,從附加的api可以看到 
 3. WebMvcAutoConfiguration自動裝配,其實會創建一個WebMvcConfigurationSupport的子類,叫EnableWebMvcConfiguration

 

Spring MVC的高級配置

文件上傳的配置

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章