@SpringBootApplication注解-从源码分析
1. 启动类的写法
1 2 3 4 5 6 @SpringBootApplication public class TestApplication { public static void main (String[] args) { SpringApplication.run(TestApplication.class, args); } }
启动类中包含两个重要部分:
@SpringBootApplication注解
main方法中SpringApplication.run()方法
2. @SpringBootApplication的注解
进入SpringBootApplication注解,其头部注解如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @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 { }
我们一项一项来讲。
2.1 @Target
1 2 3 4 5 6 7 8 9 10 11 12 @Documented @Retention (RetentionPolicy.RUNTIME)@Target (ElementType.ANNOTATION_TYPE)public @interface Target { ElementType[] value(); }
该接口用于指定注解可以用于什么地方,并且用了一个枚举类数组来表示。四个元注解之一。
关于为什么注解的属性后面有括号
注解是java以接口为基础实现的。不同的是把方法在内部作为属性处理了。所以属性以方法的方式来进行定义,必须有括号。
再来看一下这个枚举类,到底是哪些地方可以用注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE }
2.2 @Retention
1 2 3 4 5 6 7 8 9 10 @Documented @Retention (RetentionPolicy.RUNTIME)@Target (ElementType.ANNOTATION_TYPE)public @interface Retention { RetentionPolicy value () ; }
Indicates how long annotations with the annotated type are to be retained. 根据介绍来看,Retention是用来表示注解的生命周期的,同样的也是通过一个枚举类来指定。四个元注解之一。
再来看一下RetentionPolicy枚举类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
2.3 @Documented
1 2 3 4 5 @Documented @Retention (RetentionPolicy.RUNTIME)@Target (ElementType.ANNOTATION_TYPE)public @interface Documented {}
这个注解没有方法。该注解是为了指明被注解的类型可以被javadoc文本话。四个元注解之一。
2.4 @Inherited
1 2 3 4 5 @Documented @Retention (RetentionPolicy.RUNTIME)@Target (ElementType.ANNOTATION_TYPE)public @interface Inherited {}
Indicates that an annotation type is automatically inherited.
该注解表示父类的注解可以被子类继承。说明被@SpringBootApplication注解的类的子类也与它有相同的注解。四个元注解之一。
2.5 @SpringBootConfiguration
1 2 3 4 5 6 7 8 9 10 @Target ({ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Documented @Configuration public @interface SpringBootConfiguration { @AliasFor ( annotation = Configuration.class ) boolean proxyBeanMethods () default true ; }
@AliasFor
在这里表示继承注解。可以发现SpringBootConfiguration继承了@Configuration
,在@AliasFor
中并没有指明value,因此默认为Configuration
注解中同名属性。
再来看@Configuration
是怎么样的。
1 2 3 4 5 6 7 8 9 10 11 12 @Target ({ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Documented @Component public @interface Configuration { @AliasFor ( annotation = Component.class ) String value () default "" ; boolean proxyBeanMethods () default true ; }
还是有一个@AliasFor
注解,和一个会被SpringBootConfiguration继承的proxyBeanMethods
属性。
先来讲一下@Configuration
的用处。Indicates that a class declares one or more Beans methods and may be processed by Spring container to generate bean definitions and service requests for those beans at runtime.
意思就是说当@Configuration
被注解在一个类上时,表明该类的内部有一个或多个生成Bean的方法,它们将被Spring容器用于生产Bean和响应运行时的服务请求。
而proxyBeanMethods()
方法,Specify whether {@code @Bean} methods should get proxied in order to enforce bean lifecycle behavior。意思是指定@Bean标注的方法是否使用代理模式,直接从IOC容器中获取对象。如果不需要将Bean交给spring管理,把这项设为false,那么每次获取的都是一个新的对象。
在@Configuration
中还包含了一个继承下来的方法,涉及到了@Component
1 2 3 4 5 6 7 @Target ({ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Documented @Indexed public @interface Component { String value () default "" ; }
Indicates that an annotated class is a “component”.Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.
被注解为Component的类会被扫描到。其中的value方法是为component指定一个名字
2.6 @EnableAutoConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Target (ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)@Documented @Inherited @AutoConfigurationPackage @Import (AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
Enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. 该注解是用来自动配置的,SpringBoot会通过上文环境以及你在classpath中存放的包和你定义的bean来猜测需要哪些配置。
1 2 3 4 5 6 @Target (ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)@Documented @Inherited @Import (AutoConfigurationPackages.Registrar.class)public @interface AutoConfigurationPackage {
@Import(AutoConfigurationPackages.Registrar.class)
引入了Registrar类,这个类是AutoConfigurationPackages内部的静态类,只有用到的时候才会被初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 static class Registrar implements ImportBeanDefinitionRegistrar , DeterminableImports { @Override public void registerBeanDefinitions (AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0 ])); } @Override public Set<Object> determineImports (AnnotationMetadata metadata) { return Collections.singleton(new PackageImports(metadata)); } }
通过register(registry,new PackageImports(metadata).getPackageNames().toArray(new String[0]));}
将主程序类包下所有的类都在spring中注册了。注意只有当前包下,不包括子包
使用registerBeanDefinition
方法注册了Bean
这里我们重点关注一下@Import(AutoConfigurationImportSelector.class)
,当import的是importSelector类时,会把该类selectImports()方法返回的Class都定义为bean
1 2 3 4 5 6 7 8 9 10 11 12 public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware , ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
通过AnnotationMetadata对象,该方法可以获取到标注@Import
类的各种信息。通过getAutoConfigurationEntry(annotationMetadata)
方法就可以获取到需要自动配置的类了。
整理一下
@EnableAutoConfiguration
注解首先通过@AutoConfigurationPackage
将有该注解的类的包下的全部类都注入spring,然后通过@Import(AutoConfigurationImportSelector.class)
判断需要符合条件的配置类,将它们也都注入到Spring中。
2.7 @ComponentScan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 @Retention (RetentionPolicy.RUNTIME)@Target (ElementType.TYPE)@Documented @Repeatable (ComponentScans.class)public @interface ComponentScan { @AliasFor ("basePackages" ) String[] value() default {}; @AliasFor ("value" ) String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; ScopedProxyMode scopedProxy () default ScopedProxyMode.DEFAULT ; String resourcePattern () default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN ; boolean useDefaultFilters () default true ; Filter[] includeFilters() default {}; Filter[] excludeFilters() default {}; boolean lazyInit () default false ; @Retention (RetentionPolicy.RUNTIME) @Target ({}) @interface Filter { FilterType type () default FilterType.ANNOTATION ; @AliasFor ("classes" ) Class<?>[] value() default {}; @AliasFor ("value" ) Class<?>[] classes() default {}; String[] pattern() default {}; }
其中@Repeatable
表示这个注解可重复执行。这个注解会让被注解类的包及其子包中的类注册当spring中。这是与@EnableAutoConfiguration的不同
在SpringBootApplication
中它的用法是
1 2 @ComponentScan (excludeFilters = { @Filter (type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter (type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
这里它指定了几个不可能探测的类。
TypeExcludeFilter
指定的是那些已经被BeanFactory和被SpringBootApplication自动扫描的类。
AutoConfigurationExcludeFilter
指定排除那些在AutoConfiguration中被注册到spring中的类。
3. @SpringBootApplication的方法
讲完了@SpringBootApplication
的方法,现在再来讲一下其内部方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @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 { @AliasFor (annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; @AliasFor (annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; @AliasFor (annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {}; @AliasFor (annotation = ComponentScan.class, attribute = "basePackageClasses" ) Class<?>[] scanBasePackageClasses() default {}; @AliasFor (annotation = ComponentScan.class, attribute = "nameGenerator" ) Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; @AliasFor (annotation = Configuration.class) boolean proxyBeanMethods () default true ; }
看了这么多,可以发现@SpringBootApplication
的所有方法都是作用于它的子注解。
4. 总结
通过@SpringBootApplication
注解,我们来看一下这个注解可以在项目启动时做哪些事。
通过@SpringBootConfiguration
将主程序标记为@Configuration
,@Configuration
内部又有@Component
,所以会被扫描到,并且其生成的Bean也会被加入到容器中。
通过@EnableAutoConfiguration
先通过@AutoConfigurationPackage
将有该注解的类的包下的全部类都注入spring(不包括子包 ),然后通过@Import(AutoConfigurationImportSelector.class)
判断需要自动配置的类,将他们也都注入到Spring中。
通过@ComponentScan
,将被标记类的包及其子包下所有的类注册到spring容器(通过exclude排除那些已经被注册过的类)。
@EnableAutoConfiguration
和@ComponentScan
的不同
@EnableAutoConfiguration
只会注册当前包下的类,而@ComponentScan
是当前包及其子包下的类。
@EnableAutoConfiguration
是通过registerBeanDefinition
方法注册bean,而@ComponentScan
是在ConfigurationClassParser
类中,通过this.componentScanParser.parse
注册bean
学习笔记,如有错误欢迎指出,立即改正。