springboot中的两个基本jar包

spring-boot-starter-parent (非必需)

作为parent之后所有官方定义的starter包都适配自己的version,减少jar包冲突,

里边引用了dependencies,在dependencyManage中定义了所有版本,可以直接用

如果不以这个包作为parent,需要自己指定版本,或者自己定义dependencyManage

spring-boot-starter (必需)

 springboot 必须的jar包,核心启动器,包含自动配置,日志,spring-core,yaml

其中 spring-boot 包含了启动类 run 方法的相关包

spring-boot-autoconfigure 包含了  @SpringBootApplication 相关包(自动配置)

springboot 两大特性:

  • 起步依赖

起步依赖本质上是一个Maven项目对象模型(Project Object Model),即pom,定义了对其他库的传递依赖;

简单来说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

  • 自动配置

springboot在我们引入对应的starter时,会自动的将一些配置类的bean注册进ioc容器,并且自动为我们配置一些组件的相关配置,

1、在需要的地方可以直接使用@Autowired或者@Resource等注解来使用

2、无需配置或者只需要少量配置就能运行编写的项目。

依赖管理

  • 版本依赖(为什么jar包不需要指定版本?

在springboot项目中,都会有一个spring-boot-starter-parent的依赖

<!-- Spring Boot父项目依赖管理 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent<11./artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

在上述配置中,将spring-boot-starter-parent依赖作为Spring Boot项目的统一父项目依赖管理,并将项目版本号统一为2.2.2.RELEASE,该版本号根据实际开发需求是可以修改的。

点进spring-boot-starter-parent去查看,发现其引用了一个spring-boot-dependencies的parent

在spring-boot-dependencies里边有一个properties节点维护了所有的版本号(只是针对于spring官方维护的starter,第三方定义的不在其内),有一个dependencyManagement节点,配置了所有的jar包依赖,后期引用jar包时只需要写groupId和artifactId即可。

<properties>
    <activemq.version>5.15.11</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.77</appengine-sdk.version>
    <artemis.version>2.10.1</artemis.version>
    <aspectj.version>1.9.5</aspectj.version>
    ...
</properties>
<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <version>2.2.2.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test</artifactId>
        <version>2.2.2.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test-autoconfigure</artifactId>
        <version>2.2.2.RELEASE</version>
      </dependency>
    ...
    </dependencies>
</dependencyManagement>

例如引入web相关依赖,只需要在dependencies节点下引入如下依赖即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • jar包依赖(为什么引入一个jar包就可以直接启动?

每一个starter中都封装了当前starter的所有依赖jar包,所以只需要引入对应的starter即可应用该组件,以spring-boot-starter-web为例:

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.2.2.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.2.2.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.2.2.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
      <version>2.2.2.RELEASE</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>
          <artifactId>tomcat-embed-el</artifactId>
          <groupId>org.apache.tomcat.embed</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.2.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.2.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

在pom文件中dependencies节点下引入了web环境依赖的所有jar包,包括web-mvc和tomcat等,以及spring-boot-starter中自带的spring的ioc和aop相关jar包,这样在引入当前starter时就可以实现web场景开发。

springboot官方除了提供web依赖容器外,还提供了其他场景相关的starter,从官方文档中可以看到如下34种,根据版本不同,可能还会有对应的增减。

这些依赖启动器(starter)适用于不同的场景开发,使用时只要在pom.xml文件中导入对应的依赖启动器即可,不需要特别指定version;

需要说明的是,Spring Boot官方并不是针对所有场景开发的技术框架都提供了场景启动器,例如数据 库操作框架MyBatis、阿里巴巴的Druid数据源等,Spring Boot官方就没有提供对应的依赖启动器。为 了充分利用Spring Boot框架的优势,在Spring Boot官方没有整合这些技术框架的情况下,MyBatis、 Druid等技术框架所在的开发团队主动与Spring Boot框架进行了整合,实现了各自的依赖启动器,例如 mybatis-spring-boot-starter、druid-spring-boot-starter等。我们在pom.xml文件中引入这些第三方的依赖启动器时,切记要配置对应的版本号。

自动配置(启动类分析)

Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }

}

代码包含两部分,一个是@SpringBootApplication注解,一个是run()方法。

@SpringBootApplication注解

点进去发现@SpringBootApplication注解是一个组合注解,上边是注解的元数据信息,下边引入了其他注解:

  • @Target                 注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中
  • @Retention           表示注解的生命周期,Runtime运行时
  • @Documented      表示注解可以记录在javadoc中
  • @Inherited            表示可以被子类继承该注解
  • @SpringBootConfiguration             标明该类为配置类
  • @EnableAutoConfiguration            启动自动配置功能
  • @ComponentScan                         包扫描器

@SpringBootConfiguration

@SpringBootConfiguration 注解表示该类是springboot的配置类,它是一个组合注解,引入了一个 @Configuration 的注解(Spring的注解,本身是 @Component 注解的一个封装,相当于起了个别名),标识是一个配置类

 

@EnableAutoConfiguration

