调用SpringApplication.run方法启动,启动类使用@SpringBootApplication注解
@SpringBootApplication
public class StringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(StringBootDemoApplication.class, args);
}
}
@SpringBootApplication注解
@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期中的运行时期)
@documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { // 扫描路径设置
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
@Configuration注解
不同于原来springmvc中用xml配置文件的形式,在springboot中我们大多用配置类来解决配置问题,能够被@Configuration注解加载的bean:
1. xml配置文件的形式配置bean2. java configuration的配置形式配置bean
@Configuration
public class MockConfiguration{
//bean定义
}
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
@ComponentScan注解ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等以及
可以通过basePackages等属性来细粒度地定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
所以SpringBoot的启动类最好是放在root package下,因为默认可以不指定basePackages
@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 {};
}
这个注解上还使用了两个注解:
- @AutoConfigurationPackage
- @import(AutoConfigurationimportSelector.class)
@AutoConfigurationPackage和@import(AutoConfigurationimportSelector.class)都使用了@import注解
@AutoConfigurationPackage源代码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
@import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class>[] basePackageClasses() default {};
}
通过这个注解的源代码可以看出,这个注解又使用了@import(AutoConfigurationPackages.Registrar.class)注解,打开AutoConfigurationPackages.Registrar
static class Registrar implements importBeanDefinitionRegistrar, Determinableimports {
//注册org.springframework.boot.autoconfigure.AutoConfigurationPackages的BeanDefinition
//实际上就是注册当前启动类的根package
@Override
public void registerBeanDefinitions(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new Packageimports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set
import(AutoConfigurationimportSelector.class)
1)AutoConfigurationimportSelector类
AutoConfigurationimportSelector 实现了 DeferredimportSelector 从 importSelector继承的方法:selectimports。
@Override
public String[] selectimports(Annotationmetadata annotationmetadata) {
if (!isEnabled(annotationmetadata)) {
return NO_importS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationmetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(Annotationmetadata annotationmetadata) {
if (!isEnabled(annotationmetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationmetadata);
//加载各个组件jar下的"meta-INF/spring.factories"
List configurations = getCandidateConfigurations(annotationmetadata, attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationmetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationimportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
Return the auto-configuration class names that should be considered选择要进行加载的全路径类名,默认加载spring.factories文件下org.springframework.boot.autoconfigure.EnableAutoConfiguration=下面的所有类,meta-INF/spring.factories长这样:
protected ListgetCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) { //就是默认即将要选择加载的全类名集合 List 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; }
代码中的configurations其实就是这些全类名集合,可以在这里打上断点以调试模式启动,可以看到里面的内容就是spring.factories里面的内容
该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表;
实际上,除了org.springframework.boot.autoconfigure.EnableAutoConfiguration,spring.factories里面还定义了很多其他bean。
所有的这些bean都是通过Spring工厂加载器SpringFactoriesLoader进行加载。
**2)Spring工厂加载器SpringFactoriesLoader
它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类
通过下面的这个方法来获取要再加载的类:
public static ListloadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); //他的key就是我们的注解类名,如“EnableAutoConfiguration”,通过key从map中取出我们的目标类名集合 return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } //以Map >形式返回spring.factories文件信息,内容如下面截图 private static Map > loadSpringFactories(ClassLoader classLoader) { Map > result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } return result; }
如果获取到类信息,spring可以通过类加载器将类加载到jvm中,现在我们已经通过spring-boot的starter依赖方式依赖了我们需要的组件,那么这些组件的类信息在select方法中就可以被获取到。
所以,@EnableAutoConfiguration自动配置的魔法其实就变成了:
从classpath中搜寻所有的meta-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
参考文章1
参考文章2



