当前位置:   article > 正文

SpringBoot原理深入及剖析_springboot深入

springboot深入

SpringBoot

什么是SpringBoot?
引用Spring官网的话说就是:SpringBoot是所有基于spring开发的项目的起点。SpringBoot的设计就是为了让你尽可能快的跑起来spring应用程序,并且尽可能的减少你的配置文件。

它的核心理念就是约定大于配置
SpringBoot的两个主要功能分别是起步依赖和自动配置

起步依赖

我们使用idea随便新建两个SpringBoot程序,在pom文件中几乎都有两个核心依赖,分别是是spring-boot-starterparent和spring-boot-starter-web

1、spring-boot-starterparent

为什么SpringBoot导入dependency时,不需要指定版本?

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

点击spring-boot-starter-parent查看他的底层源文件
在这里插入图片描述
继续往下点,查看底层源文件
在这里插入图片描述
那么我们从这里就可以看出,该文件对一些常用技术框架的依赖文件进行了统一版本号管理,例如activemq、aspectj、db2等等,都是与当前springboot相匹配的版本,这也就是我们在pom文件中引入常用框架不需要写版本号的原因

2、spring-boot-starter-web

spring-boot-starterparent是对常用项目依赖版本的统一管理,那么项目运行依赖的jar包是从何而来?

和上面一样,我们点进spring-boot-starter-web来看看它的底层源文件

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4

源文件代码

 <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.5.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.5.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.5.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.3.7</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.7</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

通过代码我们就可以看出来,在spring-boot-starter-web这个文件中就对web开发所需要的所有依赖进行了封装打包,例如json、Tomcat、web,mvc等等。
因此,我们使用SpringBoot项目的时候只需要在pom文件中引入spring-boot-starter-web这个依赖启动器,就不需要再额外配置Tomcat,springmvc等。

SpringBoot除了提供了web依赖启动器以外,还提供了很多其他的依赖启动器,通常我们只需要在springboot官方文档中搜索starter关键字即可
在这里插入图片描述
列出了Spring Boot官方提供的部分场景依赖启动器,这些依赖启动器适用于不同的场景开发,使用时只需要在pox.xml文件中导入对应的依赖启动器即可。
需要说明的是,Spring Boot官方并不是针对所有场景开发的技术框架都提供了场景启动器,例如数据库操作框架MyBatis、阿里巴巴的Druid数据源等,Spring Boot官方就没有提供对应的依赖启动器。为
了充分利用Spring Boot框架的优势,在Spring Boot官方没有整合这些技术框架的情况下,MyBatis、Druid等技术框架所在的开发团队主动与Spring Boot框架进行了整合,实现了各自的依赖启动器,例如
mybatis-spring-boot-starter、druid-spring-boot-starter等。我们在pom.xml文件中引入这些第三方的依赖启动器时,切记要配置对应的版本号

自动配置

什么是自动配置?

自动配置就是在我们加载jar包依赖的时候,自动为我们配置一些组件的相关配置,我们无需或只需少许的配置就能够运行编写的项目

如何进行自动配置
SpringBoot的项目应用启动入口是@SpringBootApplication注解标注类的main方法
@SpringBootApplication注解能够扫描Spring组件并且自动配置SpringBoot

@SpringBootApplication分析

@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举
中
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时
@Documented //表示注解可以记录在javadoc中
@Inherited //表示可以被子类继承该注解
@SpringBootConfiguration // 标明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan( // 包扫描器
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这段代码我们可以看出,@SpringBootApplication是一个组合注解,其中@Target、@Retention、@Document、@Inherited这些都是元注解信息,这里于我们所要研究的问题关系不大, 不在这里讨论,主要看@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解

1、@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

@SpringBootConfiguration注解刨除元注解以外,只剩一个@Configuration注解,这个注解常用Spring的小伙伴应该比较熟悉,就是用来表明当前类是一个配置文件类,并且可以被扫描到。
那么由此可见@SpringBootApplication和@Configuration作用相同,只不过是SpringBoot对其进行从新分装命名而已

