Sping Boot 学习-进阶

一、Spring Boot 自动装配原理

1.1、核心注解解析

1、@SpringBootApplication

  • @SpringBootApplication 是 SpringBoot 启动类上的注解,标志着该类是一个启动类。
  • @SpringBootApplication 是一个复合注解,其中主要包括 @SpringBootConfiguration、SpringBootConfiguration、SpringBootConfiguration
  • 注解关系如下图:

2、@SpringBootConfiguration

  • 是一个自定义注解,点进去之后可以看见其是由 元注解 + @Configuration 组成。
  • @Configuration 表明该类是一个配置类。
  • @Configuration 注解点进去可以看见 @Component 注解,说明其被 Spring IOC 容器所管理。

3、@EnableAutoConfiguration

  • 见名知意,@EnableAutoConfiguration 是自动装配的核心注解。
  • @EnableAutoConfiguration 是一个复合注解,其组成注解包括 @AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class)

4、@AutoConfigurationPackage

  • @AutoConfigurationPackage 也是一个复合注解,其由 元注解 + @Import(AutoConfigurationPackages.Registrar.class) 注解组成。
  • 这个注解是自动配置包,主要是使用的@Import来给Spring容器中导入一个组件 ,这里导入的是AutoConfigurationPackages.Registrar.class 类。
  • 就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意。

5、@ComponentScan

  • 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除TypeExcludeFilterAutoConfigurationExcludeFilter
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

1.2、核心类解析

1、AutoConfigurationPackages.Registrar.class

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) 
{
	register(registry, new PackageImport(metadata).getPackageName());
}
  • metadata :主配置类的全路径类名。
  • debug 可以看见 ‘’new PackageImport(metadata).getPackageName()‘’,获得是主配置所在包下的包名。

2、AutoConfigurationImportSelector.class

  • AutoConfigurationImportSelector 类实现了 ImportSelector接口,也就实现了这个接口中的 selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中
@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		'AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);'
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

===================================================================================

	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		'List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);'
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

=====================================================================================
    
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		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;
	}

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
				'classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					'ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));'
			result = new LinkedMultiValueMap<>();
  • Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。
`AutoConfigure/META-INF/spring.factories: 里面包含了所有springBoot整合的类的全路径类名,基于反射射原理通过类加载器就能获取类的实例对象 .  ”\ “是换行使用的

`AutoConfigure/META-INF/spring-configation-metadata.json: 里面配置所有springBoot整合的类的相关默认配置.

1.3、@Condition

# EnableAutoConfiguration 能获得所有的SpringBoot整合的类的实例化对象,但并不是每个类它都会将创建并注入SpringIOC容器之中,只有引入了相关的依赖,其才会将类创建对象.@Condition是实现这一个功能的注解.	
 	
 	Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载相应的Bean
@Conditional要配合Condition的实现类(ClassCondition)进行使用.

