redis-start自动装配

redis-start自动装配学习笔记:

废话不多,先上代码

获取自定义redis数据源配置文件配置
@ConfigurationProperties(prefix = "test.redis")
@Setter
@Getter
public class StarterRedisProperties {
    /**
     * 扩展RedisProperties构建多数据源映射表
     */
    private Map<String, RedisProperties> redis;
}
配置文件:
test:
  redis:
    basic:
      database: 0
      password: 
      shutdown-timeout: 1000ms
      socket-timeout: 2000ms
      sentinel:
        master: 
        nodes: 
      lettuce:
        pool:
          max-active: 100
          min-idle: 20
          max-idle: 100
          max-wait: 2000ms

配置redis客户端链接
@Slf4j
public class TestRedisRegistrar implements EnvironmentAware, ImportBeanDefinitionRegistrar {
    private Map<String, RedisConnectionFactory> registerBean = new ConcurrentHashMap<>(24);
    private Environment environment;
    private Binder binder;
    private static final String PREFIX = "test.redis";
    private static final String PREFIX_WITH_COMMA = "test.redis.";

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
        binder = Binder.get(environment);
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        log.info("config redis start.");
        Map<String, Map<String, Object>> redisConfigs = null;
        try {
            // 获取yaml中的配置文件
            redisConfigs = binder.bind(PREFIX, Map.class).get();
        } catch (NoSuchElementException e) {
            log.error("Please config the redis prefix config.", e);
            throw e;
        }
        boolean havePrimary = true;
        for (String redisSourceName : redisConfigs.keySet()) {
            //配置基础连接信息
            RedisConfiguration redisConfiguration = getRedisConfiguration(redisSourceName);

            //配置连接池信息
            GenericObjectPoolConfig poolConfig = getRedisPoolConfig(redisSourceName);

            //配置RedisConnectionFactory
            RedisConnectionFactory redisConnectionFactory = getRedisConnectionFactory(redisSourceName, redisConfiguration, poolConfig);
            //构造RedisConnectionFactory Supplier
            Supplier<RedisConnectionFactory> supplier = () -> {
                return redisConnectionFactory;
            };

            //构建RedisConnectionFactoryBean
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(getRedisConnectionFactoryClass(redisConnectionFactory), supplier);
            AbstractBeanDefinition beanDefinition = builder.getRawBeanDefinition();
            beanDefinition.setPrimary(havePrimary);
            registry.registerBeanDefinition(getRedisConnectionFactoryBeanName(redisSourceName), beanDefinition);

            //构建StringRedisTemplateBean
            GenericBeanDefinition stringRedisTemplate = new GenericBeanDefinition();
            stringRedisTemplate.setBeanClass(StringRedisTemplate.class);
            ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
            constructorArgumentValues.addIndexedArgumentValue(0, redisConnectionFactory);
            stringRedisTemplate.setConstructorArgumentValues(constructorArgumentValues);
            stringRedisTemplate.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
            registry.registerBeanDefinition(getStringRedisTemplateBeanName(redisSourceName), stringRedisTemplate);

            //构建RedisTemplateBean
            GenericBeanDefinition redisTemplate = new GenericBeanDefinition();
            redisTemplate.setBeanClass(RedisTemplate.class);
            redisTemplate.getPropertyValues().add("connectionFactory", redisConnectionFactory);
            redisTemplate.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
            registry.registerBeanDefinition(getRedisTemplateBeanName(redisSourceName), redisTemplate);

            //创建RedisRepositoryBean
            GenericBeanDefinition redisRepository = new GenericBeanDefinition();
            redisRepository.setBeanClass(RedisRepository.class);
            ConstructorArgumentValues cav = new ConstructorArgumentValues();
            cav.addIndexedArgumentValue(0, stringRedisTemplate);
            redisRepository.setConstructorArgumentValues(cav);
            redisRepository.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
            registry.registerBeanDefinition(getRedisRepositoryBeanName(redisSourceName), redisRepository);

            if (havePrimary) {
                havePrimary = false;
            }

            log.info("config success for key: {}", redisSourceName);

        }
        log.info("config redis end.");
    }


    /**
     * 获取RedisConnetionFactory Class
     *
     * @param redisConnectionFactory
     * @return
     */
    private Class getRedisConnectionFactoryClass(RedisConnectionFactory redisConnectionFactory) {
        Class clazz = null;
        if (redisConnectionFactory instanceof LettuceConnectionFactory) {
            clazz = LettuceConnectionFactory.class;
        }
        if (redisConnectionFactory instanceof JedisConnectionFactory) {
            clazz = JedisConnectionFactory.class;
        }
        return clazz;
    }

    /**
     * 配置 RedisConnectionFactory: 配置连接工厂。
     * SpringData redis 默认提供JedisConnctionFactory 和 LettuceConnectionFactory 2种实现,
     * 如果要支持RedissonConnectionFactory, 请参见redisson官方starter。
     *
     * @param redisSourceName
     * @param redisConfiguration
     * @param poolConfig
     * @return
     */
    private RedisConnectionFactory getRedisConnectionFactory(String redisSourceName, RedisConfiguration redisConfiguration, GenericObjectPoolConfig poolConfig) {
        String connectionFactoryBeanName = getRedisConnectionFactoryBeanName(redisSourceName);
        RedisConnectionFactory redisConnectionFactory = registerBean.get(connectionFactoryBeanName);
        if (redisConnectionFactory != null) {
            return redisConnectionFactory;
        }
        Duration socketTimeOut = null;
        try {
            socketTimeOut = binder.bind(PREFIX_WITH_COMMA + redisSourceName + ".socket-timeout", Duration.class).get();
        } catch (NoSuchElementException e) {
            throw new IllegalArgumentException("missing socket-timeout config.");
        }
        if (isClientJedis(redisSourceName)) {
            redisConnectionFactory = createJedisConnectionFactory(redisSourceName, socketTimeOut, redisConfiguration, poolConfig);
        }
        if (isClientLettuce(redisSourceName)) {
            redisConnectionFactory = createLettuceConnectionFactory(redisSourceName, socketTimeOut, redisConfiguration, poolConfig);
        }
        return redisConnectionFactory;
    }

    /**
     * 创建LettuceConnectionFactory
     *
     * @param redisSourceName
     * @param socketTimeOut
     * @param redisConfiguration
     * @param poolConfig
     * @return
     */
    private RedisConnectionFactory createLettuceConnectionFactory(String redisSourceName, Duration socketTimeOut, RedisConfiguration redisConfiguration, GenericObjectPoolConfig poolConfig) {
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
        Duration shutDownTimeOut = binder.bind(PREFIX_WITH_COMMA + redisSourceName + ".shutdown-timeout", Duration.class).get();
        if (shutDownTimeOut != null) {
            builder.shutdownTimeout(shutDownTimeOut);
        }
        if (socketTimeOut != null) {
            builder.commandTimeout(socketTimeOut);
        }
        LettuceClientConfiguration lettuceClientConfiguration = builder.poolConfig(poolConfig).build();
        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisConfiguration, lettuceClientConfiguration);
        registerBean.put(getRedisConnectionFactoryBeanName(redisSourceName), lettuceConnectionFactory);
        return lettuceConnectionFactory;
    }

    /**
     * 创建JedisConnectionFactory
     *
     * @param redisSourceName
     * @param socketTimeOut
     * @param redisConfiguration
     * @param poolConfig
     * @return
     */
    private RedisConnectionFactory createJedisConnectionFactory(String redisSourceName, Duration socketTimeOut, RedisConfiguration redisConfiguration, GenericObjectPoolConfig poolConfig) {
        JedisClientConfiguration.DefaultJedisClientConfigurationBuilder builder = (JedisClientConfiguration.DefaultJedisClientConfigurationBuilder) JedisClientConfiguration.builder();
        if (socketTimeOut != null) {
            builder.connectTimeout(socketTimeOut);
            builder.readTimeout(socketTimeOut);
        }
        builder.poolConfig(poolConfig);
        JedisClientConfiguration jedisClientConfiguration = builder.build();
        JedisConnectionFactory jedisConnectionFactory = null;
        if (redisConfiguration instanceof RedisStandaloneConfiguration) {
            jedisConnectionFactory = new JedisConnectionFactory((RedisStandaloneConfiguration) redisConfiguration, jedisClientConfiguration);
        }
        if (redisConfiguration instanceof RedisSentinelConfiguration) {
            jedisConnectionFactory = new JedisConnectionFactory((RedisSentinelConfiguration) redisConfiguration, jedisClientConfiguration);
        }
        if (redisConfiguration instanceof RedisClusterConfiguration) {
            jedisConnectionFactory = new JedisConnectionFactory((RedisClusterConfiguration) redisConfiguration, jedisClientConfiguration);
        }
        registerBean.put(getRedisConnectionFactoryBeanName(redisSourceName), jedisConnectionFactory);
        return jedisConnectionFactory;
    }

    /**
     * 配置redis连接池信息
     *
     * @param redisSourceName
     * @return
     */
    private GenericObjectPoolConfig getRedisPoolConfig(String redisSourceName) {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        try {
            //如果需要支持多客户端类型,请按优先级加载。比如优先Lettuce > Jedis > Redisson
            RedisProperties.Pool pool = null;
            if (isClientJedis(redisSourceName)) {
                pool = binder.bind(PREFIX_WITH_COMMA + redisSourceName + ".jedis.pool", RedisProperties.Pool.class).get();
            }
            if (isClientLettuce(redisSourceName)) {
                pool = binder.bind(PREFIX_WITH_COMMA + redisSourceName + ".lettuce.pool", RedisProperties.Pool.class).get();
            }
            poolConfig.setMaxIdle(pool.getMaxIdle());
            poolConfig.setMaxTotal(pool.getMaxActive());
            poolConfig.setMinIdle(pool.getMinIdle());
            if (pool.getMaxWait() != null) {
                poolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
            }
            return poolConfig;
        } catch (Exception e) {
            log.error("Please config the redis pool info.", e);
            throw new IllegalArgumentException("pool config error。");
        }
    }

    /**
     * 按优先级(单点-->哨兵-->集群)获取RedisConfiguration配置类型
     *
     * @param redisSourceName: redis数据源名字
     * @return 1. RedisStandaloneConfiguration 单例
     * 2. RedisSentinelConfiguration 哨兵
     * 3. RedisClusterConfiguration 集群
     */
    private RedisConfiguration getRedisConfiguration(String redisSourceName) {
        Map<String, String> map = binder.bind(PREFIX_WITH_COMMA + redisSourceName, Map.class).get();
        String host = map.get("host");
        String password = map.get("password");
        String configDataBase = String.valueOf(map.get("database"));
        if (StringUtils.isBlank(configDataBase) || "null".equals(configDataBase)) {
            configDataBase = "0"; //默认0库
        }
        Integer database = Integer.parseInt(configDataBase);
        if (StringUtils.isNotBlank(host)) {
            //配置单点连接基础信息
            RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
            redisStandaloneConfiguration.setHostName(map.get("host"));
            redisStandaloneConfiguration.setPort(Integer.parseInt(String.valueOf(map.get("port"))));
            redisStandaloneConfiguration.setDatabase(database);
            if (StringUtils.isNotBlank(password)) {
                redisStandaloneConfiguration.setPassword(password);
            }
            return redisStandaloneConfiguration;
        }
        RedisProperties.Sentinel sentinel = null;
        try {
            sentinel = binder.bind(PREFIX_WITH_COMMA + redisSourceName + ".sentinel", RedisProperties.Sentinel.class).get();
        } catch (NoSuchElementException e) {
            //ignore
        }
        if (sentinel != null) {
            //配置哨兵连接基础信息
            RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
            redisSentinelConfiguration.master(sentinel.getMaster());
            redisSentinelConfiguration.setSentinels(createSentinels(sentinel));
            if (StringUtils.isNotBlank(password)) {
                redisSentinelConfiguration.setPassword(RedisPassword.of(password));
            }
            redisSentinelConfiguration.setDatabase(database);
            return redisSentinelConfiguration;
        }
        RedisProperties.Cluster cluster = null;
        try {
            cluster = binder.bind(PREFIX_WITH_COMMA + redisSourceName + ".cluster", RedisProperties.Cluster.class).get();
        } catch (NoSuchElementException e) {
            //ignore
        }
        if (cluster != null) {
            //配置集群连接基础信息
            RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(cluster.getNodes());
            if (cluster.getMaxRedirects() != null) {
                redisClusterConfiguration.setMaxRedirects(cluster.getMaxRedirects());
            }
            if (StringUtils.isNotBlank(password)) {
                redisClusterConfiguration.setPassword(RedisPassword.of(password));
            }
            return redisClusterConfiguration;
        }
        throw new IllegalArgumentException("No match configuration for " + redisSourceName);
    }

    /**
     * 配置Sentinel节点列表
     *
     * @param sentinel
     * @return
     * @see org.springframework.boot.autoconfigure.data.redis.RedisConnectionConfiguration#createSentinels
     */
    private List<RedisNode> createSentinels(RedisProperties.Sentinel sentinel) {
        List<RedisNode> nodes = new ArrayList<>();
        for (String node : sentinel.getNodes()) {
            try {
                String[] parts = org.springframework.util.StringUtils.split(node, ":");
                Assert.state(parts.length == 2, "Must be defined as 'host:port'");
                nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1])));
            } catch (RuntimeException ex) {
                throw new IllegalStateException("Invalid redis sentinel " + "property '" + node + "'", ex);
            }
        }
        return nodes;
    }

    /**
     * 此数据源是用的Jedis客户端吗?
     *
     * @param redisSourceName
     * @return
     */
    private boolean isClientJedis(String redisSourceName) {
        try {
            binder.bind(PREFIX_WITH_COMMA + redisSourceName + ".jedis", Map.class).get();
        } catch (NoSuchElementException e) {
            return false;
        }
        return true;
    }

    /**
     * 此数据源是用的Lettuce客户端吗?
     *
     * @param redisSourceName
     * @return
     */
    private boolean isClientLettuce(String redisSourceName) {
        try {
            binder.bind(PREFIX_WITH_COMMA + redisSourceName + ".lettuce", Map.class).get();
        } catch (NoSuchElementException e) {
            return false;
        }
        return true;
    }

    /**
     * 获取RedisConnectionFactoryBeanName
     *
     * @param redisSourceName
     * @return
     */
    private String getRedisConnectionFactoryBeanName(String redisSourceName) {
        return redisSourceName + "-redis-connection-factory";
    }

    /**
     * 获取StringRedisTemplateBeanName
     *
     * @param redisSourceName
     * @return
     */
    private String getStringRedisTemplateBeanName(String redisSourceName) {
        return redisSourceName + "-string-redis-template";
    }

    /**
     * 获取RedisTemplateBeanName
     *
     * @param redisSourceName
     * @return
     */
    private String getRedisTemplateBeanName(String redisSourceName) {
        return redisSourceName + "-redis-template";
    }

    /**
     * 获取RedisRepositoryBeanName
     *
     * @param redisSourceName
     * @return
     */
    private String getRedisRepositoryBeanName(String redisSourceName) {
        return redisSourceName + "-repository";
    }
}

