在SSM项目中,按照往常惯例,Spring需要在xml中配置开启包扫描的功能去扫描相应包下带有特定注解的类,然后帮我们创建实例,完成自动注入的功能。但是SpringBoot项目中,却并没有看到诸如此类的配置,在启动类我同样也没有看到关于@ComponentScan的注解。于是让我产生了好奇,SpringBoot到底是怎么的一种加载机制呢,接下来,大家就跟我一起去看看源码,一起来分析它是如何加载的吧!
SpringBoot启动类有一个@SpringBootApplicaiotn注解,首先得知道这个注解的含义。让我们点进去,看看它的庐山真面目。
@Target(ElementType.TYPE)//表名该注解可以作用的范围,TYPE表示可以作用在类,enum,接口
@Retention(RetentionPolicy.RUNTIME)//表示注解的声明周期,保留在class文件中(三个声明周期)
@Documented//表名该注解应该被javadoc记录
@Inherited//表名该注解可以被继承
@SpringBootConfiguration//继承至@Configuration,Spring中的注解,表名当前为配置类
@EnableAutoConfiguration//开启自动配置的功能,这个是核心注解
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
//扫描路径设置,可以通过basePackages属性来指定扫描范围,默认扫描当前类所在包以及子包
public @interface SpringBootApplication {
@Target(ElementType.TYPE)//表名该注解可以作用的范围,TYPE表示可以作用在类,enum,接口
@Retention(RetentionPolicy.RUNTIME)//表示注解的声明周期,保留在class文件中(三个声明周期)
@Documented//表名该注解应该被javadoc记录
@Configuration(proxyBeanMethods = false)//表名当前为配置类
public @interface SpringBootConfiguration {
从这我们可以看出,@SpringBootApplication等价于@Configuration+@EnableAutoConfiguration+@ComponentScan。上面我也大致解释过了每个注解的含义,所以@SpringBootApplication注解大致的功能就是带有该注解的类是一个自动配置类,能够开启自动配置和自动扫描包的功能。
再重点看下它的关键注解@EnableAutoConfiguration。能够开启自动配置,将所在扫描包下带有特定注解的类创建bean加入IOC容器中。
@Target(ElementType.TYPE)//表名该注解可以作用的范围,TYPE表示可以作用在类,enum,接口
@Retention(RetentionPolicy.RUNTIME)//表示注解的声明周期,保留在class文件中(三个声明周期)
@Documented//表名该注解应该被javadoc记录
@Inherited//表名该注解可以被继承
@AutoConfigurationPackage//自动配置包
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@Target(ElementType.TYPE)//表名该注解可以作用的范围,TYPE表示可以作用在类,enum,接口
@Retention(RetentionPolicy.RUNTIME)//表示注解的声明周期,保留在class文件中(三个声明周期)
@Documented//表名该注解应该被javadoc记录
@Inherited//表名该注解可以被继承
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
@EnableAutoConfiguration由2个重要的注解@Import和@AutoConfigurationPackage组成。它们分别的含义是导入自动配置的组件,自动扫描包中的类。既然@AutoConfigurationPackage实现自动扫描包,我们debug跟踪下。点进去AutoConfigurationPackages.Registrar中,启动main方法。
可以看到当前需要扫描的包为启动类所在的包。大家有没有好奇是怎么走到这一步的呢?下面debug跟进下从SpringApplication启动到这一步的全过程吧。
@SpringBootApplication
public class GoodsApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsApplication.class, args);
}
}
//点进去run方法!进入SpringApplication中的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);//里面调用的是内部的run方法
}
//此方法是创建了一个SpringApplication实例,然后再调用具体的run方法,构造运行的环境
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这一步没什么好说的,接下来看看创建SpringApplication实例的时候都干了些什么事吧。
//构建实例
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
//此处传入的resourceLoader为null,对传入的primarySources进行空判断,此处我们传入的是Application.class
Assert.notNull(primarySources, "PrimarySources must not be null");
//此处用List初始化Set,做一个去重的操作
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//获取当前应用程序的类型,包含REACTIVE(响应式),NONE(非web,即普通java程序),SERVLET(标准的javaee程序)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//通过加载Spring.factories文件,去加载默认的初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//通过加载Spring.factories文件,去加载默认的监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//通过判断是否含有main方法,确定主程序的入口类
this.mainApplicationClass = deduceMainApplicationClass();
}
static WebApplicationType deduceFromClasspath() {
//此处设置应用程序的一个判断规则,是否满足
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
接下来,进入真正的run方法,有一个refreshContext方法,此处是BeanFactory进行bean生命周期构造的实现,因为创建@Import是实现自动扫描包的功能,创建bean需要去扫描包下带有特定注解的类。run方法中的其他步骤今天暂时先不深究,继续延续我们的主题,需要找到是怎么走到registry方法。继续点进去refreshCOntext方法,最终我们会进入到AbstractApplicationContext。此处代码比较经典,算是Spring中的创建Bean的主要流程代码了。其中的invokeBeanFactoryPostProcessors(beanFactory);会去加载需要创建bean的类,用beanName做key,beanClass做value,存入beanDefanition中,bean的声明大概是为了声明一些bean的属性,例如为signton,lazy-init等,为以后创建bean实例做准备。
//此处省略了部分无关代码,重点关注invokeBeanFactoryPostProcessors(beanFactory);
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
}
继续点进去这个方法,有执行到PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()),继续点进去。会进入PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法。这个方法中会有invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)方法才是真正干事的地方,继续点进去。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
//循环遍历,进行bean声明的注册
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}