2、@EnableAutoConfiguration

@EnableAutoConfiguration注解表示开启自动配置功能,是实现自动化配置的注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

从源文件代码中,可以看出@EnableAutoConfiguration也是一个组合注解,它由@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})这个两个组合而成。

2.1、@AutoConfigurationPackage
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

@AutoconfigurationPackage主要是由Import({Registrar.class})实现的,@Import注解的作用就是用来给容器导入某个组件,那么@Import({Registrar.class})就是讲Registrar这个组件导入到容器中。

在这里插入图片描述
然后我们通过代码可以看到有一个registerBeanDefinition方法,通过debug可以知道图中选中部分返回值就是主程序类所在的包路径。也就是说@AutoConfigurationPackage就是将主程序和其子包下的组件扫描到容器中。

所以我们在定义项目架构的时候,主程序启动类也就是注解@SpringBootApplication所在的类要放在项目最外层,根目录的位置,这样才能保证所有的组件都能被扫描到
在这里插入图片描述

2.2、@Import({AutoConfigurationImportSelector.class})

同样Import({AutoConfigurationImportSelector.class})是将AutoConfigurationImportSelector这个组件导入到spring容器中,AutoConfigurationImportSelector可以帮助Springboot将所有符合条件的@Configation配置都加载到当前创建的spring ioc容器中
它通过selectImport方法来告诉springboot需要导入哪些组件

// 这个方法告诉springboot都需要导入那些组件
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		//判断 enableautoconfiguration注解有没有开启,默认开启(是否进行自动装配)
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		//1. 加载配置文件META-INF/spring-autoconfigure-metadata.properties,从中获取所有支持自动配置类的条件
		//作用:SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties进行配置。
		// SpringBoot会将收集好的@Configuration进行一次过滤进而剔除不满足条件的配置类
		// 自动配置的类全名.条件=值
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

查看loadMetadata方法

protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";//文件中为需要加载的配置类的类路径

	private AutoConfigurationMetadataLoader() {
	}

	public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
		//重载方法
		return loadMetadata(classLoader, PATH);
	}

	static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
		try {
			//1.读取spring-boot-autoconfigure.jar包中spring-autoconfigure-metadata.properties的信息生成urls枚举对象
            // 获得 PATH 对应的 URL 们
			Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
            // 遍历 URL 数组,读取到 properties 中
            Properties properties = new Properties();

			//2.解析urls枚举对象中的信息封装成properties对象并加载
			while (urls.hasMoreElements()) {
				properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
			}
			// 将 properties 转换成 PropertiesAutoConfigurationMetadata 对象

			//根据封装好的properties对象生成AutoConfigurationMetadata对象返回
			return loadMetadata(properties);
		} catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

深入getCandidateConfigurations方法
getCandidateConfigurations()用来获取默认支持的自动配置类名列表,spring Boot在启动的时候,使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories,找出其中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的属性定义的工厂类名称,将这些值作为自动配置类导入到容器中,自动配置类就生效了

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	    // 让SpringFactoryLoader去加载一些组件的名字
		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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述
那么@EnableAutoConfiguration就是从classpath中搜索META-INF/spring.factories配置文件,并将其中的org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射实例化对应的标注了@configuration的Java形式的配置类,并加载到ioc容器中

我们以上面说到的spring-boot-starter-web为例,在项目启动的时候,对应的WebMvcAutoConfiguration自动配置类就会生效,这个配置类对SpringMVC运行所需要的环境进行了配置,包括默认前缀,默认后缀,视图解析器等等,而这些配置本质上就是spring的xml配置文件,只不过springboot是在自动配置类的形式进行了预先的默认配置,所以springboot在加入相关依赖启动器以后就不需要任何配置就可以启动程序,当然我们也可以对其默认配置进行修改。

