我们都知道一个SpringBoot主配置类只要标注上<u>@SpringBootApplication</u>的注解,Spring就会帮我们自动配置各个组件和实例化Bean,我们来通过源码分析一下SpringBoot自动配置原理。
首先我们要知道,SpringBoot将符合条件的@Configuration类都加载到Spring容器中,就像一只八爪鱼,我们的启动类就是一个典型的@Configuration类。
包括下面两个关键的注解
@SpringBootConfiguration @EnableAutoConfiguration
其中@SpringBootConfiguration 就是get主配置类添加上@Configuration 注解让主配置类的自动配置能被扫描到
下面我们主要分析一下@EnableAutoConfiguration 注解
其中也包含两个关键注解
@AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class})
我们先看一下第一个
这个注解导入了SpringBoot中的Registrar类 用作包路径下的Bean扫描并注册到BeanFactory中
@Import({Registrar.class})
详细看一下这个类
其中主要的方法是registerBeanDefinitions
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //获取到元信息的包名传入注册器 AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()); }
传入两个参数:
目录结构如下:
元信息如下:
new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()获取到该启动类所在路径的包名,传如register方法注册该包名下的所有需要注册并实例化的Bean(包括@Component @Service @Mapper @Repository等)
public static void register(BeanDefinitionRegistry registry, String... packageNames)
根据传入的register和包名packageName注册该包名下的所有需要注册并实例化的Bean
其中我们要关注的是下面这段代码:
//新建一个GenericBeanDefinition描述Bean的实例 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); //设置bean的类名称 beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class); //获取构造器参数并保存 beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); //Bean的角色 感兴趣的可以去了解一下,有0,1,2对应三种不同角色 beanDefinition.setRole(2); //参数设置完调用registerBeanDefinition注册并实例化Bean registry.registerBeanDefinition(BEAN, beanDefinition); }
这个方法用于获取构造器参数并保存
public ConstructorArgumentValues getConstructorArgumentValues() { if (this.constructorArgumentValues == null) { //创建一个新的构造器参数保存器 this.constructorArgumentValues = new ConstructorArgumentValues(); } return this.constructorArgumentValues; }
该方法对GenericBeanDefinition创建的Bean进行注册到BeanFactory
传入beanName和beanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
其中主要代码如下:
//开始注册Bean //如果已启动注册状态则要加锁注册单例singleton if (this.hasBeanCreationStarted()) { synchronized(this.beanDefinitionMap) { //把Bean存入beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1); //把需要注册的Bean添加到map中 updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; //默认为单例 if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } //如果未启动直接注册无需加锁 } else { this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); }
下图就是beanDefinitionMap返回的值,里面除了Spring框架提供的一些必要的Bean需要注册外,就是我们主启动类所在包下的所有需要扫描的Bean,我只有一个主启动类和一个controller 下面标出
当我尝试把写的HelloWorldController的@RestController注解注释掉以后,SpringBoot没有扫描到这个Controller,也就没有把它注册到BeanFactory中
看完@AutoConfigurationPackage 注解我们看一下 @EnableAutoConfiguration另一个注解@Import({AutoConfigurationImportSelector.class}) 该注解导入了SpringBoot中AutoConfigurationImportSelector类(自动配置选择器)用作选择SpringBoot提供的所需组件Bean的选择并自动配置
主要是下面的方法
传入两个参数
注解元信息的参数(配置类上添加的@ComponentScan(Exclude)):
excludeName和exclude表示需要排除扫描自动配置的类,String[0]表示了没有需要排除的
//获取注解元信息参数 AnnotationAttributes attributes = this.getAttributes(annotationMetadata); //调用getCandidateConfigurations获取需要自动配置的类或者功能 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); //去重 configurations = this.removeDuplicates(configurations); //检查并排除exclude类 Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.filter(configurations, autoConfigurationMetadata); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
负责加载META-INF/spring.factories中的配置的类,这些类就是SpringBoot提供的所需要加载的那些*AutoConfiguration类,也就是要注册的Bean或功能,获取到候选类的BeanName返回一个List
借助SpringFactoriesLoader类实现加载自动配置类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //加载类路径下META-INF/spring.factories中的自动配置类 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; }
返回结果如下:
SpringBoot使用ClassLoader类加载机制加载META-INF/spring.factories
将根据EnableAutoConfiguration类名称去加载需要的类或者功能
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
SpringFactoriesLoader的实现机制跟util包下的 ServiceLoader(SPL)实现机制类似,是一种服务查找机制,为接口查找服务实现类,感兴趣的可以去了解一下
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DependsOn { String[] value() default {}; }
比如:
一般加载:
我们在SearchController中注入了WordConifg这个bean,那么在配置时,会优先配置WordConfig这个类,之后才会继续配置SearchController这个类,然后加载SearchController时会先检查是否在IOC容器中存在WordConfig这个bean,有就加载Controller,没有就报错
@DependsOn({"wordConfig"}) public class SearchController { @Autowired private WordConfig wordConfig; }
但是如果是这样,IOC就不能正确判断优先加载哪个bean,( Service中相互引用
),但是这两个类中都有两个初始化方法:
@Service public class A implements InitializingBean{ @Autowired private B b; public void afterPropertiesSet(){ } } @Service public class B implements InitializingBean { @Autowired private A a; public void afterPropertiesSet(){ } }
如果此时我们想要A的初始化方法 afterPropertiesSet
先于B的初始化方法执行,那么我们需要使用到@DependsOn注解,
如下:
@Service @DependsOn({"b"}) public class A implements InitializingBean{ @Autowired private B b; public void afterPropertiesSet(){ } }
我们看看它的源码:
在 AbstractBeanFactory.doGetBean
方法中有下面这段代码
try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } --------------------------------------- //上面逻辑结束加载该bean bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
bean1
的 BeanDefinition
dependson
注解 beanName
拿到相应的 beans
(多个),注册beans bean1
的加载和注册