<!--redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
spring.redis.host=127.0.0.1 spring.redis.password= spring.redis.port=6379 spring.redis.jedis.pool.max-idle=200 spring.redis.jedis.pool.max-active=1024 spring.redis.jedis.pool.max-wait=1000
① selectImports方法:java
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);
//獲取自動裝配的入口 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
② getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata)方法:redis
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); /** * 獲取候選的配置類,主要是到classpath下面的\META-INF\spring.factories中, * 取key爲org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置類 */ List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); /**去除重複的配置類,若咱們本身寫的starter 可能存主重複的*/ configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); /**根據maven依賴導入的啓動器過濾出須要導入的配置類*/ configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
③ getCandidateConfigurations(annotationMetadata, attributes)方法:spring
/** * Return the auto-configuration class names that should be considered. By default * this method will load candidates using {@link SpringFactoriesLoader} with * {@link #getSpringFactoriesLoaderFactoryClass()}. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return a list of candidate configurations */ protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //去spring.factories中去查詢EnableAutoConfiguration類 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; }
④ SpringFactoriesLoader.loadFactoryNames方法:maven
/** * Load the fully qualified class names of factory implementations of the * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given * class loader. * @param factoryClass the interface or abstract class representing the factory * @param classLoader the ClassLoader to use for loading resources; can be * {@code null} to use the default * @throws IllegalArgumentException if an error occurs while loading factory names * @see #loadFactories */ public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); //去spring.factories 中去查詢EnableAutoConfiguration類 return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
⑤ loadSpringFactories(classLoader)方法:ide
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { /** * The location to look for factories. Can be present in multiple JAR files. * FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; */ 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 factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.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以下:spring-boot
導入了三個組件:RedisTemplate,StringRedisTemplate,JedisConnectionConfigurationui
① RedisTemplate組件(默認採用java序列化,因此通常要自定義該組件):this
@Bean //當沒有Spring容器中沒有redisTemplate的Bean的時候才加載 @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; }
自定義RedisTemplate組件,主要修改序列化方式,以下:url
@Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setDefaultSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); template.setConnectionFactory(redisConnectionFactory); return template; }
② StringRedisTemplate(默認採用java序列化,因此通常要自定義該組件):spa
@Bean //當沒有Spring容器中沒有StringRedisTemplate類型的Bean的時候才加載 @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }
③ JedisConnectionConfiguration組件:
/** * Redis connection configuration using Jedis. */ @Configuration @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class }) class JedisConnectionConfiguration extends RedisConnectionConfiguration { /** * redis配置 */ private final RedisProperties properties; private final ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers; JedisConnectionConfiguration(RedisProperties properties, ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration, ObjectProvider<RedisClusterConfiguration> clusterConfiguration, ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) { super(properties, sentinelConfiguration, clusterConfiguration); this.properties = properties; this.builderCustomizers = builderCustomizers; } /** * Jedis鏈接工廠 * @return * @throws UnknownHostException */ @Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException { return createJedisConnectionFactory(); } /** * Jedis鏈接工廠 * @return */ private JedisConnectionFactory createJedisConnectionFactory() { JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(); if (getSentinelConfig() != null) { return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration); } if (getClusterConfiguration() != null) { return new JedisConnectionFactory(getClusterConfiguration(), clientConfiguration); } return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration); } ......
redis對應的屬性配置類:
@ConfigurationProperties(prefix = "spring.redis") public class RedisProperties { /** * Database index used by the connection factory. */ private int database = 0; /** * Connection URL. Overrides host, port, and password. User is ignored. Example: * redis://user:password@example.com:6379 */ private String url; /** * Redis server host. */ private String host = "localhost"; /** * Login password of the redis server. */ private String password; /** * Redis server port. */ private int port = 6379; ...... }
本文以Spring Boot整合Redis爲例,把Spring Boot整合第三方組件的自動裝配原理進行了解析,對應其餘的第三方組件,好比整合Mybatis,套路是同樣的。