@EnableAutoConfiguration 注解表示开启自动配置功能,它是一个组合注解,引入了一个 @AutoConfigurationPackage 注解(会把@springbootApplication注解标注的类所在包名拿到,并且对该包及其子包进行扫描,将组件添加到容器中) 和 一个 @Import 注解(帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中)

 @AutoConfigurationPackage

 @AutoConfigurationPackage 注解,引入了一个 @Import 注解(spring框架的底层注解,它的作用就是给容器中导入某个组件类,此处就是将Registrar这个组件类导入到容器中)

 对应实现 org.springframework.beans.factory.support.DefaultListableBeanFactory

 跟代码发现,@Import(AutoConfigurationPackages.Registrar.class) 实际上是将  org.springframework.boot.autoconfigure.AutoConfigurationPackages  注册到 beanDefinitionMap 中

@Import(AutoConfigurationImportSelector.class)

将 AutoConfigurationImportSelector这个类导入到spring容器中, AutoConfigurationImportSelector可以帮助springboot应用将所有符合条件的@Configuration配置 都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中

点进去发现 AutoConfigurationImportSelector 类主要是实现了DeferredImportSelector 接口,其他 Aware 结尾的接口是获取一些配置信息,ordered 进行排序,当前功能

DeferredImportSelector 接口继承了 ImportSelector 接口,有一个默认的方法  getImportGroup() ,返回了内部接口 Group 的一个实现类,Group 有 process 和 selectImports 两个方法需要实现,核心逻辑都在这里边

在spring加载 ioc 容器时,AbstractApplicationContext 类里边的核心方法 refresh() 会调用  ConfigurationClassParser 类的 getImports() 方法,getImports 方法会执行所有实现了DeferredImportSelector 接口的实现

 在 AutoConfigurationImportSelector 的实现中,process 方法会去调用 getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) 方法,读取所有符合条件的配置并返回 AutoConfigurationEntry ,并调用 selectImports 方法将 AutoConfigurationEntry  转换成 Iterable<Entry>,最终通过 loadBeanDefinitionsForConfigurationClass 方法注册到 BeanDefinationMap中

 上边提到的DeferredImportSelector 接口继承的 ImportSelector 接口,需要实现  String[] selectImports(AnnotationMetadata var1)  方法;

在AutoConfigurationImportSelector的实现中,selectImports 方法会去调用getAutoConfigurationEntry 方法,返回所有的配置类(断点调试未走到该方法)

默认情况下,有一个 CacheConfigurationImportSelector ,实现了该方法,返回对应的配置类,此处cache默认返回了十个springboot定义的缓存配置类

后边通过  processImports 方法将配置类注册到 BeanDefinationMap中

 跟代码发现,@Import(AutoConfigurationImportSelector.class) 实际上是将  resources/META-INF/spring.factories 文件中读取到的 EnableAutoConfiguration 根据条件筛选后的配置以及实现 ImportSelector 接口中  String[] selectImports(AnnotationMetadata var1)  方法获取到的配置类注册到 beanDefinitionMap 中

 Run方法

分为 new SpringApplication(primarySources)run(args) 两步,整个执行流程如图所示:

  • new 方法

指定主类判断servlet容器类型,后边run方法会根据类型创建对应容器,设置了bootstrapRegistryInitializersinitializerslisteners,并确定了启动类

