注解源码:
@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
源码
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }发现其实就是一个@Configuration注解,作用就是将配置了这个注解的类加载到ioc容器。主要是为了替换掉xml配置文件。
这个注解是大家接触得最多的了,相当于xml配置文件中的<context:component-scan>。它的主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到spring的Ioc容器中。
标识需要装配的类的形式主要是:@Component、@Repository、@Service、@Controller这类的注解标识的类。ComponentScan默认会扫描当前package下的的所有加了相关注解标识的类到IoC容器中
仍然是在spring3.1版本中,提供了一系列的@Enable开头的注解,Enable主机应该是在JavaConfig框架上更进一步的完善,是的用户在使用spring相关的框架是,避免配置大量的代码从而降低使用的难度比如常见的一些Enable注解:@EnableWebMvc,(这个注解引入了MVC框架在Spring 应用中需要用到的所有bean);比如@EnableScheduling,开启计划任务的支持
找到EnableAutoConfiguration,我们可以看到每一个涉及到Enable开头的注解,都会带有一个@Import的注解。
@Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {这个注解相当于xml 格式下的<import resource/> 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包中的一个类,所以需要导包才能使用。