自动配置 启动器 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency >
是SpringBoot的启动场景
比如使用spring-boot-starter-web
,他会帮我们自动导入web环境所有的依赖
SpringBoot会将所有的场景都变成一个个的启动器,我们要使用什么功能就只需要引入对应的启动器即可
Starters 启动器列表
注解 1 2 3 4 5 6 7 @SpringBootConfiguration: springboot的配置 @Configuration: Spring的配置类 @Component: 是Spring的组件 @EnableAutoConfiguration: 自动配置 @AutoConfigurationPackage: 自动配置包 @Import(AutoConfigurationPackages.Registrar.class): 自动配置包注册 @Import(AutoConfigurationImportSelector.class): 自动配置导入选择
org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected AutoConfigurationEntry getAutoConfigurationEntry (AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); } protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct." ); return configurations; }
META-INF/spring.factories: 自动配置的核心文件
org/springframework/core/io/support/SpringFactoriesLoader.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }private static Map<String, List<String>> loadSpringFactories (@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null ) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap <>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource (url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException ("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]" , ex); } }
结论:springboot所有的自动配置都是在启动的时候扫描并加载:spring.factories
中的所有的自动配置类,但是不一定全部都生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功了
springboot在启动的时候从类路径下META-INF/spring.factories
获取指定的值
将这些配置的类导入容器,自动配置就会生效,帮我们进行自动配置
以前需要我们配置的东西,现在springboot帮我们做了
整合javaEE,解决方案和自动配置的东西都在spring-boot-test-autoconfigure-2.2.5.RELEASE.jar
这个包下
它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器了
spring.factories
中存在许多xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入类这个场景需要的所有组件,并自动配置
有了自动配置类(@Configuration),免去了我们手动配置
application配置文件 如何知道可以在配置文件中配置哪些项目?
META-INF/spring.factories
下存在被加载的配置类
以HttpEncodingAutoConfiguration
为例,进入HttpEncodingAutoConfiguration.java
@EnableConfigurationProperties(HttpProperties.class)
可以看到该类关联了HttpProperties.class
做为Properties
该类里面的属性为@ConfigurationProperties(prefix = "spring.http")
配置的前缀后的属性
1 2 3 4 5 @ConfigurationProperties(prefix = "spring.http") public class HttpProperties { }
主启动类运行原理 1 2 3 4 5 6 7 8 9 @SpringBootApplication public class SpringBootDemoApplication { public static void main (String[] args) { SpringApplication.run(SpringBootDemoApplication.class, args); } }
类SpringApplication
做了以下事情:
推动应用的类型是普通项目还是web项目
查找并加载所有可用的初始化器,设置到initializers属性中
找出所有的应用程序监听器,设置到listeners属性中
推断并设置main方法的定义类,找到运行的主类
静态资源加载 WebMvcAutoConfiguration.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public void addResourceHandlers (ResourceHandlerRegistry registry) { if (!this .resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled" ); return ; } Duration cachePeriod = this .resourceProperties.getCache().getPeriod(); 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)); } }
WebMvcProperties.java
1 2 3 4 5 private String staticPathPattern = "/**" ;public String getStaticPathPattern () { return this .staticPathPattern; }
ResourceProperties.java
1 2 3 4 5 6 7 8 private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/" , "classpath:/resources/" , "classpath:/static/" , "classpath:/public/" };private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
静态资源加载方法
webjars
映射到:localhost:8080/webjars/
public, static, /**, resources目录下
静态资源加载路径优先级 resources > static(默认) > public
自定义静态资源路径 application.properties
1 spring.mvc.static-path-pattern =/hello,classpath:/test/
国际化 WebMvcAutoConfiguration.java
:
1 2 3 4 5 6 7 8 9 10 11 12 @Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "spring.mvc", name = "locale") public LocaleResolver localeResolver () { if (this .mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) { return new FixedLocaleResolver (this .mvcProperties.getLocale()); } AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver (); localeResolver.setDefaultLocale(this .mvcProperties.getLocale()); return localeResolver; }
可以看到这里使用到了AcceptHeaderLocaleResolver
这个类来进行国际化解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class AcceptHeaderLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale (HttpServletRequest request) { Locale defaultLocale = getDefaultLocale(); if (defaultLocale != null && request.getHeader("Accept-Language" ) == null ) { return defaultLocale; } Locale requestLocale = request.getLocale(); List<Locale> supportedLocales = getSupportedLocales(); if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) { return requestLocale; } Locale supportedLocale = findSupportedLocale(request, supportedLocales); if (supportedLocale != null ) { return supportedLocale; } return (defaultLocale != null ? defaultLocale : requestLocale); } }
配置自己的国际化解析器 和AcceptHeaderLocaleResolver
一样需要继承LocaleResolver
类,并实现resolveLocale
和setLocale
这两个方法
com/sicmatr1x/config/MyLocaleResolver.java
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.sicmatr1x.config;import java.util.Locale;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.util.StringUtils;import org.springframework.web.servlet.LocaleResolver;public class MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale (HttpServletRequest request) { String language = request.getParameter("l" ); Locale locale = Locale.getDefault(); if (!StringUtils.isEmpty(language)) { String[] split = language.split("_" ); locale = new Locale (split[0 ], split[1 ]); } return locale; } @Override public void setLocale (HttpServletRequest request, HttpServletResponse response, Locale locale) { } }
然后需要到MyMvcConfig
中去注册即可
com/sicmatr1x/config/MyMvcConfig.java
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers (ViewControllerRegistry registry) { registry.addViewController("/" ).setViewName("index" ); registry.addViewController("/index.html" ).setViewName("index" ); } @Bean public LocaleResolver localeResolver () { return new MyLocaleResolver (); } }