`在AutoConfigure/org.springframework.boot.autconfigure/comdition 文件下springboot提供了大量的条件注解,其中常用的有:
ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean

1.4、@Import

# @Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:
①导入Bean
②导入配置类
③导入 ImportSelector 实现类。一般用于加载配置文件中的类
④导入 ImportBeanDefinitionRegistrar 实现类。
- 导入Bean  @Import(User.class)
- 导入配置类  @Import(UserConfig.class)

二、Spring Boot 零配置原理

2.1、Spring Boot 零配置原理分析

	SpringBoot框架主要的特性就是简化Spring开发的过程,它只是整合了Sprin并没有对其做什么其他的改动。Spring在开发过程中就提供配置文件(Application.xml)或纯注解开发两种模式,在纯注解开发的过程中就已经不需要配置任何的配置文件了。在配置文件模式下开发,就需要提供 'SpringApplication.xml' 'SpringMVCApplication.xml' 'Web.xml'等三个比较主要的配置文件。
	'SpringApplication.xml'  & 'SpringMVCApplication.xml'配置文件可以通过创建相应的配置类来替代掉,在对应的配置中添加 '@Configuration' + '@Bean'注解。
	Spring的IOC容器和SpringMVC中的IOC容器具有父子容器的概念,SpringMVC的IOC容器为子容器,Spring的IOC容器为父容器,子容器中的Bean能调用父容器中的方法,父容器中的方法不能调用子容器中的Bean.
	当启动一个'WEB'项目时,容器(Tomcat)首先会读取项目'web.xml'配置文件里的配置。所以要替换掉'web.xml'文件就得在项目启动之初就能被加载到。这里就涉及到一个'SPI'机制的概念。
	'SPI'是Servlet3.0之后推出的一种规范,是JDK内置的一种服务发现机制,Java提供的一套被第三方实现或扩展的API。它规定在jar包的'META-INFO/services'目录下创建以'服务接口全路径类名命名的文件',该文件里是该接口的具体'实现类的全路径类名'。当外部的程序(Tomcat)装配这个模块时,就能通过'(SpringMVC)jar/META-INFO/services/'里的配置文件找到具体的实现类的全路径类名,然后通过反射机制获取对应类的实例化对象。
    找到对应的SpringMVV的jar包,找到META-INFO/services/。该目录下就有一个SevletContainerInitializer接口的全路径类名为文件名的文件,文件内容就是该接口的具体实现类-SpringServletContainerInitalizer。
    'SpringServletContainerInitalizer':在这个类的上方定义了一个'@HandlesTypes'注解,该注解中配置类'{WebApplicationInitializer.class}'。它会将'WebApplicationInitializer'所有的实现类添加到'onStartup()'方法的'Set<Class<?>>'集合之中('ServletContainerInitializer'接口中只有'onStartup()'一个方法).
	SpringServletContainerInitializer类中会将所有'WebApplicationInitializer'接口的实现类实例化,然后循环执行'onStartup(servletContext)'方法.

2.2、SPI 机制

# 1.什么是SPI机制
	SPI(service provider Interface) ,是JDK内置的一种服务发现机制,是java提供的一套被第三方实现或扩展的API.它规定在'org.springframework:spring-web.jar'包的'META-INFO/services'目录下创建一个以'服务接口全路径类名命名的文件',该文件里是该接口的具体'实现类的全路径类名'.当外部程序(Tomcat)装配这个模块是,就能通过'jar/META-INFO/services/'里的配置文件找到具体的实现类的全路径类名,通过反射机制完成实例化.
'org.springframework.web.SpringServletContainerInitializer'
# 2.为什么一定要在 classes 中的 	META-INFO/services 下呢?
	JDK提供服务实现查找的一个工具类: java.util.ServiceLoader 在这个类里面写死了
	//默认会去这里寻找相关信息
	private static final String PREFIX = 'META-INFO/services';

2.3、SpringServletContainerInitializer 分析

 1.SpringServletContainerInitializer类的作用
	'@HandlesTypes'注解中配置'{WebApplicationInitializer.class}',它会将'WebApplicationInitializer'所有的实现类添加到'onStartup()'方法的'Set<Class<?>>'集合之中('ServletContainerInitializer'接口中只有'onStartup()'一个方法).
	SpringServletContainerInitializer类中会将所有'WebApplicationInitializer'接口的实现类实例化,然后循环执行'onStartup(servletContext)'方法.

三、内置Tomcat原理解析

SpringBoot 在启动的时候会加载到'TomcatWebServer' 类对象,执行里面的初始化方法,初始方法中会调用一个'tomcat.start()',将内置tomcat进行启动.
	Apache提供了一款内嵌式的Tomcat,引入相关依赖让项目可以以 jar 的形式发布, 其中有一个'Tomcat'的类,可以创建器对象,'tomcat.start()',能启动整个tomcat项目.
posted @ 2022-03-02 23:04  菜鸟-肥龙  阅读(185)  评论(0编辑  收藏  举报