加深了解springboot(三)

我们使用springboot很大一部分场景是用于网页应用开发,但是现在大型的项目基本都采用前后端分离模式,所以后端一般只做api接口。而对于一些小型项目,可能前后端一体更节约时间。这次简单描述下springboot的web开发相关内容
使用SpringBoot;

1)、创建SpringBoot应用,选中我们需要的模块;

2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来

3)、自己编写业务代码;

自动配置原理?
这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?怎么扩展等等。

xxxxAutoConfiguration:帮我们给容器中自动配置组件;
xxxxProperties:配置类来封装配置文件的内容;

我们先来看看springboot对静态资源的映射。
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration类中,都是mvc相关的自动配置

		@Override
		public void addResourceHandlers(ResourceHandlerRegistry registry) {
			if (!this.resourceProperties.isAddMappings()) {
				logger.debug("Default resource handling disabled");
				return;
			}
			Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
			//@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
			//public class ResourceProperties implements ResourceLoaderAware {
			//resourceProperties可以设置跟资源有关的参数
			CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
				//静态资源文件夹映射
			if (!registry.hasMappingForPattern("/webjars/**")) {
				customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
						.addResourceLocations("classpath:/META-INF/resources/webjars/")
						.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
			}
			String staticPathPattern = this.mvcProperties.getStaticPathPattern();
			if (!registry.hasMappingForPattern(staticPathPattern)) {
				customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
						.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
						.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
			}
		}
		
		
        //配置欢迎页映射
        //HandlerMapping 底层保存的对应的请求谁来处理
		@Bean
		public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
				FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
			WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
					new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
					this.mvcProperties.getStaticPathPattern());
			welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
			welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
			return welcomePageHandlerMapping;
		}
		//实际还是在静态文件路径下拼上index.html
		private Optional<Resource> getWelcomePage() {
			String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
			return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
		}

		private Resource getIndexHtml(String location) {
			return this.resourceLoader.getResource(location + "index.html");
		}

webjars官网
1)、所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;

​ webjars:以jar包的方式引入静态资源;

localhost:8080/webjars/jquery/3.3.1/jquery.js

<!--引入jquery-webjar-->在访问的时候只需要写webjars下面资源的名称即可
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jquery</artifactId>
			<version>3.3.1</version>
		</dependency>

2)、"/**" 访问当前项目的任何资源,如果没有对应的处理规则,都去(静态资源的文件夹)找映射

"classpath:/META-INF/resources/", 
"classpath:/resources/",
"classpath:/static/", 
"classpath:/public/" 
"/":当前项目的根路径

localhost:8080/abc === 去静态资源文件夹里面找abc

3)、欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;
​ localhost:8080/ 找index页面

4)、手动定义静态资源映射路径,默认定义会失效;
spring.resources.static-locations=classpath:/fantasy,classpath:/fantasy2

  • SpringMVC自动配置
    这里摘录一段官方文档说明,再配上相应的源码

> 28.1.1 Spring MVC自动配置
Spring Boot为Spring MVC提供了自动配置,适用于大多数应用程序。

自动配置在Spring的默认值之上添加了以下功能:

包含ContentNegotiatingViewResolver和BeanNameViewResolver beans。
支持提供静态资源,包括对WebJars的支持( 本文档稍后介绍))。
自动注册Converter,GenericConverter和Formatter beans。
支持HttpMessageConverters( 本文档稍后部分)。
自动注册MessageCodesResolver( 本文档后面部分)。
静态index.html支持。
自定义Favicon支持(本文档稍后介绍)。
自动使用ConfigurableWebBindingInitializer bean(本文 后面会介绍)。
如果你想保留Spring Boot MVC功能,并且你想添加额外的 MVC配置(拦截器,格式化程序,视图控制器和其他功能),
你可以添加自己的@Configuration类WebMvcConfigurer类但没有 @EnableWebMvc。
如果您希望提供RequestMappingHandlerMapping,RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例
,则可以声明WebMvcRegistrationsAdapter实例以提供此类组件。

如果您想完全控制Spring MVC,可以添加自己的@Configuration注释@EnableWebMvc。

