使用Spring Initializer 生成一个最简单的 Web 应用,打开启动类。
1 2 3 4 5 6 7 @SpringBootApplication public class Demo01Application { public static void main (String[] args) { SpringApplication.run(Demo01Application.class , args ) ; } }
点击 @SpringBootApplication ,我们可以看到如下注解信息:
1 2 3 4 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan (excludeFilters = { @Filter (type = FilterType.CUSTOM, classes = TypeExcludeFilter.class ), @Filter (type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class ) })
@SpringBootConfiguration @SpringBootConfiguration 表示一个类提供Spring Boot应用程序。它的作用等同于@Configuration,以便可以自动找到配置。应用程序应该只包含一个 @SpringBootConfiguration ,通常的Spring Boot应用程序将从@SpringBootApplication自动继承它。
1 2 3 4 5 6 @Configuration public @interface SpringBootConfiguration { @AliasFor (annotation = Configuration.class ) boolean proxyBeanMethods () default true ;}
proxyBeanMethods 方法指定是否应代理@Bean方法:默认为 Full模式 ,保证每个@Bean方法被调用多少次返回的Bean都是单实例的;也可为 Lite 模式(值为 false),每次调用 @Bean 方法都会返回新的实例。 如果配置类 Bean 之间有依赖关系,则强制使用 Full 模式。 新建 MyConfig 配置类来测试
1 2 3 4 5 6 7 8 @Configuration public class MyConfig { @Bean public User getUser () { return new User("li" , 18 ); } }
接下来在 Main 方法中添加如下代码:
1 2 3 4 MyConfig bean = run.getBean(MyConfig.class ) ; User u1 = bean.getUser(); User u2 = bean.getUser(); System.out.println("U1 == U2? " + (u1 == u2));
运行,可以看到结果为 true,在用户代码中直接调用@Bean方法的情况下也返回共享的单例bean实例。
接下来为 @Configuration
添加配置@Configuration(proxyBeanMethods = false)
,再次运行,可以看到结果为 false,返回了两个不同的实例。
接下来测试组建依赖,修改 MyConfig 为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration (proxyBeanMethods = true )public class MyConfig { @Bean public User getUser () { User u = new User("li" , 18 ); u.setPet(getPet()); return u; } @Bean public Pet getPet () { return new Pet("tom" ); } }
如果将 proxyBeanMethods 改为 false,IDEA 将会提示“使用 Bean 标记的方法被 proxyBeanMethods 为 false 的配置类调用,将其改为 true 或者使用依赖注入。”
@ComponentScan 替换默认的 Filter,替换为AutoConfigurationExcludeFilter
@EnableAutoConfiguration 1 2 3 @AutoConfigurationPackage @Import (AutoConfigurationImportSelector.class ) public @interface EnableAutoConfiguration {}
@AutoConfigurationPackage 使用 @AutoConfigurationPackage 来注册包。如果没有指定 base packages 或 base package classes,则注册带有@AutoConfigurationPackage 的类所在的包。
@Import(AutoConfigurationPackages.Registrar.class) AutoConfigurationImportSelector 中有如下函数:
1 2 3 4 5 6 7 8 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
找到 getAutoConfigurationEntry 定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected AutoConfigurationEntry getAutoConfigurationEntry (AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
其中最核心的方法是 getCandidateConfigurations ,用来获取所有的配置类,点击进入。
1 2 3 4 5 6 7 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; }
点击进入 loadFactoryNames 方法
1 2 3 4 5 6 7 8 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader.class .getClassLoader () ; } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
点击进入 loadSpringFactories方法
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 private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = cache.get(classLoader); if (result != null ) { return result; } result = new HashMap<>(); try { Enumeration<URL> 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()); } } } 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; }
打断点运行到 classLoader.getResource位置,我们可以看到 FACTORIES_RESOURCE_LOCATION 此时为META-INF/spring.factories
,这里扫描系统中所有 META-INF/spring.factories
位置的文件,spring-boot-autoconfigure-2.4.3.jar 中的 spring.factories 文件写死了 springboot 启动就给容器中加载所有的配置类(xxxxx AutoConfiguration)。
在 spring boot 启动过程中,所有的自动配置都是条件配置,以org.springframework.boot.autoconfigure.web.servlet 为例:
1 2 3 4 5 @AutoConfigureOrder (Ordered.HIGHEST_PRECEDENCE)@Configuration (proxyBeanMethods = false )@ConditionalOnWebApplication (type = Type.SERVLET)@ConditionalOnClass (DispatcherServlet.class ) @AutoConfigureAfter (ServletWebServerFactoryAutoConfiguration .class )
@ConditionalOnClass(DispatcherServlet.class) 注解指定 只有 DispatcherServlet.class 在类路径中存在才自动配置。
1 2 3 4 5 6 7 @Bean @ConditionalOnBean (MultipartResolver.class ) @ConditionalOnMissingBean (name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)public MultipartResolver multipartResolver (MultipartResolver resolver) { return resolver; }
还可以自己配置 MultipartResolver,如果自行定义了这个 Bean,那么 spring boot 就会使用用户自定义的。
总结:
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 —-> application.properties