专栏目录

1-SpringBoot架构设计与实现原理-SpringBoot设计与特性2-SpringBoot架构设计与实现原理-SpringBoot注解原理3-SpringBoot架构设计与实现原理-自动装配底层原理和手写自动装配4-SpringBoot架构设计与实现原理-SpringBoot自定义starter5-SpringBoot架构设计与实现原理-SpringBootApplication运行原理剖析

SpringBoot的自动加载机制与原理自动装配源码入口

查看@SpringBootApplication源码找到@EnableAutoConfiguration

点击@EnableAutoConfiguration源码

@EnableAutoConfiguration --> @Import --> AutoConfigurationImportSelector

@EnableAutoConfiguration --> @AutoConfigurationPackage --> @Import({Registrar.class}) org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar

根据上下文做动态加载bean,或者批量加载bean

手写自定义自动装载注解

新建xh-auto-configuration项目

创建两个bean对象

public class AccountService {
}
public class UserService {
}

创建XHDefinitionRegistrar

public class XHDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        Class beanClass=AccountService.class;
        RootBeanDefinition beanDefinition=new RootBeanDefinition(beanClass);
        String beanName=StringUtils.uncapitalize(beanClass.getSimpleName());
        beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition);
    }
}

创建XHImportSelector

public class NXImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        Map<String,Object> attributes=                annotationMetadata.getAnnotationAttributes(EnableNXAutoConfiguration.class.getName());
        //动态注入bean :判断逻辑实现动态配置

        //返回的是一个固定的UserService
        return new String[]{UserService.class.getName()};
    }
}

自定义注解EnableXHAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({NXImportSelector.class,NXDefinitionRegistrar.class}) //
public @interface EnableNXAutoConfiguration {

    //配置一些方法
    Class<?>[] exclude() default {};
}

运行验证

@SpringBootApplication
@EnableNXAutoConfiguration
public class NxAutoConfigurationApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext ca=SpringApplication.run(NxAutoConfigurationApplication.class,args);
        System.out.println(ca.getBean(UserService.class));
        System.out.println(ca.getBean(AccountService.class));
    }

}
源码分析自动装配

在 Spring Boot 场景下,基于约定大于配置的原则,实现 Spring 组件自动装配的目的。其中使用了

底层装配技术

Spring 模式注解装配 Spring @Enable 模块装配 Spring 条件装配装配 Spring 工厂加载机制

实现类: SpringFactoriesLoader

配置资源: META-INF/spring.factories

自动装配举例

参考 META-INF/spring.factories

实现方法

  1. 激活自动装配 - @EnableAutoConfiguration
  2. 实现自动装配 - XXXAutoConfiguration
  3. 配置自动装配实现 - META-INF/spring.factories

自定义自动装配

XHAutoConfiguration 条件判断: user.name == “xh” 模式注解: @Configuration @Enable 模块: @EnableXH -> XHImportSelector -> XHConfiguration - > XH

自动装载源码剖析

  1. 打开注解SpringBootApplication,上面有ComponentScan注解用来扫描bean和排除bean
  2. 看看上面的EnableAutoConfiguration注解,上面引用了@Import(AutoConfigurationImportSelector.class)
  3. 我们看看AutoConfigurationImportSelector.class先看看类关系AutoConfigurationImportSelector implements DeferredImportSelector implements extends ImportSelector#selectImports()那么说明我们需要从selectImports方法入手去看
@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
    //自动装配入口 重点看getAutoConfigurationEntry方法
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //重点 getCandidateConfigurations
		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);
		//激活bean中的事件监听
    fireAutoConfigurationImportEvents(configurations, exclusions);
    //返回自动配置的数据对象
		return new AutoConfigurationEntry(configurations, exclusions);
	}

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //SpringFactoriesLoader SPI 机制  
    //去加载spring.factories文件
    //打开spring-boot-autoconfigure-2.4.0-sources.jar!/META-INF/spring.factories  看下EnableAutoConfiguration
    //去演示NXImportSelector 实际EnableAutoConfiguration的values就是需要自动装配加载的bean的类路径
		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;
	}
  1. 重点看看spring-boot-autoconfigure-2.1.6.RELEASE.jar!/META-INF/spring-autoconfigure-metadata.properties

搜索 Configuration、ConditionalOnClass、ConditionalOnBean发现bean加载过程的依赖条件

  1. AutoConfigurationImportSelector中处理自动注入原数据的逻辑
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
			if (this.autoConfigurationMetadata == null) {
				this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
			}
			return this.autoConfigurationMetadata;
		}
  1. 跟踪源码查看哪里调用了AutoConfigurationImportSelector#selectImports一直向上查找直到Spring初始化的源头AbstractApplicationContext#refresh().invokeBeanFactoryPostProcessors(beanFactory);
  2. EnableAutoConfiguration注解的上面有个@AutoConfigurationPackage注解点进去,@Import(AutoConfigurationPackages.Registrar.class)
  3. 将扫描的包注册到IOC容器中
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		//将扫描的包注册到IOC容器中  接下来看看new PackageImports部分代码
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}
	}
	// 获取带有AutoConfigurationPackage注解的类
  PackageImports(AnnotationMetadata metadata) {
			AnnotationAttributes attributes = AnnotationAttributes
	.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
			List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
			for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
				packageNames.add(basePackageClass.getPackage().getName());
			}
			if (packageNames.isEmpty()) {
				packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
			}
			this.packageNames = Collections.unmodifiableList(packageNames);
		}

SpringBoot SPI

SPI的全称是Service Provider Interface, 直译过来就是"服务提供接口"

涉及到的知识点

  • SPI机制
  • FactoryBean
  • JDK动态代理

具体实现看META-INF下的文件

打开spring-boot-autoconfigure-2.4.0-sources.jar!/META-INF/spring.factories 看下EnableAutoConfiguration

key=values[] key=value[] key=values[]

spi的扩展 满足目录结构一致 文件名一致 key要存在并且符合当前的加载

专栏目录

1-SpringBoot架构设计与实现原理-SpringBoot设计与特性2-SpringBoot架构设计与实现原理-SpringBoot注解原理3-SpringBoot架构设计与实现原理-自动装配底层原理和手写自动装配4-SpringBoot架构设计与实现原理-SpringBoot自定义starter5-SpringBoot架构设计与实现原理-SpringBootApplication运行原理剖析