- 首先我们在程序入口的位置点进SpringApplication这个类的run方法
1 2 3 4 5 6 7
| @SpringBootApplication public class Application {
public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
|
run方法点进去
1 2 3
| public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); }
|
继续
1 2 3
| public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }
|
可以看到run方法只是创建了SpringApplication对象,并执行run方法。继续跟进
1 2 3
| public SpringApplication(Object... sources) { initialize(sources); }
|
initialize具体代码:
1 2 3 4 5 6 7 8 9 10 11
| @SuppressWarnings({ "unchecked", "rawtypes" }) private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
|
getSpringFactoriesInstances点进去:
1 2 3
| private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); }
|
继续:
1 2 3 4 5 6 7 8 9 10 11 12
| private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
|
loadFactoryNames
点进去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
|
可以看到常量 FACTORIES_RESOURCE_LOCATION
的定义:
1
| public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
|
找到org.springframework.boot.autoconfigure
的spring.factories
文件:

以Redis为例,我们可以看到spring.factories
文件中有Redis的配置
1
| org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
|
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 37 38 39 40 41 42 43 44 45 46
| @Configuration @ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class }) @EnableConfigurationProperties(RedisProperties.class) public class RedisAutoConfiguration {
/** * Redis connection configuration. */ @Configuration @ConditionalOnClass(GenericObjectPool.class) protected static class RedisConnectionConfiguration {
private final RedisProperties properties;
private final RedisSentinelConfiguration sentinelConfiguration;
private final RedisClusterConfiguration clusterConfiguration;
public RedisConnectionConfiguration(RedisProperties properties, ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration, ObjectProvider<RedisClusterConfiguration> clusterConfiguration) { this.properties = properties; this.sentinelConfiguration = sentinelConfiguration.getIfAvailable(); this.clusterConfiguration = clusterConfiguration.getIfAvailable(); }
@Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException { return applyProperties(createJedisConnectionFactory()); }
protected final JedisConnectionFactory applyProperties( JedisConnectionFactory factory) { configureConnection(factory); if (this.properties.isSsl()) { factory.setUseSsl(true); } factory.setDatabase(this.properties.getDatabase()); if (this.properties.getTimeout() > 0) { factory.setTimeout(this.properties.getTimeout()); } return factory; } ...
|
说明:
@ConditionalOnClass
激活一个配置,当类路径中存在这个类时才会配置该类
@EnableConfigurationProperties
自动映射一个POJO从Spring Boot配置文件(默认是application.properties文件)的属性集。
@ConditionalOnMissingBean
启用一个Bean定义,但必须是这个Bean之前未定义过才有效,还可以使用@ AutoConfigureBefore
、@AutoConfigureAfter
注解来定义这些配置类的载入顺序
具体几个@Conditon*注解的含义
@ConditionalOnBean
仅仅在当前上下文中存在某个对象时,才会实例化一个Bean
@ConditionalOnClass
某个class位于类路径上,才会实例化一个Bean),该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类
@ConditionalOnExpression
当表达式为true的时候,才会实例化一个Bean
@ConditionalOnMissingBean
仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean,该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean,可以给该注解传入参数例如@ConditionOnMissingBean(name = “example”),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行
@ConditionalOnMissingClass
某个class类路径上不存在的时候,才会实例化一个Bean
@ConditionalOnNotWebApplication
不是web应用时,才会执行
如若想关闭自动配置时我们就是把spring.factories文件不需要自动配置的类过滤掉
比如:
1 2 3 4 5 6 7
| @SpringBootApplication(exclude = {RedisAutoConfiguration.class}) public class Application {
public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
|
。
SpringBoot 的 自动配置得益于 SpringFramework 强大的支撑,框架早已有很多工具和注解可以自动装配 Bean 。SpringBoot 通过 一个封装,将市面上通用的组件直接写好了配置类。当我们程序去依赖了这些组件的 jar 包后,启动 SpringBoot应用,于是自动加载开始了。
我们也可以定义自己的自动装配组件,依赖之后,Spring直接可以加载我们定义的 starter 。笔者将在后续文章中进行编码和解读