Глубокое понимание основных принципов SpringBoot (1) -------- Механизм запуска (механизм запуска)

Spring Boot

Введение

   Студенты, которые использовали Springboot, должны уже знать, что Springboot значительно упростил начальный процесс построения и разработки проекта, настроив использование многих фреймворков по умолчанию. Цель этой статьи — шаг за шагом проанализировать процесс запуска Springboot, на этот раз в основном для анализа автоматической сборки функций Springboot.
   Итак, сначала давайте посмотрим, как наши веб-проекты создавались в прошлом. Обычно нам нужно создать веб-приложение на основе Spring. Нам нужно сделать следующее:
Файл   1.pom представляет связанные пакеты jar, включая spring, springmvc, redis, mybaits, log4j, mysql-connector-java и другие связанные jar-файлы...
   2. Настройка web.xml, конфигурация прослушивателя, конфигурация фильтра, конфигурация сервлета, конфигурация log4j, конфигурация ошибок...
  3. Настроить подключение к базе данных, настроить транзакцию Spring
  4. Настройте преобразователь представления
  5. Включить аннотации и автоматическое сканирование   6. После завершения настройки разверните tomcat и начните отладку
   ......
  На сборку начального проекта может уйти час или полдня на его сохранение, но все будет очень удобно после использования SpringBoot.Давайте сначала разберем стартовые зависимости SpringBoot и автоматическую настройку.

2. Стартовая зависимость

   1. Добавьте в наш файл pom следующие jar-файлы:

   <modelVersion>4.0.0</modelVersion>
   <parent>
   	<groupId>org.springframework.boot</groupId>
   	<artifactId>spring-boot-starter-parent</artifactId>
   	<version>2.0.4.RELEASE</version>
   	<relativePath /> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.example</groupId>
   <artifactId>demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>demo</name>
   <description>Demo project for Spring Boot</description>

   <properties>
   	<java.version>1.8</java.version>
   </properties>

   <dependencies>
   	<dependency>
   		<groupId>org.springframework.boot</groupId>
   		<artifactId>spring-boot-starter-web</artifactId>
   	</dependency>

   	<dependency>
   		<groupId>org.springframework.boot</groupId>
   		<artifactId>spring-boot-starter-test</artifactId>
   		<scope>test</scope>
   	</dependency>

   	<!--mybatis 开发包 -->
   	<dependency>
   		<groupId>org.mybatis.spring.boot</groupId>
   		<artifactId>mybatis-spring-boot-starter</artifactId>
   		<version>1.3.2</version>
   	</dependency>
   	<!--springboot web模块支持 -->
   	<dependency>
   		<groupId>org.springframework.boot</groupId>
   		<artifactId>spring-boot-starter-web</artifactId>
   	</dependency>
   	<dependency>
   		<groupId>mysql</groupId>
   		<artifactId>mysql-connector-java</artifactId>
   		<scope>runtime</scope>
   	</dependency>
   </dependencies>

   <build>
   	<plugins>
   		<plugin>
   			<groupId>org.springframework.boot</groupId>
   			<artifactId>spring-boot-maven-plugin</artifactId>
   		</plugin>
   	</plugins>
   </build>

Пакет spring-boot-starter-web автоматически помогает нам ввести соответствующие пакеты jar, необходимые для разработки веб-модуля.
mybatis-spring-boot-starter помог нам представить пакеты jar, связанные с разработкой dao.
spring-boot-starter-xxx — это официальный стартер, а xxx-spring-boot-starter — сторонний стартер.
Сделайте скриншот нашего mybatis-spring-boot-starter

avatar
Видно, что у mybatis-spring-boot-starter нет никакого исходного кода, только pom-файл, его роль — помочь нам представить другие jar-файлы.

2. Настройте источник данных

spring:
 datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    hikari:
     # 最小空闲连接数量
     minimum-idle: 5
     # 连接池最大连接数,默认是10
     maximum-pool-size: 60
     # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
     auto-commit: true
     # 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟
     idle-timeout: 600000
     # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
     max-lifetime: 1800000
     # 数据库连接超时时间,默认30秒,即30000
     connection-timeout: 60000

Механизм   stater помогает нам завершить соответствующие пакеты jar, необходимые для запуска проекта. Тогда снова возникает вопрос: не нужно ли настраивать многие bean-компоненты в application.xml в традиционных приложениях spring, например, конфигурацию dataSource, конфигурацию transactionManager... Как springboot помогает нам завершить настройку этих bean-компонентов? Давайте проанализируем этот процесс

Три, автоматическая конфигурация ------------

1. Конфигурация бина на основе java-кода

Взяв в качестве примера mybatis, на приведенном выше снимке экрана мы обнаружили, что пакет mybatis-spring-boot-starter помог нам ввести пакет mybatis-spring-boot-autoconfigure, как показано ниже:

