SpringBoot源码分析之为何自动扫描功能为主程序当前包

在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);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值