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的配置
總結關於配置和註解的使用如下:
-
@EnableWebMvc+extends WebMvcConfigurationAdapter,在擴展的類中重寫父類的方法即可,這種方式會屏蔽springboot的@EnableAutoConfiguration中的設置
-
extends WebMvcConfigurationSupport,在擴展的類中重寫父類的方法即可,這種方式會屏蔽springboot的@EnableAutoConfiguration中的設置
-
extends WebMvcConfigurationAdapter,在擴展的類中重寫父類的方法即可,這種方式依舊使用springboot的@EnableAutoConfiguration中的設置
-
@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的高級配置
文件上傳的配置