一个简单的SpringBoot主程序包含以下内容
@SpringBootApplication
public class HelloWorldMainApplication{
public static void main(String[] args) {
SpringApplication.run(HelloWorldMainApplication.class, args);
}
}
@SpringBootApplication注解:标记SpringBoot主程序,说明这是一个SpringBoot应用。
@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 {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "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
*/
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
接下来逐个分析注解。
@Target:
@Target注解是java的元注解,该注解说明了Annotation所修饰的对象范围。
Indicates the contexts in which an annotation type is applicable
此处为ElementType.TYPE:类,接口(包含注解类型),枚举
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE
}
@Retention:
@Retention注解是java元注解,定义了该注解被保留的时间长短。
Indicates how long annotations with the annotated type are to be retained
此处为RetentionPolicy.RUNTIME:运行时保留
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
@Documented:
@Documented是java元注解,注解表明这个注解应该被javadoc工具记录,注解信息也会被包含在生成的文档中,是一个标记注解。Indicates that annotations with a type are to be documented by javadoc and similar tools by default
@Inherited:
@Inherited是java元注解,标明子类将自动继承该注解。
Indicates that an annotation type is automatically inherited
@SpringBootConfiguration:
/**
* Indicates that a class provides Spring Boot application
* {@link Configuration @Configuration}. Can be used as an alternative to the Spring's
* standard {@code @Configuration} annotation so that configuration can be found
* automatically (for example in tests).
* <p>
* Application should only ever include <em>one</em> {@code @SpringBootConfiguration} and
* most idiomatic Spring Boot applications will inherit it from
* {@code @SpringBootApplication}.
*
* @author Phillip Webb
* @since 1.4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@SpringBootConfiguration注解,继承自@Configuration
@Configuration标注当前类是配置类(一个配置类就相当于一个xml配置文件)。并会将当前类声明的一个或多个以@Bean注解标注的方法的实力纳入spring容器中,实例名就是方法名。
Indicates that a class declares one or more {@link Bean @Bean} methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime
/**
* Indicates that a class declares one or more {@link Bean @Bean} methods and
* may be processed by the Spring container to generate bean definitions and
* service requests for those beans at runtime
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated
* with this Configuration class. If left unspecified (the common case),
* a bean name will be automatically generated.
* <p>The custom name applies only if the Configuration class is picked up via
* component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.
* If the Configuration class is registered as a traditional XML bean definition,
* the name/id of the bean element will take precedence.
* @return the suggested component name, if any (or empty String otherwise)
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
String value() default "";
}
@EnableAutoConfiguration:
/**
* Enable auto-configuration of the Spring Application Context, attempting to guess and
* configure beans that you are likely to need
*/
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.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 {};
}
首先来看@AutoConfigurationPackage:
/**
* Indicates that the package containing the annotated class should be registered with
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
@Import是java元注解
Indicates one or more {@link Configuration @Configuration} classes to import.
由此可见,@AutoConfigurationPackage注解功能是由导入的Registrar类来实现的,查看一下Registrar源码:
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.<Object>singleton(new PackageImport(metadata));
}
}
Registrar:存储导入配置的包
debug调试registerBeanDefinitions方法:
传入的参数是"com.springboot.sty.HelloWorldMainApplication"、即被@SpringBootApplication注解标记的主程序
因此,@SpringBootApplication注解提供了将主配置类(被@SpringBootApplication标注的类)所在包及所有子包里的所有组件扫描到spring容器中的功能。
接下来看@Import(EnableAutoConfigurationImportSelector.class):
/**
* {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
* auto-configuration}. This class can also be subclassed if a custom variant of
* {@link EnableAutoConfiguration @EnableAutoConfiguration}. is needed.
*/
@Deprecated
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {
@Override
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
return getEnvironment().getProperty(
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
true);
}
return true;
}
}
isEnable()方法只是用来判断传入的组件是否支持自动配置,那么这些组件是从哪里来的呢,查看其父类会发现这么一个方法:
@Override
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 configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
selectImports()选择导入的组件,debug调试源码:
首先载入元数据
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
final class AutoConfigurationMetadataLoader {
protected static final String PATH = "META-INF/"
+ "spring-autoconfigure-metadata.properties";
private AutoConfigurationMetadataLoader() {
}
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
即载入"META-INF/spring-autoconfigure-metadata.properties"路径下的元数据。
然后获取候选元数据
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
![](https://img-blog.csdnimg.cn/20200831192149498.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIwNDY2NTY3,size_16,color_FFFFFF,t_70)
最终筛选出目标元数据
configurations = filter(configurations, autoConfigurationMetadata);
![](https://img-blog.csdnimg.cn/20200831193020760.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzIwNDY2NTY3,size_16,color_FFFFFF,t_70)
这样就完成了SpringBoot的自动配置功能。即自动导入从spring-boot-autoconfigure-1.5.9.RELEASE.jar包下META-INF/spring.factories资源文件中EnableAutoConfiguration指定的值中获取所有需要的组件,以全类名方式返回,并添加到容器中(***AutoConfiguration)