avatar
В нем есть класс MybatisAutoConfiguration, откройте этот класс, чтобы посмотреть, что там.
avatar
Учащиеся, знакомые с двумя bean-компонентами @Configuration& и @Bean, возможно, уже знакомы с ними. Эти две аннотации можно использовать вместе для создания класса конфигурации на основе кода Java, который можно использовать для замены соответствующего файла конфигурации xml.
Классы, аннотированные с помощью @Configuration, можно рассматривать как фабрики, которые производят экземпляры bean-компонентов для управления контейнером Spring IoC.
Аннотация @Bean сообщает Spring, что метод, аннотированный @Bean, вернет объект, который должен быть зарегистрирован в контейнере Spring.
Таким образом, приведенный выше класс MybatisAutoConfiguration автоматически помогает нам генерировать важные экземпляры Mybatis, такие как SqlSessionFactory, и передавать их контейнеру Spring для управления, тем самым завершая автоматическую регистрацию bean-компонентов.

2. Автоматически настраивать условные зависимости

Из аннотаций, используемых в классе MybatisAutoConfiguration, видно, что существуют зависимости для завершения автоматической настройки.

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {

 private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);

 private final MybatisProperties properties;

 private final Interceptor[] interceptors;

 private final ResourceLoader resourceLoader;

 private final DatabaseIdProvider databaseIdProvider;

 private final List<ConfigurationCustomizer> configurationCustomizers;
 ......

Первый предварительный просмотр Springboot — это общие аннотации условной зависимости:

@ConditionalOnBean, экземпляр bean-компонента будет создан только в том случае, если в текущем контексте есть bean-компонент.

@ConditionalOnClass, когда класс находится в пути к классам, будет создан экземпляр компонента.

@ConditionalOnExpression, когда выражение истинно, будет создан экземпляр компонента.

@ConditionalOnMissingBean экземпляр bean-компонента будет создан только в том случае, если он не существует в текущем контексте.

@ConditionalOnMissingClass, когда класс не существует в пути к классам, будет создан экземпляр компонента.

@ConditionalOnNotWebApplication, экземпляр этого компонента создается, если он не является веб-приложением.

@AutoConfigureAfter, создайте экземпляр компонента после его автоматической настройки.

@AutoConfigureBefore, создайте экземпляр компонента до его автоматической настройки.

Таким образом, для завершения автоматической настройки Mybatis два класса, SqlSessionFactory.class и SqlSessionFactoryBean.class, должны существовать в пути к классам, а также должен существовать компонент DataSource, который автоматически регистрируется.
Введите класс DataSourceAutoConfiguration, вы увидите, что этот класс принадлежит этому пакету: org.springframework.boot.autoconfigure.jdbc Этот пакет относится к пакету spring-boot-autoconfigure-2.0.4.RELEASE.jar, В пакете автоматической настройки представлены пакеты jdbc, kafka, logging, mail, mongo и другие. Многие пакеты должны быть автоматически настроены после того, как мы введем соответствующий jar, чтобы они вступили в силу.

avatar

3. Получение параметров Bean

Пока мы уже знаем процесс настройки bean-компонента, но мы не видели, как springboot считывает свойства файла конфигурации yml или properties для создания источника данных?
В классе DataSourceAutoConfiguration мы заметили использование аннотации EnableConfigurationProperties.

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
		DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

	@Configuration
	@Conditional(EmbeddedDatabaseCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import(EmbeddedDataSourceConfiguration.class)
	protected static class EmbeddedDatabaseConfiguration {

	}
......

Свойства источника данных инкапсулированы в DataSourceProperties, а префикс файла конфигурации указывается с помощью аннотации ConfigurationProperties.

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {

	private ClassLoader classLoader;

	/**
	 * Name of the datasource. Default to "testdb" when using an embedded database.
	 */
	private String name;

	/**
	 * Whether to generate a random datasource name.
	 */
	private boolean generateUniqueName;

	/**
	 * Fully qualified name of the connection pool implementation to use. By default, it
	 * is auto-detected from the classpath.
	 */
	private Class<? extends DataSource> type;

	/**
	 * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
	 */
	private String driverClassName;

	/**
	 * JDBC URL of the database.
	 */
	private String url;

	/**
	 * Login username of the database.
	 */
	private String username;

	/**
	 * Login password of the database.
	 */
	private String password;

	/**
	 * JNDI location of the datasource. Class, url, username & password are ignored when
	 * set.
	 */
	private String jndiName;

	/**
	 * Initialize the datasource with available DDL and DML scripts.
	 */
	private DataSourceInitializationMode initializationMode = DataSourceInitializationMode.EMBEDDED;

	/**
	 * Platform to use in the DDL or DML scripts (such as schema-${platform}.sql or
	 * data-${platform}.sql).
	 */
	private String platform = "all";

	/**
	 * Schema (DDL) script resource references.
	 */
	private List<String> schema;

	/**
	 * Username of the database to execute DDL scripts (if different).
	 */
	private String schemaUsername;

	/**
	 * Password of the database to execute DDL scripts (if different).
	 */
	private String schemaPassword;

	/**
	 * Data (DML) script resource references.
	 */
	private List<String> data;
	
	......

Благодаря приведенному выше анализу мы можем узнать, что:
Роль аннотации @ConfigurationProperties заключается в преобразовании файла конфигурации yml или свойств в bean-компонент.
Роль аннотации @EnableConfigurationProperties состоит в том, чтобы сделать аннотацию @ConfigurationProperties эффективной. Если настроена только аннотация @ConfigurationProperties, bean-компонент, преобразованный из файла конфигурации yml или properties, не может быть получен в контейнере spring.

Таким образом, параметры конфигурации yml или properties преобразуются в bean-компоненты, и как эти bean-компоненты обнаруживаются и загружаются?

3. Открытие Бина

По умолчанию springboot сканирует все компоненты основного класса и подклассов в пакете, в котором находится класс запуска, но не включает классы в зависимом пакете, так как же обнаруживаются и загружаются bean-компоненты в зависимом пакете?

Обычно мы добавляем аннотацию @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 {

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	@AliasFor(annotation = EnableAutoConfiguration.class)
	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
	 */
	 ......

На самом деле имеют значение только три аннотации:

@Configuration (@Configuration по-прежнему применяется в @SpringBootConfiguration)

@EnableAutoConfiguration

@ComponentScan

Роль @Configuration Мы уже знаем выше, что аннотированный класс станет классом конфигурации компонента.

Роль @ComponentScan заключается в автоматическом сканировании и загрузке подходящих компонентов, таких как @Component и @Repository, и, наконец, в загрузке этих определений bean-компонентов в контейнер Spring.

Функция аннотации @EnableAutoConfiguration очень важна, благодаря поддержке @Import она собирает и регистрирует связанные определения bean-компонентов в зависимых пакетах.

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

	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 {};

}


Как и в приведенном выше исходном коде, аннотация @EnableAutoConfiguration вводит две аннотации: @AutoConfigurationPackage и @Import. Роль @AutoConfigurationPackage заключается в автоматической настройке пакета, а @Import импортирует компоненты, которые необходимо настроить автоматически.

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

}

/**
 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
 * configuration.
 */
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		register(registry, new PackageImport(metadata).getPackageName());
	}

	@Override
	public Set<Object> determineImports(AnnotationMetadata metadata) {
		return Collections.singleton(new PackageImport(metadata));
	}

}

