springboot-08-Web启动器

spring-boot-starter-web(Web启动器)

简介

Spring MVC是Spring提供的一个基于MVC设计模式的轻量级开发框架,其本身是Spring框架的一部分,与Spring无缝集成。

Spring Boot是在Spring基础上创建的一款开源框架,它提供spring-boot-starter-web(Web场景启动器)为Web开发提供支持。其中spring-boot-starter-web为我们提供了大量的自动配置类,并嵌入了Servlet容器以及SpringMVC的依赖,使用于大多数Web开发场景

Spring Boot Web开发

Spring Boot为SpringMVC提供了大量的自动配置,并在对其进行了拓展:

  1. 引入了 ContentNegotiatingViewResolver 和 BeanNameViewResolver(视图解析器)
    在这里插入图片描述

  2. 对包括 WebJars 在内的静态资源的支持
    在这里插入图片描述

  3. 自动注册 Converter、GenericConverter 和 Formatter (转换器和格式化器)
    在这里插入图片描述

  4. 对 HttpMessageConverters 的支持(Spring MVC 中用于转换 HTTP 请求和响应的消息转换器)
    在这里插入图片描述

5.自动注册 MessageCodesResolver(用于定义错误代码生成规则)
在这里插入图片描述

  1. 支持对静态首页(index.html)的访问
    在这里插入图片描述

  2. 自动使用 ConfigurableWebBindingInitializer(初始化数据绑定器)

在这里插入图片描述

只要pom.xml引入了spring-boot-starter-web,就可以直接运行web项目了。

示例

  1. 创建一个名为spring-boot-springmvc-demo1的Spring Boot项目,发现在pom.xml已经帮我们导入了spring-boot-starter-web依赖
   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>

  1. 在项目的包名下添加一个controller的子包,创建一个名为UseController的类,代码如下:
package com.liang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("hello")
public class UseController {
    @RequestMapping("/s")
    @ResponseBody
    public String test()
    {
        return "hello,springboot";
    }
}

  1. 启动Spring Boot ,浏览器访问“http://localhost:8080/hello/s”
    在这里插入图片描述

注意:如果我们要开发的是web项目,则不需要去导入spring-boot-starter包了,因为spring-boot-starter-web已经帮我们导入了
在这里插入图片描述


MVC自动配置原理

官方阅读

基于源码分析外:,我们可以通过官方文档来查看Spring Boot 对Spring MVC做了哪些配置,包括如何拓展,如何配置


Spring MVC Auto-configuration
// Spring Boot为Spring MVC提供了自动配置,它可以很好地与大多数应用程序一起工作。

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
// 自动配置在Spring默认设置的基础上添加了以下功能:

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

// 包含视图解析器
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

// 支持静态资源文件夹的路径,以及webjars
Support for serving static resources, including support for WebJars 

// 自动注册了Converter:
// 转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把"1"字符串自动转换为int类型
// Formatter:【格式化器,比如页面给我们了一个2019-8-10,它会给我们自动格式化为Date对象】
Automatic registration of Converter, GenericConverter, and Formatter beans.

// HttpMessageConverters
// SpringMVC用来转换Http请求和响应的的,比如我们要把一个User对象转换为JSON字符串,可以去看官网文档解释;

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).
// 初始化数据绑定器:帮我们把请求数据绑定到JavaBean中!

Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

/*
如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己
的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。如果希望提供
RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义
实例,则可以声明WebMVCregistrationAdapter实例来提供此类组件。
*/
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 RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or 
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.


// 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc进行注释。
If you want to take complete control of Spring MVC, you can
 add your own @Configuration annotated with @EnableWebMvc.

首先配置了视图解析器,我们先看看它是怎么配置的

ContentNegotiatingViewResolver内容协商视图器

自动配置了ViewResolver,我们之前SpringMVC学习的视图解析器。

即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。

		@Bean
		@ConditionalOnBean(ViewResolver.class)
		//在ViewResolver在视图解析器的条件下
		@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
		public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
			ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
			resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
			// ContentNegotiatingViewResolver uses all the other view resolvers to locate
			// a view so it should have a high precedence
			resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
			  // ContentNegotiatingViewResolver使用所有其他视图解析器来定位视图,因此它应该具有较高的优先级
			return resolver;
		}

