关注“Java后端技术全栈”**
回复“面试”获取全套大厂面试材料
明天咱们来说说Spring Boot的外围——主动拆卸原理。
大家都记得咱们SpringBoot我的项目有个启动类XXApplication.java
类。上面就是启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
其中这里有外围注解@SpringBootApplication
注解@SpringBootApplication
其源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//先省略
}
它次要加载了@SpringBootApplication注解主配置类,这个@SpringBootApplication注解主配置类里边最次要的性能就是SpringBoot开启了一个@EnableAutoConfiguration注解的主动配置性能。
这外面的重要关注的三个注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
注解@EnableAutoConfiguration:
翻译过去就是开启主动配置,源码如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//省略
}
它次要利用了一个EnableAutoConfigurationImportSelector选择器给Spring容器中来导入一些组件。其中的要害性能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()办法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具备META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
这个spring.factories文件也是一组一组的key=value的模式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:
再来看看spring.factories的内容
每一个xxxAutoConfiguration类都是容器中的一个组件,并都退出到容器中。退出到容器中之后的作用就是用它们来做主动配置,这就是Springboot主动配置之源,也就是主动配置的开始,只有这些主动配置类进入到容器中当前,接下来这个主动配置类才开始进行启动。
这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的外部就会执行selectImports()办法,找到所有JavaConfig主动配置类的全限定名对应的class,而后将所有主动配置类加载到Spring容器中。
先看EnableAutoConfigurationImportSelector的类图
重要办法
protected Collection<String> loadFactoryNames(Class<?> source) {
return SpringFactoriesLoader.loadFactoryNames(source,
getClass().getClassLoader());
}
public final class SpringFactoriesLoader {
//读取配置文件META-INF下的spring.factories
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, 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 factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
}
读取进去后如何使其失效呢?
主动配置失效
每一个XxxxAutoConfiguration主动配置类都是在某些条件之下才会失效的,这些条件的限度在Spring Boot中以注解的模式体现,常见的条件注解有如下几项:
@ConditionalOnBean:当容器里有指定的bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类门路下有指定类的条件下。
@ConditionalOnMissingClass:当类门路下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比方@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的状况下也为true。
以ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何失效,比方:server.port=8081,是如何失效的(当然不配置也会有默认值,这个默认值来自于org.apache.catalina.startup.Tomcat)。
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
//省略
}
这里看到一个tomcat servlet相干的类了。
public class TomcatServletWebServerFactoryCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered {
private final ServerProperties serverProperties;
public TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
}
//省略
}
再进serverProperties中,终于发现一个相熟的注解@ConfigurationProperties了,读取配置文件中server为前缀的配置项。
ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
private InetAddress address;
//省略
}
而@EnableConfigurationProperties负责导入这个曾经绑定了属性的bean到spring容器中(见下面截图)。那么所有其余的和这个类相干的属性都能够在全局配置文件中定义,也就是说,真正“限度”咱们能够在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字结尾的一组属性是惟一对应的。
至此,咱们大抵能够理解。在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,而后再通过@EnableConfigurationProperties注解导入到Spring容器中。
而诸多的XxxxAutoConfiguration主动配置类,就是Spring容器的JavaConfig模式,作用就是为Spring 容器导入bean,而所有导入的bean所须要的属性都通过xxxxProperties的bean来取得。
借用网上一张图来回顾整个流程:
解锁面试被问
面试官可能看到你简历上写着把握SpringBoot,通常都会让你说说Spring Boot主动拆卸原理。如果你能把下面过程中的源码讲一遍给他听,那是很NB了。然而通常只有如下这样答复就能够了。
Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有主动配置类,并对其进行加载,而这些主动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig模式的Spring容器配置类,它能通过以Properties结尾命名的类中获得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。
ok,明天就聊到这里。
码字不易,点个在看+分享朋友圈呗!!!
举荐浏览
如何优雅的导出 Excel
终于明确为什么要加 final 关键字了!
发表回复