new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()

new AutoConfigurationPackages.PackageImport(metadata)

Функция этих двух строк кода состоит в том, чтобы загрузить все компоненты основного класса и подкласса в пакете, где находится класс запуска, и зарегистрировать их в контейнере Spring.Это сканирование по умолчанию основного класса и подкласса в пакете где находится класс запуска Все компоненты.

Тогда снова возникает вопрос, откуда берутся бобы, которые нужно собрать и зарегистрировать в контейнере Spring?

Введите класс AutoConfigurationImportSelector,

Мы можем обнаружить, что метод SpringFactoriesLoader.loadFactoryNames вызывает метод loadSpringFactories для чтения информации файла META-INF/spring.factories из всех пакетов jar.

Ниже приведена часть файла spring.factories в jar spring-boot-autoconfigure.Существует ключевое значение org.springframework.boot.autoconfigure.EnableAutoConfiguration, определяющее bean-компоненты, которые необходимо настроить автоматически.Прочитав эту конфигурацию, группа класса @ Configuration.

org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

Каждый xxxAutoConfiguration является классом конфигурации bean-компонента на основе Java. На самом деле не все эти xxxAutoConfiguratios будут загружены и будут загружены в соответствии с такими условиями, как @ConditionalOnClass в xxxAutoConfiguration; класс @Configuration в spring.factories создается как соответствующий столбец экземпляра java с помощью механизма отражения.

Теперь, когда мы знаем, как обнаружить bean-компоненты для автоматической настройки, последний шаг — загрузить эти bean-компоненты в контейнер Spring.

4. Загрузка бобов

Если вы хотите, чтобы общий класс управлялся контейнером Spring, обычно существуют следующие методы:

1. Используйте аннотации @Configuration и @Bean

2. Аннотируйте класс аннотацией @Controller @Service @Repository @Component, а затем включите @ComponentScan для автоматического сканирования.

3. Используйте метод @Import

Метод @Import используется в Springboot.

В аннотации @EnableAutoConfiguration используется аннотация @Import({AutoConfigurationImportSelector.class}).AutoConfigurationImportSelector реализует интерфейс DeferredImportSelector.

Интерфейс DeferredImportSelector наследует интерфейс ImportSelector, а интерфейс ImportSelector имеет только один метод selectImports.

Метод selectImports возвращает набор bean-компонентов, а аннотация @EnableAutoConfiguration использует аннотацию @Import для внедрения этого набора bean-компонентов в контейнер Spring. Springboot официально завершает внедрение bean-компонентов с помощью этого механизма.

4. Резюме

Мы можем резюмировать ключевые шаги автоматической настройки и соответствующие аннотации следующим образом:

1. @Configuration& и @Bean------>>> bean-конфигурация на основе java-кода

2. @Conditional-------->>>>>> установить условные зависимости автоматической конфигурации

3. @EnableConfigurationProperties и @ConfigurationProperties->Прочитайте файл конфигурации и преобразуйте его в bean-компонент.

4. @EnableAutoConfiguration, @AutoConfigurationPackage и @Import-> реализуют обнаружение и загрузку компонентов.