@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 {
我们发现主要由3个注解组成 @SpringBootConfiguration
,@ComponentScan
,@EnableAutoConfiguration
@SpringBootConfiguration
源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
发现其实就是一个@Configuration
注解,作用就是将配置了这个注解的类加载到ioc容器。主要是为了替换掉xml配置文件。
@ComponentScan
这个注解是大家接触得最多的了,相当于xml配置文件中的
。它的主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到spring的Ioc容器中。
标识需要装配的类的形式主要是:@Component、@Repository、@Service、@Controller
这类的注解标识的类。ComponentScan默认会扫描当前package下的的所有加了相关注解标识的类到IoC容器中
@EnableAutoConfiguration
仍然是在spring3.1版本中,提供了一系列的@Enable
开头的注解,Enable主机应该是在JavaConfig框架上更进一步的完善,是的用户在使用spring相关的框架是,避免配置大量的代码从而降低使用的难度比如常见的一些Enable注解:@EnableWebMvc
,(这个注解引入了MVC框架在Spring 应用中需要用到的所有bean);比如@EnableScheduling
,开启计划任务的支持
找到EnableAutoConfiguration,我们可以看到每一个涉及到Enable开头的注解,都会带有一个@Import的注解。
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@Import
这个注解相当于xml 格式下的
import就是把多个配置合并在一个配置中。在JavaConfig中所表达的意义是一样的。
我们用Import注解将bean进行注入
1基于普通bean或者带有@Configuration的bean进行注入
比如这个时候@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector 就会被ico管理。
2.实现ImportSelector接口进行动态注入
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
返回值是一个字符串的数组,这个数组会将里面的字符串加载到ioc容器当中,这个字符串必须就是类的的全限定名
3.实现ImportBeanDefinitionRegistrar接口进行动态注入
这个上面的类似,感兴趣的可以去了解下。
我们回来过来继续看AutoConfigurationImportSelector 中有个selectImports 方法。
我们不是将那些需要自动装备的类的名称找到并返回不就好了吗?
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
这个方法返回了configurations,这个configurations 我们猜想就是那些需要自动装备的类,我们跟下configurations 是怎么来的
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
点进去
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的类加载到的,继续点进去,最终发下了一行这样带代码
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
它会去"META-INF/spring.factories"
查找,我们去找找看,我们发现在 autoconfigere 包下确实有这个文件。
我们发现了个大秘密,这不就是那些自动装配的配置类吗?
为什么我们导入这个redis 的包就可以直接使用 RedisTemplate来操作redis(还是要在配置文件中配置端口的。)?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
我们发现在spring.factories 的文件中有一个类
这个类会被spring 管理,我们点进去
@Configuration
@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(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
为什么可以使用RedisTemplate?因为这个类帮我们创建了RedisTemplate
。
tip:
@ConditionalOnClass(RedisOperations.class)
这个注解的意思是RedisOperations.class这个类存在时,会加载这个类。而RedisOperations是spring-boot-starter-data-redis包中的一个类,所以需要导包才能使用。