配置META-INF/spring.factroies(重点)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xxx.xxx.redis.TestRedisAutoConfiguration
配置spring-configuration-metadata.json(可选)
{
  "groups": [
    {
      "name": "test.redis",
      "type": "com.xxx.xxx.redis.TestRedisProperties",
      "sourceType": "com.xxx.xxx.redis.TestRedisProperties"
    }
  ],
  "properties": [
    {
      "name": "test.redis",
      "type": "java.util.Map<java.lang.String,org.springframework.boot.autoconfigure.data.redis.RedisProperties>",
      "sourceType": "com.xxx.xxx.redis.TestRedisProperties"
    }
  ],
  "hints": []
}
自动装配“value类”
@Configuration
@EnableConfigurationProperties(TestRedisProperties.class)
@AutoConfigureBefore({RedisAutoConfiguration.class})
@Import(TestRedisRegistrar.class)
public class TestRedisAutoConfiguration {
}

看完所有代码之后,简单梳理下,首先就会有以下问题:

1、spring.factories文件是什么?
2、自动装配"value类"是什么?
3、自动装配如何实现?
4、spring-configuration-metadata.json有什么用,为什么可选?

下面一一解答这几个问题

1、spring.factories文件是什么?

在springboot启动的时候,会自动扫描所有的spring.factories文件,spring.factories文件的内容信息以kv存储,其中key和value对应的值就是简单的“=”获取,是springboot自动装配的核心配置