/**
 * Create a new {@link SpringApplication} instance. The application context will load
 * beans from the specified primary sources (see {@link SpringApplication class-level}
 * documentation for details). The instance can be customized before calling
 * {@link #run(String...)}.
 * @param resourceLoader the resource loader to use
 * @param primarySources the primary bean sources
 * @see #run(Class, String[])
 * @see #setSources(Set)
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 根据加载的类路径是否存在对应类型来判断 web 容器的类型:servlet,reactive,none,没设置容器默认为 none
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	// 从 resources/META-INF/spring.factories 文件中读取配置的BootstrapRegistryInitializer并实例化
	this.bootstrapRegistryInitializers = new ArrayList<>(
			getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
	// 从 resources/META-INF/spring.factories 文件中读取配置的ApplicationContextInitializer并实例化
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// 从 resources/META-INF/spring.factories 文件中读取配置的ApplicationListener并实例化
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 从堆栈跟踪对象数组中,找到main方法对应的类作为启动类
	this.mainApplicationClass = deduceMainApplicationClass();
}
  • run 方法 

加载配置,启动监听器,打印banner,根据servlet类型创建了ioc容器并加载实例刷新容器,执行 callRunners 方法(springboot如何在启动结束后执行方法?

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
	long startTime = System.nanoTime();
	// 根据从spring.factory 中读取到bootstrapRegistryInitializers初始化启动上下文容器
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	ConfigurableApplicationContext context = null;
	configureHeadlessProperty();
	// 获取并启动监听器,从 resources/META-INF/spring.factories 文件中读取配置的 SpringApplicationRunListener
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 启动监听器,发布事件,invokeListener,onApplicationEvent
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 根据监听器以及启动参数来准备环境
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		// 排除不需要的运行环境
		configureIgnoreBeanInfo(environment);
		// 根据bannerMode判断是否打印,读取resource目录下的banner文件并打印
		Banner printedBanner = printBanner(environment);
		// 根据 new SpringApplication 时判断的servlet(webApplicationType)类型,创建IOC容器
		context = createApplicationContext();
		// 设置 DefaultApplicationStartup
		context.setApplicationStartup(this.applicationStartup);
		// spring ioc 容器前置处理,将environment,listeners,applicationArguments,printedBanner 加载进容器
		// 将springApplicationArguments,springBootBanner加载进单例池,设置是否允许循环依赖,是否允许覆盖注册,是否允许懒加载
		// 将来源文件(启动类)注册进 AnnotatedBeanDefinitionReader ,后边根据对应的来源文件去进行加载
		prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
		// 注册关闭勾子,在jvm关闭时关闭上下文;调用 spring 的refresh方法,刷新ioc容器,
		// 这一步执行完之后,beanDefinationMap 和 singletonObject 中就会加载进去所有扫描到的类
		refreshContext(context);
		// 后置处理,空模板,可重写扩展
		afterRefresh(context, applicationArguments);
		Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
		}
		// 观察者模式,设置回调事件,发布 SpringApplicationRunListeners 的 started 监听事件
		listeners.started(context, timeTakenToStartup);
		// 启动完成之后执行 ApplicationRunner 和 CommandLineRunner,都是执行run方法,没指定order的情况下,限制性ApplicationRunner,再执行CommandLineRunner
		// ApplicationRunner 参数是 kv 格式 ApplicationArguments args
		// CommandLineRunner 参数是数组格式  String... args
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, listeners);
		throw new IllegalStateException(ex);
	}
	try {
		Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
		listeners.ready(context, timeTakenToReady);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, null);
		throw new IllegalStateException(ex);
	}
	// 返回创建完的容器
	return context;
}

run 方法中最核心的两步  prepareContext 和 refreshContext

prepareContext 方法

做了spring ioc 容器前置处理,

将environment,listeners,applicationArguments,printedBanner 加载进容器, 

将springApplicationArguments,springBootBanner加载进单例池,设置是否允许循环依赖,是否允许覆盖注册,是否允许懒加载 ,

将来源文件(启动类)注册进 AnnotatedBeanDefinitionReader ,后边根据对应的来源文件去进行加载

 refreshContext 方法

注册关闭勾子,在jvm关闭时关闭上下文;

调用 spring 的refresh方法,刷新ioc容器,AbstractApplicationContext 类的 refresh() 方法,是spring 容器的核心方法,ioc,aop 都会在这一步处理,需要单独去分析

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
		ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments, Banner printedBanner) {
	// 将前置加载的配置信息放到 context 中
	context.setEnvironment(environment);
	// 对 context 进行后置处理,配置beanName生成器,配置加载器、类加载器,转换服务
	postProcessApplicationContext(context);
	// 在 context 容器中,对前边加载的 initializers 进行初始化
	applyInitializers(context);
	// 观察者模式,设置回调事件,发布 SpringApplicationRunListeners 的 context-prepared 监听事件
	listeners.contextPrepared(context);
	// 通过 ApplicationEventMulticaster 发布启动上下文关闭事件 BootstrapContextClosedEvent
	bootstrapContext.close(context);
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	// 注册 springApplicationArguments 进单例池 singletonObjects
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		// 注册 springBootBanner 进单例池 singletonObjects
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
		// 设置是否允许循环引用
		((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
		if (beanFactory instanceof DefaultListableBeanFactory) {
			// 设置是否允许 bean 覆盖注入
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
	}
	if (this.lazyInitialization) {
		// 如果是懒加载,设置对应的后置处理器
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// DefaultPropertiesPropertySource 设置为最低优先级,最后执行
	context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
	// 获取所有的来源文件,主要是启动类
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	// 将对应的源注册进 AnnotatedBeanDefinitionReader,后边根据对应的来源文件去进行加载
	load(context, sources.toArray(new Object[0]));
	// 观察者模式,设置回调事件,发布 SpringApplicationRunListeners 的 context-loaded 监听事件
	listeners.contextLoaded(context);
}

prepareContext  中的 postProcessApplicationContext(context)  方法

/**
 * Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
 * apply additional processing as required.
 * @param context the application context
 */
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
	if (this.beanNameGenerator != null) {
		// 如果当前容器名字的生成器不为空,往单例池中注入配置beanName生成器 internalConfigurationBeanNameGenerator
		context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
				this.beanNameGenerator);
	}
	if (this.resourceLoader != null) {
		// 如果配置的加载器不为空,将配置加载器、类加载器都保存到容器工厂中
		if (context instanceof GenericApplicationContext) {
			((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
		}
		if (context instanceof DefaultResourceLoader) {
			((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
		}
	}
	if (this.addConversionService) {
		// 将环境中配置的的转换服务保存到容器中
		context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
	}
}

版权声明:本文为weixin_37929489原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_37929489/article/details/111317452