Skip to content

Latest commit

 

History

History
145 lines (100 loc) · 6.25 KB

02.深入浅出SpringBoot的核心原理(上).md

File metadata and controls

145 lines (100 loc) · 6.25 KB

SpringBoot

约定优于配置的体现主要是:

1.maven 的目录结构

a) 默认有 resources 文件夹存放配置文件

b) 默认打包方式为 jar

2.spring-boot-starter-web 中默认包含 spring mvc 相关 依赖以及内置的 tomcat 容器,使得构建一个 web 应用 更加简单

3.默认提供 application.properties/yml 文件

4.默认通过 spring.profiles.active 属性来决定运行环境时读取的配置文件

5.EnableAutoConfiguration 默认对于依赖的 starter 进行 自动装载

SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

SpringBootApplication 本质上是由 3 个注解组成,分别是

1.@Configuration

2.@EnableAutoConfiguration

3.@ComponentScan

@Configuration

Configuration 这个注解大家应该有用过,它是 JavaConfig,形式的基于 Spring IOC 容器的配置类使用的一种注解。因 为 SpringBoot 本质上就是一个 spring 应用,所以通过这 个注解来加载 IOC 容器的配置是很正常的。所以在启动类 里面标注了@Configuration,意味着它其实也是一个 IoC 容器的配置类。

传统意义上的 spring 应用都是基于 xml 形式来配置 bean 的依赖关系。然后通过 spring 容器在启动的时候,把 bean 进行初始化并且,如果 bean 之间存在依赖关系,则分析这 些已经在 IoC 容器中的 bean 根据依赖关系进行组装。 直到 Java5 中,引入了 Annotations 这个特性,Spring 框 架也紧随大流并且推出了基于 Java 代码和 Annotation 元 信息的依赖关系绑定描述的方式。也就是 JavaConfig。 从 spring3 开始,spring 就支持了两种 bean 的配置方式, 一种是基于 xml 文件方式、另一种就是 JavaConfig 任何一个标注了@Configuration 的 Java 类定义都是一个 JavaConfig 配置类。而在这个配置类中,任何标注了 @Bean 的方法,它的返回值都会作为 Bean 定义注册到 Spring 的 IOC 容器,方法名默认成为这个 bean 的 id

@EnableAutoConfiguration

@ComponentScan

相当 于 xml 配置文件中的context:component-scan它的主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到 spring 的 Ioc 容器中。

标识需 要装配的类的 形式主要是: @Component 、 @Repository、@Service、@Controller 这类的注解标识的 类。 ComponentScan 默认会扫描当前 package 下的的所有加 了相关注解标识的类到 IoC 容器中。

@Import 注解

import 注解是什么意思呢? 联想到 xml 形式下有一个 形式的注解,就明白它的作用了。 import 就是把多个分来的容器配置合并在一个配置中。在 JavaConfig 中所表达的意义是一样的。

动态注入

ImportSelector

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(CacheImportSelector.class)
public @interface EnableDefineService {

    Class<?>[] exclude() default {};
}
public class CacheImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(EnableDefineService.class.getName());

        // 动态注入bean
        return new String[]{CacheService.class.getName()};
    }
}

ImportBeanDefinitionRegister

public class LoggerDefinitionRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Class beanClass = LoggerService.class;
        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
        String beanName = StringUtils.uncapitalize(beanClass.getSimpleName());
        registry.registerBeanDefinition(beanName, beanDefinition);;
    }
}

@EnableAutoConfiguration 注解的实现原理

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	/**
	 * Environment property that can be used to override when auto-configuration is
	 * enabled.
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

可以猜到它是基于 ImportSelector 来实现 基于动态 bean 的加载功能。之前我们讲过 Springboot @Enable*注解的工作原理 ImportSelector 接口 selectImports 返回的数组(类的全类名)都会被纳入到 spring 容器中。

本质上来说,其实 EnableAutoConfiguration 会帮助 springboot 应用把所有符合@Configuration 配置都加载 到当前 SpringBoot 创建的 IoC 容器,而这里面借助了 Spring 框架提供的一个工具类 SpringFactoriesLoader 的支持。以及用到了 Spring 提供的条件注解 @Conditional,选择性的针对需要加载的 bean 进行条件 过滤。

在分析 AutoConfigurationImportSelector 的源码时,会 先扫描 spring-autoconfiguration-metadata.properties 文件,最后在扫描 spring.factories 对应的类时,会结合 前面的元数据进行过滤,为什么要过滤呢? 原因是很多 的@Configuration 其实是依托于其他的框架来加载的, 如果当前的 classpath 环境下没有相关联的依赖,则意味 着这些类没必要进行加载,所以,通过这种条件过滤可以 有效的减少@configuration 类的数量从而降低 SpringBoot 的启动时间。

image-20220413000751740