前言:SpringBoot作为新一代框架,极大简化了Spring应用的开发过程,成为市面上主流的开发框架。整个SpringBoot的核心其实就是自动装配,而自动装配所做的,就是自动将Bean装配到IOC容器中这么一个操作。
阅读本文章前,请务必了解 Maven Spring等相关技术,按需求自行创建SpringBoot应用
目录:
- @SpringBootApplication
- @EnableAutoConfiguration
- 总结
- spring.factories拓展
1.@SpringBootApplication
@SpringBootApplication
public class XimuApplication {
public static void main(String[] args) {
SpringApplication.run(XimuApplication.class, args);
}
}
我们先来分析一下主启动类,发现有个叫@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 {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
//根据包路径扫描
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
//直接根据class类扫描
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
@Target(ElementType.TYPE),@Retention(RetentionPolicy.RUNTIME),@Documented,@Inherited这四个基础注解本文不做赘述,请自行百度。
着重来看下面三个注解
@SpringBootConfiguration:顾名思义,这个实质就是Configuration配置类,相当于一个beans.xml文件,用到后面我们会见到各种各样的Configuration。
@ComponentScan:它的功能其实就是自动扫描并加载符合条件的组件或Bean定义,最终将这些bean定义加载到容器中。
@EnableAutoConfiguration:代表开启SpringBoot的自动装配,也是最重要的一个注解,接下来就会围绕它展开分析。
2.@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
//按类型排序不需要自动装配的类
Class<?>[] exclude() default {};
//按名称排除不需要自动装配的类
String[] excludeName() default {};
}
点开EnableAutoConfiguration又后发现了五个注解,上面四个与上一个注解依旧相同,来看看下面两个都是写啥东西。
@AutoConfigurationPackage:它负责保存标注相关注解的类的所在包路径。使用一个BasePackage类,保存这个路径。然后使用@Import注解将其注入到ioc容器中。这样,可以在容器中拿到该路径。
@Import(AutoConfigurationImportSelector.class):加载自动装配类,通过它@AutoConfiguration就可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前springboot创建并使用的IOC容器。
我们点进AutoConfigurationImportSelector.class继续往下看:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//判断自动装配开关是否打开
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//获取所有需要装配的bean
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
这个类的内容比较多,我会挑些主要流程来写。
这里看到有一个方法叫做selectImports,能看出它调用了一个名为autoConfigurationEntry的方法,这个方法主要负责加载自动配置类的。跳转到autoConfigurationEntry分析。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取配置候选
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
在这个方法中有一个叫做getCandidateConfigurations的方法,它的意思是获取候选配置,那么这些配置从哪里来,我们接着点下去。
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;
}
发现getCandidateConfigurations做了两件事,一个是非空断言,还有一个就是调用了loadFactoryNames方法,SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类。说道这里可能还是不了解,来看看它做了什么。
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);
}
}
loadFactoryNames返回了下面叫做的方法loadSpringFactories,其中的getResources方法指向获取的一个资源文件,进入这个常量:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
于是到这里一切都拨云见雾了,关于spring.factories,我会在拓展简略说明
3.总结
@EnableAutoConfiguration作用就是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。这些功能配置类要生效的话,会去classpath中找是否有该类的依赖类(也就是pom.xml必须有对应功能的jar包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。
所以功能类能生效需要的条件:
1.spring.factories里面有这个类的配置类(一个配置类可以创建多个围绕该功能的依赖类
2.并且pom.xml里面需要有对应的jar包
4.spring.factories拓展
位于spring-boot-autoconfigure.jar下的/META-INF/spring.factories文件,其内容就是预制了非常多常用的Configuration,正是它们让我们免于大量的手写配置。他们通常长这样:
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
那我们看看这里面都写了些什么,以jdbc数据源的自动配置为例:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
//这里是简略介绍,就不详细写了,了解用法即可
}
第四条注解,@EnableConfigurationProperties(DataSourceProperties.class),加载了一个为DataSourceProperties的类,这个应该就是我们需要的类,继续:
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
private ClassLoader classLoader;
private String name;
private boolean generateUniqueName = true;
private Class<? extends DataSource> type;
private String driverClassName;
private String url;
private String username;
private String password;
@ConfigurationProperties:直译过来就是配置属性的意思。
里面的参数prefix = "spring.datasource",这个用SpringBoot链接过数据库的可能就眼熟了。prefix前缀不就是写在yml或者factories里的参数前缀嘛,那下面的属性也显而易见了,在factories中设置它的用户名就是spring.datasource.username=*****;
其他配置同理。
end~