SpringBoot自动加载的原理

2019-03-15  本文已影响0人  皮多堡
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

run方法点进去

public static ConfigurableApplicationContext run(Object source, String... args) {
        return run(new Object[] { source }, args);
    }

继续

    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
        return new SpringApplication(sources).run(args);
    }

可以看到run方法只是创建了SpringApplication对象,并执行run方法。继续跟进

public SpringApplication(Object... sources) {
        initialize(sources);
    }

initialize具体代码:

@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点进去:

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
    }

继续:

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点进去:

    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 的定义:

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

找到org.springframework.boot.autoconfigurespring.factories文件:

以Redis为例,我们可以看到spring.factories文件中有Redis的配置

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
@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;
        }
    ...

说明:

  1. @ConditionalOnClass激活一个配置,当类路径中存在这个类时才会配置该类
  2. @EnableConfigurationProperties 自动映射一个POJO从Spring Boot配置文件(默认是application.properties文件)的属性集。
  3. @ConditionalOnMissingBean 启用一个Bean定义,但必须是这个Bean之前未定义过才有效,还可以使用@ AutoConfigureBefore@AutoConfigureAfter注解来定义这些配置类的载入顺序

具体几个@Conditon*注解的含义

如若想关闭自动配置时我们就是把spring.factories文件不需要自动配置的类过滤掉
比如:

@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 。笔者将在后续文章中进行编码和解读

上一篇 下一篇

猜你喜欢

热点阅读