1. 程式人生 > >SpringBoot學習-SpringMVC自動配置

SpringBoot學習-SpringMVC自動配置

SpringBoot學習-SpringMVC自動配置

 

前言

在SpringBoot官網對於SpringMVCde 自動配置介紹

1-原文介紹如下:

Spring MVC Auto-configuration

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.

The auto-configuration adds the following features on top of Spring’s defaults:

  • Inclusion of ContentNegotiatingViewResolver
     and BeanNameViewResolver beans.
  • Support for serving static resources, including support for WebJars (covered later in this document)).
  • Automatic registration of ConverterGenericConverter, and Formatter beans.
  • Support for HttpMessageConverters (covered 
    later in this document
    ).
  • Automatic registration of MessageCodesResolver (covered later in this document).
  • Static index.html support.
  • Custom Favicon support (covered later in this document).
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered 
    later in this document
    ).

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMappingRequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

 

2-谷歌翻譯如下

Spring MVC自動配置

Spring Boot為Spring MVC提供自動配置,且適用於大多數應用程式。

自動配置在Spring預設配置基礎上,添加了以下功能:

  • 包括:ContentNegotiatingViewResolver和BeanNameViewResolver bean。
  • 支援提供靜態資源,包括對WebJars的支援。
  • 自動註冊Converter,GenericConverter和Formatter bean。
  • 支援HttpMessageConverters。
  • 自動註冊MessageCodesResolver(本文件後面會介紹)。
  • 靜態 index.html 頁面支援。
  • 自定義Favicon支援。
  • 自動使用ConfigurableWebBindingInitializer bean)。

如果要保留Spring Boot MVC功能並且想要新增其他MVC配置(interceptors,formatters,view controllers和其他功能),可以新增自己的@Configuration類,型別為WebMvcConfigurer,但不包含@EnableWebMvc。如果您希望可以自定義RequestMappingHandlerMapping,RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver例項,則可以宣告WebMvcRegistrationsAdapter例項以提供此類元件。

如果您想完全控制Spring MVC,可以使用@EnableWebMvc新增自己的@Configuration註釋。

 

以下正文,對上面標紅的內容進行分析

 

正文

 

1- ContentNegotiatingViewResolver and BeanNameViewResolver beans

1-1-原始碼出處

 

public class WebMvcAutoConfiguration {
    //-在Springboot啟動初始化時,自動裝載WebMvcAutoConfiguration類,
    //其中包括WebMvcAutoConfiguration內部類WebMvcAutoConfigurationAdapter下的ContentNegotiatingViewResolver viewResolver()方法
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {

        @Bean
        @ConditionalOnBean({ViewResolver.class})
        @ConditionalOnMissingBean(
            name = {"viewResolver"},
            value = {ContentNegotiatingViewResolver.class}
        )
        public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
            ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
            resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
            resolver.setOrder(-2147483648);
            return resolver;
        }

    }
}

 

public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered, InitializingBean {
    //2-ContentNegotiatingViewResolver 執行其內部initServletContext()初始化方法,從BeanFactoryUtils中獲取全部ViewResolver
    protected void initServletContext(ServletContext servletContext) {
        Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
        ViewResolver viewResolver;
        if (this.viewResolvers == null) {

            Iterator var3 = matchingBeans.iterator();
            while(var3.hasNext()) {
                viewResolver = (ViewResolver)var3.next();
                if (this != viewResolver) {
                    this.viewResolvers.add(viewResolver);
                }
            }
        } 
        …………………………省略
    }
    
    //2-當一個請求進來時,呼叫ContentNegotiatingViewResolver 下的View resolveViewName()方法,,並返回bestView,主要包括beanName引數,即對應渲染的(比如:html)檔名稱
    @Nullable
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        //ContentNegotiatingViewResolver  根據檔名和請求頭型別來決定返回什麼樣的View。而mediaTypes這個屬性儲存了 你請求字尾名 或者 引數 所對應 的mediaType        
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
        if (requestedMediaTypes != null) {
            //獲取候選檢視物件
            List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
            //選擇一個最適合的檢視物件
            View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
            if (bestView != null) {
                //返回檢視物件
                return bestView;
            }
        }
        ……………省略
    }
    
    //2-1-獲取候選檢視物件
    private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
        List<View> candidateViews = new ArrayList();
        if (this.viewResolvers != null) {
            //獲取全部檢視解析器
            Iterator var5 = this.viewResolvers.iterator();
            //篩選出候選View
            while(var5.hasNext()) {
                …………………………省略
                while(var11.hasNext()) {
                    String extension = (String)var11.next();
                    String viewNameWithExtension = viewName + '.' + extension;
                    view = viewResolver.resolveViewName(viewNameWithExtension, locale);
                    if (view != null) {
                        candidateViews.add(view);//獲取view
                    }
                }
            }

        }
        return candidateViews;
    }
}

 

 

