4.0.0
com.ljm
spring-cloud-alibaba-learn
1.0-SNAPSHOT
8
UTF-8
org.springframework.boot
spring-boot-starter-data-redis
2.2.2.RELEASE
org.springframework.boot
spring-boot-starter-web
2.2.2.RELEASE
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
true
${java.version}
${java.version}
${project.build.sourceEncoding}
spring.redis.host=localhost
spring.redis.port=6379
package com.springcloud.mystart;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author liaojiamin
* @Date:Created in 10:50 2022/3/7
*/
@RestController
public class HelloController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@GetMapping("helloRedis")
public String helloRedis(){
redisTemplate.opsForValue().set("helloRedis", "RedisTemplateAutoConfig");
return "helloRedis";
}
}
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author liaojiamin
* @Date:Created in 11:08 2022/3/7
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@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 {......}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
注解,作用在于用了该注解的类所在的包以及子包下所有的组件扫描到Spring IOC容器中
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {......}
/**
* @author liaojiamin
* @Date:Created in 11:49 2022/3/7
*/
public class FilterFirstObj {
}
/**
* @author liaojiamin
* @Date:Created in 11:49 2022/3/7
*/
public class FilterSecondObj {
}
/**
* @author liaojiamin
* @Date:Created in 11:49 2022/3/7
*/
public class GpImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{FilterFirstObj.class.getName(), FilterSecondObj.class.getName()};
}
}
/**
* @author liaojiamin
* @Date:Created in 11:52 2022/3/7
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({GpImportSelector.class})
public @interface EnableAutoImport {
}
/**
* @author liaojiamin
* @Date:Created in 11:08 2022/3/7
*/
@SpringBootApplication
@EnableAutoImport
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext ca = SpringApplication.run(Application.class, args);
System.out.println(ca.getBean(FilterFirstObj.class));
}
}
@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());
}
如上源码中有两个功能:
在AutoConfigurationImportSelector 类中并不直接执行对呀selectImport方法,其中有一个process方法,这个方法最中还是会调用getAutoConfigurationEntry方法获取自动装配的所有配置类,我们可以看如下源码
process方法
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
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 = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
通过Debug代码可以看每个步骤得到的返回值,简单分析代码的作用
以上步骤中,核心在于获取自动装配的配置类getCandidateConfigurations,其他只是在最筛选等其他步骤,我们看该方法实现
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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,他是Spring内部提供的一种类加载方式,类似java的SPI,他会扫描classpath目录下的META-INF/spring.factories文件,spring.factories文件中的数据以 key=value 的形式存储,也就是getCandidateConfigurations 方法的返回值
如下图是spring-boot-autoconfiguration对应的jar包中文件,我们可以在jar中找到对应的spring.factories
内容如下
......
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
......
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
@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:[email protected]: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;
/**
* Whether to enable SSL support.
*/
private boolean ssl;
/**
* Connection timeout.
*/
private Duration timeout;
/**
* Client name to be set on connections with CLIENT SETNAME.
*/
private String clientName;
private Sentinel sentinel;
private Cluster cluster;
......
}
spring.redis.host=127.0.0.1
spring.redis.port=6379
下一篇:SpringBoot中Bean按条件装配