2、自动装配“value类是什么”?

是自己起的名字,相当于就是在springboot启动时,自动装配时用来加载(装配)的对应的value

3、自动装配如何实现?

上面说到spring.factories在springboot启动时加载,是springboot自动装配的核心配置,那么她为什么核心,怎么会自动扫描加载呢?我们一步一步看

1、springboot如何实现自动装配?

首先,说到springboot自动装配,离不开的就是springboot的启动,说到springboot的启动,那就不得不提在启动类上的关键注解__@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 {
	......
}

各种各样的注解花里胡哨,但是本次的重点不是他们,直击核心,看到其中有一个注解是__@EnableAutoConfiguration__,翻译过来就是:启用自动装配,这个注解一听就是此次自动装配的核心。

2、@EnableAutoConfiguration做了什么?

先不着急,进入到__@EnableAutoConfiguration__注解中,看下里面有什么

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	......
}

又是这一大盘子注解…但是,不能被其他注解迷惑(也没什么迷惑的,有些一看就知道没关系),所谓__自动装配__少不了的就是__Auto Configuration__,那一下子需要把关注点放到__@Import(AutoConfigurationImportSelector.class)__上,那么这个__AutoConfigurationImportSelector.class__又是何方神圣,做了什么呢?废话不说,点进去看了才知道