ContentNegotiatingViewResolver 组合所有的视图解析器,返回最优解

	@Nullable
	private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) {
		for (View candidateView : candidateViews) {
			if (candidateView instanceof SmartView) {
				SmartView smartView = (SmartView) candidateView;
				if (smartView.isRedirectView()) {
					return candidateView;
				}
			}
		}
	@Override
	protected void initServletContext(ServletContext servletContext) {
		Collection<ViewResolver> matchingBeans =
		BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
		//初始化时加载所有的视图解析器。们可以自定义视图解析器,在容器加载时,也会被加载进来
		//一个视图解析器实现ViewResolver接口,实现其方法 如下:
				//@Bean
			   	//public ViewResolver MyViewResolver(){
			      //  return new MyViewResolver();
			    //}
			   //  public static class MyViewResolver implements ViewResolver{
							// @Override
								// public View resolveViewName(String viewName, Locale locale) throws Exception {
			    				//	 return null;
			 			//	}
			//随后debug启动,可在DispatchServlet类的doDispatch方法中,找到此解析器
		if (this.viewResolvers == null) {
			this.viewResolvers = new ArrayList<>(matchingBeans.size());
			for (ViewResolver viewResolver : matchingBeans) {
				if (this != viewResolver) {
					this.viewResolvers.add(viewResolver);
				}
			}
		}else {
			for (int i = 0; i < this.viewResolvers.size(); i++) {
				ViewResolver vr = this.viewResolvers.get(i);
				if (matchingBeans.contains(vr)) {
					continue;
				}
				String name = vr.getClass().getName() + i;
				obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name);
			}

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

Converter:类型转换器 Formatter:格式化器

			/**
			* Add {@link GenericConverter}, {@link Converter}, {@link Printer}, {@link Parser}
			* and {@link Formatter} beans from the specified context.
			* @param registry the service to register beans with
			* @param beanFactory the bean factory to get the beans from
			* @since 2.2.0
			*/
			public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) {
			Set<Object> beans = new LinkedHashSet<>();
			beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values());
			beans.addAll(beanFactory.getBeansOfType(Converter.class).values());
			beans.addAll(beanFactory.getBeansOfType(Printer.class).values());
			beans.addAll(beanFactory.getBeansOfType(Parser.class).values());
			for (Object bean : beans) {
			if (bean instanceof GenericConverter) {
			registry.addConverter((GenericConverter) bean);
			}
			else if (bean instanceof Converter) {
			registry.addConverter((Converter<?, ?>) bean);
			}
			else if (bean instanceof Formatter) {
			registry.addFormatter((Formatter<?>) bean);
			}
			else if (bean instanceof Printer) {
			registry.addPrinter((Printer<?>) bean);
			}
			else if (bean instanceof Parser) {
			registry.addParser((Parser<?>) bean);
			}
			}
			}
			// 同理,我们自己也可以自定义转换器,再添加到容器里

HttpMessageConverters:springmvc转换http请求和响应

 public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
				ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
				ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
				ObjectProvider<DispatcherServletPath> dispatcherServletPath) {
			this.resourceProperties = resourceProperties;
			this.mvcProperties = mvcProperties;
			this.beanFactory = beanFactory;
			this.messageConvertersProvider = messageConvertersProvider;
			this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
			this.dispatcherServletPath = dispatcherServletPath;
		}
		//HttpMessageConverters 有参构造方法,入参都是从容器取的。自定义与以上同理

MessageCodesResolver:定义错误代码生成规则
ConfigurableWebBindingInitializer:初始化web数据绑定器WebDataBinder

org.springframework.boot.autoconfigure.web:web的所有自动配置场景; 详细的大家可以自己看看
自动配置的场景太多,再次不一一列举,说的最清楚的应该是官方文档。

扩展springmvc,既保留了所有自动配置,也能用我们自定义的配置
编写一个配置类(@Configuration),实现接口WebMvcConfigurer,自定义拓展;不能标注@EnableWebMvc

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //浏览器发送MyMvcConfig请求,来到index.html页面
        registry.addViewController("/MyMvcConfig").setViewName("index");
    }
}

原因分析:
WebMvcAutoConfiguration是SpringMVC的自动配置类
在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)

	@Configuration(proxyBeanMethods = false)
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
			//一个参考实现:将所有的webmvc相关配置都一起添加到容器
			//@Override
			//public void addViewControllers(ViewControllerRegistry registry) {
			//	for (WebMvcConfigurer delegate : this.delegates) {
			//		delegate.addViewControllers(registry);
			//	}
			//}
		}
	}

容器中所有的WebMvcConfigurer都会一起起作用
效果:SpringMVC的自动配置和我们的扩展配置都会起作用;

全面接管springmvc 加@EnableWebMvc 默认配置不启用,所有配置自己配,but why?

@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//看下面这个条件
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

然后我们应该掌握一些共同点

  1. SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
  2. 在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
  3. 在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置
    以上,说的有点乱,大家多看源码多看文档吧
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值