1-2-原始碼解釋

  • 1-在Springboot啟動初始化時,自動裝載WebMvcAutoConfiguration類,其中包括WebMvcAutoConfiguration內部類WebMvcAutoConfigurationAdapter下的ContentNegotiatingViewResolver viewResolver()方法
  • 2-ContentNegotiatingViewResolver 執行其內部initServletContext()初始化方法,從BeanFactoryUtils中獲取全部ViewResolver
  • 3-當一個請求進來時,呼叫ContentNegotiatingViewResolver 下的View resolveViewName()方法根據方法的返回值得到檢視物件(View),,並返回bestView,主要包括beanName引數,即對應渲染的(比如:html)檔名稱

1-3-自定義檢視解析器

1-3-1-自定義解析其原始碼

@SpringBootApplication
public class SpringbootdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootdemoApplication.class, args);
    }

    //在Springboot啟動時,載入自定義的MyViewResolver類
    @Bean
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }
    
    //(這裡為了方便,在啟動類下寫了一個內部類)
    //MyViewResolver實現介面類ViewResolver
    private static class MyViewResolver implements ViewResolver{

        @Override
        public View resolveViewName(String s, Locale locale) throws Exception {
            return null;
        }
    }

}

 

1-3-2-原始碼解析

  • 1-自定義MyViewResolver 類必須實現ViewResolver介面
  • 2-並在Springboot初始化是,被自動載入

1-3-3-使用結果:在Spring分發請求時,發現自定義的MyViewResolver被被載入

 

 

2-支援提供靜態資源,包括對WebJars的支援。請參考本人部落格:https://www.cnblogs.com/wobuchifanqie/p/10112302.html

 

3-自動註冊Converter,GenericConverter和Formatter bean

3-1-自定義Converter

 

//1-controller層
@Controller
public class DemoController {
    Logger log = LoggerFactory.getLogger(DemoController.class);

    @RequestMapping(value="get/student")
    public String getStudent(Student student,Model model){
        log.debug("student: " + student);
        model.addAttribute("student",student);
        return "student";
    }

}
//2-自定義converter
@Configuration
public class StringToStudentConverrter implements Converter<String,Student> {
    @Override
    public Student convert(String s) {
        //student=tyj-18
        String[] split = s.split("-");
        Student student = new Student(split[0],Integer.parseInt(split[1]));
        return student;
    }
}

 

//3-html示例
<!
DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div th:text="${student.name}"></div> <div th:text="${student.age}"></div> <strong>success!</strong> </body> </html>

4-支援HttpMessageConverters:SpringMVC用來轉換Http請求和響應的;是從容器中確定;獲取所有的HttpMessageConverter

4-2-自定義HttpMessageConverters

//1-自定義HttpMessageConverters
public class MyMessageConveter extends AbstractHttpMessageConverter<Student>{

    public MyMessageConveter() {
        //Content-Type= application/xxx-tyj
        super(new MediaType("application", "xxx-tyj", Charset.forName("UTF-8")));
    }