3、AutoConfigurationImportSelector.class是何方神圣?
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	......
}

好家伙,我直接就是好家伙,上来就可以看到__AutoConfigurationImportSelector__实现这么多接口,既然来都来了,那就先简单看下每个实现的接口是干什么的吧(说简单看,就简单看)

DeferredImportSelector:

DeferredImportSelector__继承了__ImportSelector__类, ImportSelector__中的__selectImports__方法返回一组__bean@EnableAutoConfiguration__注解借助__@Import__注解将这组__bean__注入到__spring__容器中,springboot正式通过这种机制来完成__bean__的注入的。

BeanClassLoaderAware:

分别获取Bean的类装载器和bean工厂

ResourceLoaderAware:

获取资源加载器,可以获得外部资源文件

BeanFactoryAware:

获得当前__bean Factory__,从而调用容器的服务

EnvironmentAware:

凡注册到Spring容器内的bean,实现了__EnvironmentAware__接口__重写setEnvironment__方法后,在工程启动时可以获得__application.properties__的配置文件配置的属性值。

Ordered:

处理相同接口实现类的优先级问题

简单的甚至有点笼统,但是都不重要,小学都知道,选择题不会就选长的,那我们就先看那个最长的吧(不重要我写那么多干嘛~),“注入”和“bean”,这一切都像极了__Spring IOC__,