点进ContentNegotiationViewResolver类看看

 @Nullable  // 注解说明:@Nullable 即参数可为null
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
        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;
            }
        }

继续点击getCandidateViews()查看它是怎么获取候选视图的

    private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
        List<View> candidateViews = new ArrayList();
        if (this.viewResolvers != null) {
            Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
            Iterator var5 = this.viewResolvers.iterator();
			//获取所有的解析视图器,进行while循环
            while(var5.hasNext()) {
                ViewResolver viewResolver = (ViewResolver)var5.next();
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    candidateViews.add(view);
                }

                Iterator var8 = requestedMediaTypes.iterator();

                while(var8.hasNext()) {
                    MediaType requestedMediaType = (MediaType)var8.next();
                    List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
                    Iterator var11 = extensions.iterator();

                    while(var11.hasNext()) {
                        String extension = (String)var11.next();
                        String viewNameWithExtension = viewName + '.' + extension;
                        view = viewResolver.resolveViewName(viewNameWithExtension, locale);
                        if (view != null) {
                            candidateViews.add(view);
                        }
                    }
                }
            }
        }

通过getCandidateViews()方法中看到它是把所有的视图解析器拿来,进行while循环,挨个解析!

结论: ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的

查看ContentNegotiatingViewResolver的组合逻辑,有个viewResolvers,看看它是如何赋值的

protected void initServletContext(ServletContext servletContext) {
        Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
        //可以看到viewResolver 是从BeanFactoryUtils工具类获取容器中所有的视图解析器
        //viewResolver.class把所有的视图解析器组合
        ViewResolver viewResolver;
        if (this.viewResolvers == null) {
            this.viewResolvers = new ArrayList(matchingBeans.size());
            Iterator var3 = matchingBeans.iterator();

            while(var3.hasNext()) {
                viewResolver = (ViewResolver)var3.next();
                if (this != viewResolver) {
                    this.viewResolvers.add(viewResolver);
                }
            }
        } else {
            for(int i = 0; i < this.viewResolvers.size(); ++i) {
                viewResolver = (ViewResolver)this.viewResolvers.get(i);
                if (!matchingBeans.contains(viewResolver)) {
                    String name = viewResolver.getClass().getName() + i;
                    this.obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolver, name);
                }
            }
        }

        AnnotationAwareOrderComparator.sort(this.viewResolvers);
        this.cnmFactoryBean.setServletContext(servletContext);
    }


给容器添加自定义视图解析器

  1. 我们在我们的主程序中写一个视图解析器
	@Bean
	public ViewResolver myViewResolver()
	{
		return new MyViewResolver();
	}

	//写一个静态内部类,将视图解析器需要实现ViewResolver接口
	private static class MyViewResolver implements ViewResolver{

		@Override
		public View resolveViewName(String viewName, Locale locale) throws Exception {
			return null;
		}
	}
  1. 怎么看我们自己写的视图解析器有没有起作用呢?

我们给 DispatcherServlet 中的 doDispatch方法 加个断点进行调试一下(DispatcherServlet可以声明这个类),因为所有的请求都会走到这个方法中
在这里插入图片描述

  1. 我们启动我们的项目,然后随便访问一个页面,看一下Debug信息;

在这里插入图片描述

如果想要使用自己定制化的东西,我们只需要给容器中添加这个组件就好了!剩下的事情SpringBoot就会帮我们做了!


转换器和格式化器

找到格式化转化器:

		@Bean
		@Primary
		@Override
		public RequestMappingHandlerMapping requestMappingHandlerMapping(
				@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
				@Qualifier("mvcConversionService") FormattingConversionService conversionService,
				@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
			// Must be @Primary for MvcUriComponentsBuilder to work
			return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
					resourceUrlProvider);
		}

发现FormattingConversionService一个格式化转换服务

在这里插入图片描述
发现了有很多的格式化服务,我们也可以自己去配置自己的格式化方式。如果配置了自己的格式化方式,就会注册到Bean中生效,我们可以在配置文件中配置日期格式化的规则:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值