    @Override
    protected boolean supports(Class<?> aClass) {
        // 表明只處理UserEntity型別的引數。
        return Student.class.isAssignableFrom(aClass);
    }

    //重寫readlntenal 方法,處理請求的資料。程式碼表明我們處理由“-”隔開的資料,並轉成 Student型別的物件。
    @Override
    protected Student readInternal(Class<? extends Student> aClass, HttpInputMessage httpInputMessage)
            throws IOException, HttpMessageNotReadableException {
        String content = StreamUtils.copyToString(httpInputMessage.getBody(), Charset.forName("UTF-8"));
        String[] split = content.split("-");
        return new Student(split[0],Integer.parseInt(split[1]));
    }

    //重寫writeInternal ,處理如何輸出資料到response。
    @Override
    protected void writeInternal(Student student, HttpOutputMessage httpOutputMessage)
            throws IOException, HttpMessageNotWritableException {
            String out = "hello: " +student.getName() + " ,你今年" + student.getAge() + "了麼?";
             httpOutputMessage.getBody().write(out.getBytes());
    }
}
//2-載入自定義的MyMessageConveter 
@Configuration
public class MySpringBootConfig  {
    @Bean
    public MyMessageConveter MyMessageConveter (){
        return new MyMessageConveter();
    }
}
//3-controller
@Controller
public class DemoController {

    @RequestMapping(value="hello/student",method = POST)
    @ResponseBody
    public Student helloStudent(@RequestBody  Student student){
        return student;
    }

}

 

介面測試
URL:POST   localhost:8080/hello/student
header: Content-Type = application/xxx-tyj
body: tyj-21

返回值:hello: tyj ,你今年21了麼?

 

4-3-新增一個converter的方式有三種

@Configuration
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
    // 新增converter的第一種方式,程式碼很簡單,也是推薦的方式
    // 這樣做springboot會把我們自定義的converter放在順序上的最高優先順序(List的頭部)
    // 即有多個converter都滿足Accpet/ContentType/MediaType的規則時,優先使用我們這個
    @Bean
    public MyMessageConveter MyMessageConveter (){
        return new MyMessageConveter();
    }
    // 新增converter的第二種方式
    // 通常在只有一個自定義WebMvcConfigurerAdapter時,會把這個方法裡面新增的converter(s)依次放在最高優先順序(List的頭部)
    // 雖然第一種方式的程式碼先執行,但是bean的新增比這種方式晚,所以方式二的優先順序 大於 方式一
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // add方法可以指定順序,有多個自定義的WebMvcConfigurerAdapter時,可以改變相互之間的順序
        // 但是都在springmvc內建的converter前面
        converters.add(new MyMessageConveter());
    }

    // 新增converter的第三種方式
    // 同一個WebMvcConfigurerAdapter中的configureMessageConverters方法先於extendMessageConverters方法執行
    // 可以理解為是三種方式中最後執行的一種,不過這裡可以通過add指定順序來調整優先順序,也可以使用remove/clear來刪除converter,功能強大
    // 使用converters.add(xxx)會放在最低優先順序(List的尾部)
    // 使用converters.add(0,xxx)會放在最高優先順序(List的頭部)
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new MyMessageConveter());
    }
}

 

5-自動註冊MessageCodesResolver:定義錯誤程式碼生成規則

 

6-靜態 index.html 頁面支援。請參考本人部落格:https://www.cnblogs.com/wobuchifanqie/p/10112302.html

 

7-自定義Favicon支援。請參考本人部落格:https://www.cnblogs.com/wobuchifanqie/p/10112302.html

 

8-自動使用ConfigurableWebBindingInitializer bean。

 

 

 

參考資料

1-https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration

2-http://www.cnblogs.com/huhx/p/baseusespringbootconverter1.html

3-https://www.cnblogs.com/page12/p/8166935.html

4-http://www.cnblogs.com/hhhshct/p/9676604.html