​ “这样写,会让他看起很酷”

​ ——沃兹基.索德

4、自动装配的实现

刚才说到__@EnableAutoConfiguration__会借助__DeferredImportSelector__搞些小动作,那么做了什么?得到了什么呢?

既然实现了接口,那必须要实现他的方法(是必须吗?Springboot可是传说中的jdk8哦~),实现了__selectImports__方法如下:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {   
    if (!isEnabled(annotationMetadata)) {  // 是否允许(开启)自动装配
        return NO_IMPORTS;   // 否,return
    }   
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); // 方法名直译:获取自动装配条目
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

看翻译也知道,很关键的就在__getAutoConfigurationEntry(annotationMetadata)这个方法(毕竟还是离不开__Auto Configuration),点进去看一眼他在偷偷的帮我们干什么!

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(annotationMetadata, attributes)__就完事了

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {   
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),         getBeanClassLoader()); // 关键:加载所有的factory
    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;
}

如何加载所有的factory,接下来就是见证奇迹的时候!

// 这个东西,似曾相识?
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

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);
		}
	}

偶买噶!盘他!这里的逻辑就是读取所有的 META-INF/spring.factories 下的内容,获取需要自动装配的类,来进行自动装配,因此开头提到__spring.factories__是自动装配的关键就是在这,他帮助spring-boot注册其他的bean,且用来记录项目包外需要注册的bean类名。

以上就是自动装配的整体流程实现!

作者码字…(什么?还有个知识点?我看看~)

4、spring-configuration-metadata.json有什么用,为什么可选?

这个文件的主要作用就是,在你的配置你的配置文件的时候,无论是__yaml__还是__properties __,可以在IDE中,给出对应的配置,例如:spring配置指定端口的时候,在idea中就会给出对应的提示key

在这里插入图片描述

这是spring中对应的__spring-configuration-metadata.json__的对应配置

在这里插入图片描述
完结撒花
以上都是本人自己的一些理解和学习demo,如果有问题和不对的地方请私信或者留言指正
关于此starter如果在使用时有问题的话也可以留言一起学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值