我们需要从Spring Boot项目的启动类开始跟踪,在启动类上我们一般会加入SpringBootApplication注解,
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.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 {
......
}
重点介绍如下三个注解:(敲黑板)
@SpringBootConfiguration :作用就相当于**@Configuration**注解,被注解的类将成为一个bean配置类
@EnableAutoConfiguration :这个注解很重要,借助@import的支持,收集和注册依赖包中相关的bean定义
@ComponentScan :作用就是自动扫描并加载符合条件的组件,最终将这些bean加载到spring容器中
②继续跟踪**@EnableAutoConfiguration**注解源码:
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
@AutoConfigurationPackage
@import(EnableAutoConfigurationimportSelector.class) //EnableAutoConfigurationimportSelector批量导入bean
public @interface EnableAutoConfiguration { // ... }
@EnableAutoConfiguration注解引入了@import这个注解。
import:导入需要自动配置的组件,此处为EnableAutoConfigurationimportSelector这个类(这块需要了解@import的相关知识)
重写了selectimports方法,将返回的String[]导入到spring容器中
④EnableAutoConfigurationimportSelector类源码如下:
@Override
public String[] selectimports(Annotationmetadata annotationmetadata) {
if (!isEnabled(annotationmetadata)) {
return NO_importS;
}
AutoConfigurationmetadata autoConfigurationmetadata = AutoConfigurationmetadataLoader
.loadmetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationmetadata);
List configurations = getCandidateConfigurations(annotationmetadata,attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationmetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationmetadata);
fireAutoConfigurationimportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
private void fireAutoConfigurationimportEvents(List configurations,
Set exclusions) {
List listeners = getAutoConfigurationimportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationimportEvent event = new AutoConfigurationimportEvent(this,
configurations, exclusions);
for (AutoConfigurationimportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationimportEvent(event);
}
}
}
继续看**getCandidateConfigurations(annotationmetadata,attributes)**方法
protected ListgetCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) { // 加载工程名称 List 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; }
Spring工厂加载器加载工厂名称
扫描具有meta-INF/spring.factories文件的jar包
public static ListloadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map > loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration 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); } }
spring.factories文件是Key=Value形式,多个Value时使用,隔开,该文件中定义了关于初始化,监听器等信息,而真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration,如下所示:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration= org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration, org.springframework.boot.autoconfigure.aop.AopAutoConfiguration, org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration, //省略
上面的EnableAutoConfiguration配置了多个类,这些都是Spring Boot中的自动配置相关类;在启动过程中会解析对应类配置信息。每个Configuation类都定义了相关bean的实例化配置。都说明了哪些bean可以被自动配置,什么条件下可以自动配置,并把这些bean实例化出来,下面的初始化数据源的部分源码:
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@import({ Registrar.class, DataSourcePoolmetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {
private static final Log logger = LogFactory
.getLog(DataSourceAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean
public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties,
ApplicationContext applicationContext) {
return new DataSourceInitializer(properties, applicationContext);
}
//略
}
会用到很多常用的注解
三、相关注解@Configuration:这个配置就不用多做解释了,我们一直在使用 @EnableConfigurationProperties:这是一个开启使用配置参数的注解,value值就是我们配置实体参数映射的ClassType,将配置实体作为配置来源。 以下为SpringBoot内置条件注解: @ConditionalOnBean:当SpringIoc容器内存在指定Bean的条件 @ConditionalOnClass:当SpringIoc容器内存在指定Class的条件 @ConditionalOnexpression:基于SpEL表达式作为判断条件 @ConditionalOnJava:基于JVM版本作为判断条件 @ConditionalOnJndi:在JNDI存在时查找指定的位置 @ConditionalOnMissingBean:当SpringIoc容器内不存在指定Bean的条件 @ConditionalOnMissingClass:当SpringIoc容器内不存在指定Class的条件 @ConditionalOnNotWebApplication:当前项目不是Web项目的条件 @ConditionalOnProperty:指定的属性是否有指定的值 @ConditionalOnResource:类路径是否有指定的值 @ConditionalOnSingleCandidate:当指定Bean在SpringIoc容器内只有一个,或者虽然有多个但是指定首选的Bean @ConditionalOnWebApplication:当前项目是Web项目的条件