上面的一堆很乱,这里有必要进行一个小结:
Springboot自动配置的步骤是:
1、SpringBoot启动
2、@SpringbootApplication起作用
3、@AutoConfigurationPackage
     他通过import将Registrar类导入到容器中,而Registrar类的作用是扫描主配置类同级目录和其子包,并将其组件导入到springboot容器中
4、@Import({AutoConfigurationImportSelector.class})
     AutoConfigurationImportSelector的作用是通过selectorimports方法执行过程中,会使用内部工具类SpringFactoriesLoader查找classpath上所有jar包的META-INF/spring.factories进行加载,实现将配置类信息交给
SpringFactory加载器进行一系列的容器创建过程

3、@ComponentScan

这个注解的作用就是包扫描和spring中的<context:component-scan base-package=“xxxx” />作用相同,通过@AutoConfigurationPackage注解返回的值确定主程序路径,扫描其同目录及其子包

那么到此为止,@SpringBootApplication 的注解的功能就分析差不多了, 简单来说就是 3 个注解的组合注解:
@SpringBootConfiguration
        @Configuration 通过Javacongif的方式将组件添加到容器中
@EnableAutoConfiguration
        @AutoConfigurationPackage //自动配置包,与@ComponentScan扫描到的添加到IOC
        @Import(AutoConfigurationImportSelector.class) //到METAINF/spring.factories中定义的bean添加到IOC容器中
@ComponentScan //包扫描

自定义starter

SpringBoot Starter机制:
SpringBoot是由众多的Starter构成的(一系列自动化配置的starter插件),SpringBoot之所以流行就是因为Starter
Starter是SpringBoot非常重要的组成部分,可以理解为是一个可插拔的插件,由于Starter的使用,某个功能的开发者,不需要关注各个依赖库的处理,不需要具体的配置信息,由SpringBoot自动通过classpath路径下的类发现需要的bean,并且载入到spring的容器中
例如,你想要使用redis插件,那么只要在pom文件中引入spring-boot-starter-redis就可以
为什么要自定义starter
在开发过程中,经常会遇到一些独立于业务之外的配置模块,如果我们将这些可独立于业务代码之外的模块分装成一个starter,复用的时候,只需要将其在pom文件中引入即可,springboot自动为我们完成自动装配
自定义starter的命名规则
SpringBoot提供的starter以spring-boot-starter-xxx的方式命名,而我们自定义的starter,官方推荐以xxx-spring-boot-starter的方式命名,这样有助于区分哪些是spring官方提供的starter,哪些是自定义的starter
自定义starter
1、新建一个maven jar工程,命名为zdy-spring-boot-starter,并且引入依赖

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
</dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、编写Javabean

@EnableConfigurationProperties(SimpleBean.class)
@ConfigurationProperties(prefix = "simplebean")
public class SimpleBean {
private int id;
private String name;
..................
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3、编写配置类MyAutoConfiguration

@Configuration
@ConditionalOnClass //@ConditionalOnClass:当类路径classpath下有指定的类的情况下进行
自动配置
public class MyAutoConfiguration {
static {
System.out.println("MyAutoConfiguration init....");
}
@Bean
public SimpleBean simpleBean(){
return new SimpleBean();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4、resources目录下创建/META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
yhy.project.MyAutoConfiguration
  • 1
  • 2

使用自定义starter
1、在demo的pom文件中引入自定义starter

 <dependency>
            <groupId>org.example</groupId>
            <artifactId>zdy-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

2、在全局配置文件中设置默认值

simplebean.id=1
simplebean.name=自定义starter
  • 1
  • 2

3、编写测试方法

//测试自定义starter
@Autowired
private SimpleBean simpleBean;
@Test
public void zdyStarterTest(){
System.out.println(simpleBean);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

测试结果
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/813189
推荐阅读
相关标签
  

闽ICP备14008679号