SpringBoot的@SpringBootApplication分析

本文主要分析Spring Boot的@SpringBootApplication注解。它由@SpringBootConfiguration、@ComponentScan和@EnableAutoConfiguration组成。其中,@SpringBootConfiguration标识配置类,@ComponentScan指定扫描路径,@EnableAutoConfiguration实现自动装配,通过loadMetadata优化启动速度,最终实现自动配置功能。

SpringBoot的@SpringBootApplication分析

@SpringBootApplication总览

上一篇,我们有提到run()方法调用了prepareContext()方法,而preparenContext就是会将run方法的primarySource即(run方法传的类,标注了SpringBootApplication的类)放到spring的BeanDefinition中,后续refresh的时候,springboot就会将该类实例化并放到spring容器中。

在这里插入图片描述

这里以我自己写的EurekaServerApplication为例子:
上面的load方法就是将箭头指向的EurekaServerApplication,放到context的beanDefinition中,后续refresh的时候,spring会将将其注入到容器中。

于是,这个类上的注解也会在此过程中生效,看看@Springboot 是有哪些注解组合而成的吧。

在这里插入图片描述

核心就是@SpringBootConfiguration和@EnableAutoConfiguration和@ComponentScan

@SpringBootConfiguration

在这里插入图片描述

它就是Configuration的包装,功能和Configuration一样,标识为一个配置类,可以在这个类里面写配置代码。

@ComponentScan

这个类是Spring的,用来指定Spring的扫描路径,不配置路径则扫描当前类的包及它的子包。

@EnableAutoConfiguration

在这里插入图片描述

这个也是个组合注解,由下面两个注解组成。

@AutoConfigurationPackage

在这里插入图片描述

@Import用于给容器注入某个组件类,上面注入了Registrar类。

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
      
       // registerBeanDefinitions顾名思义是给Beandefinitions加东西
       // packageName就是标注了@AutoConfigurationPackage的包名
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata,
				BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImport(metadata));
		}

	}
	public static void register(BeanDefinitionRegistry registry, String... packageNames) {
		// 这里参数 packageNames 缺省情况下就是一个字符串,是使用了注解
		// @SpringBootApplication 的 Spring Boot 应用程序入口类所在的包
		
		if (registry.containsBeanDefinition(BEAN)) {
			// 如果该bean已经注册,则将要注册包名称添加进去
			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
			ConstructorArgumentValues constructorArguments = beanDefinition
					.getConstructorArgumentValues();
            // 加多一个包名进去
			constructorArguments.addIndexedArgumentValue(0,
					addBasePackages(constructorArguments, packageNames));
		}
		else {
			//如果该bean尚未注册,则注册该bean,参数中提供的包名称会被设置到bean定义中去
			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            // BasePackages的源码可以看到,它有个List<String> packages
            // 用来存储包名
			beanDefinition.setBeanClass(BasePackages.class);
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
					packageNames);
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}

由此可见,这个注解的作用就是将标注了@AutoConfigurationPackage的包名加入到容器已有的Bean中,Bean的名字为AutoConfigurationPackages.class.getName(),而Bean的class实际为BasePackages。

那么,将这些报名加入有何用呢?当然是为了之后从这个Bean里面取出来对应的类咯。取出来干吗用,就看具体场景了。

@Import(AutoConfigurationImportSelector.class)

将AutoConfigurationImportSelector注入到容器之中。

AutoConfigurationImportSelector可以将spring.factories下的KEY为AutoConfiguration的类"预注入"到容器,具体能不能注入要看它们的条件注入注解,springboot还提供了一个文件,用于提前判断是不是要"预注入,spirng-autoconfigure-metadata.properties,这个能优化springboot的启动速度,意味着不需要再由@Condition之类的注解判断是否能注入到容器,而是提前判断了。因为AutoConfiguration类都是注解了@Configuration的,里面的具体编码肯定是各种配置,由此就实现了自动装配的目的。

AutoConfigurationImportSelector实现自动装配原理就是它实现了DeferredImportSelector接口,重写了selectImports,下面就分析实现自动装配的源码。

在这里插入图片描述

loadMetadata

在这里插入图片描述

loadMetadata的作用就是我前面说的spirng-autoconfigure-metadata.properties优化机制。

getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 这个方法内有调用List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
		//		getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
       // 第一个参数返回值是EnableAutoConfiguration.class 第二个参数是类加载器
    // loadFactoryNames就是用这个加载器去加载spring.factories并返回Key为EnableAutoConfiguration的全限定类名的值,该值是一个列表,包含了各种自动配置类的全限定名
		List<String> configurations =
            getCandidateConfigurations(annotationMetadata,
				attributes);
		configurations = removeDuplicates(configurations);
     // 这个就是将我说的spirng-autoconfigure-metadata.properties不符合条件的获取到
     // 后续就不用将它对应类传递给spring容器了,所以叫exclusions
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions
       // Entry包含需要注入和不需要注入的,外部调用的方法只取了它的configurations部分
		return new AutoConfigurationEntry(configurations, exclusions);
	}

最后,返回了configurations给spring之后,spring将会根据对应的类名去进行注入,由此自动配置功能就得以实现了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值