Из исходного кода SpringBoot для самостоятельной инкапсуляции Starter

Spring Boot
Из исходного кода SpringBoot для самостоятельной инкапсуляции Starter

В этом блоге в основном рассказывается о том, как springboot упрощает для нас большую часть конфигурации, а затем инкапсулирует Starter с исходным кодом.Во-первых, нам нужно поговорить о двух местах, первое — это начальная зависимость springboot, а второе — автоматическая сборка спрингбута;

Зависит от запуска

Нам нужно представить при создании проекта Springbootspring-boot-starter-webэта зависимость;

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

Мы нажимаем на эту зависимость и видим, что эта начальная зависимость объединяет общие веб-зависимости, такие какspring-web,spring-webmvc

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <version>2.1.4.RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-json</artifactId>
  <version>2.1.4.RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <version>2.1.4.RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.0.16.Final</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.1.6.RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.1.6.RELEASE</version>
  <scope>compile</scope>
</dependency>

Грубо говоря, стартовые зависимости Spring Boot должны снова инкапсулировать часто используемые зависимости, что удобно нам для внедрения и упрощает настройку pom.xml, но что более важно, управление зависимостями передается Spring Boot, и нам не нужно обращать внимание на разные зависимости.Есть ли конфликт между разными версиями, Spring Boot помог нам его рассмотреть, мы можем его использовать!

Прежде чем использовать стартовые зависимости Spring Boot, нам нужноpom.xmlДобавьте конфигурацию в:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

позволятьpom.xmlНаследовать от Spring Bootpom.xml, в то время как Spring Bootpom.xmlОн определяет зависимости часто используемых фреймворков и соответствующие номера версий, поэтому нам не нужно беспокоиться о конфликтах версий;

автоматическая сборка

Во-первых, мы знаем, что Springboot нужен класс начальной загрузки, который не только является точкой входа приложения, но и играет важную роль в настройке Spring Boot.

@SpringBootApplication
public class Application {

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

можно увидеть@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 {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};
    
    //........
}

здесь@SpringBootConfigurationи@ComponentScanОбратите внимание, первое на самом деле@ConfigurationАннотация заключается в том, чтобы объявить этот класс как класс конфигурации, а последний играет роль включения компонентов автоматического сканирования.

Давайте проанализируем@EnableAutoConfigurationЭта аннотация, функция этой аннотации - включить функцию автоматической сборки Spring Boot, давайте посмотрим на нее:

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

Давайте проанализируем@Import({AutoConfigurationImportSelector.class})Эту аннотацию мы знаем@ImportРоль заключается в добавлении компонентов в контейнер Spring, и вотAutoConfigurationImportSelectorЭтот компонент добавляется в контейнер Spring. то естьAutoConfigurationImportSelectorобъявлен как Bean;

Давайте проанализируем@Importв примечанияхAutoConfigurationImportSelectorДобрый;

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

существуетgetAutoConfigurationEntryметод сканированияClassPathвсе подjarупаковкаspring.factoriesфайл, будетspring.factoriesдокументkeyзаEnableAutoConfigurationВсе значения , то эти значения на самом деле являются полными именами классов,То есть полное имя класса автоконфигурации, затем классы Spring Boot загружают (отражают) эти полные имена, чтобы добавить эти автоматически настроенные классы в контейнер Spring.

Мы находимspring-boot-autoconfigure-2.1.4.RELEASE.jarбаночка-упаковка, откройте ееspring.factoriesфайл, обнаружил, что этот файл имеетkeyзаEnableAutoConfigurationпара ключ-значение

image.png

вот этоjarВ пакете есть класс автоконфигурации, и вы можете обнаружить, что эти конфигурации автоконфигурации начинаются сxxxAutoConfigurationНазванные в соответствии с правилами именования, эти классы автоматической конфигурации включают в себя классы автоматической конфигурации наших часто используемых фреймворков, таких какaop,mongo,redisиwebИ так далее, это может в основном удовлетворить наши ежедневные потребности в развитии. Например, если нам нужно использовать aop в нашей программе, мы можем напрямую ввести соответствующие зависимости!

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

Возьмем для анализа относительно простой класс конфигурации и посмотрим, как он играет свою роль конфигурации; мы используемHttpEncodingAutoConfigurationНапример, часть кода выглядит следующим образом:

//声明这个类为配置类
@Configuration 
//开启ConfigurationProperties功能,同时将配置文件和HttpProperties.class绑定起来
@EnableConfigurationProperties({HttpProperties.class})
//只有在web应用下自动配置类才生效
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
//只有存在CharacterEncodingFilter.class情况下 自动配置类才生效
@ConditionalOnClass({CharacterEncodingFilter.class})
//判断配置文件是否存在某个配置spring.http.encoding,如果存在其值为enabled才生效,如果不存在这个配置类也生效。
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
    private final Encoding properties;

    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }

    //将字符编码过滤器组件添加到 Spring 容器中
    @Bean
    //仅在该注解规定的类不存在于 spring容器中时,使用该注解的config或者bean声明才会被实例化到容器中
    @ConditionalOnMissingBean
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
        return filter;
    }
    
    @Bean
public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
    return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
}

Конфигурация: эта аннотация объявляет, что этот класс является классом конфигурации (так же, как класс конфигурации, который мы обычно пишем, эта аннотация также добавляется к классу).

Енаблеконфигуратионпропертиес: вкл.ConfigurationPropertiesфункция, то есть файл конфигурации иHttpProperties.classЭтот класс привязывает соответствующие значения файла конфигурации кHttpProperties.classсвязанные с переменными, вы можете нажатьHttpProperties.classзайди и посмотри,

@ConfigurationProperties(
    prefix = "spring.http"
)

public static final Charset DEFAULT_CHARSET;
private Charset charset;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map<Locale, Charset> mapping;

пройти черезConfigurationPropertiesУкажите префикс для настройки файлаapplication.propertiesс префиксомspring.httpстоимость иHttpProperties.classПеременные, связанные с классом, можно найти через переменные класса, свойства, которые мы можем установить:charset,force,forceRequest,forceResponseиmapping. Кроме тогоConfigurationPropertiesАннотации будутHttpPropertiesКласс вводится в контейнер Spring, чтобы стать объектом bean-компонента, потому что, как правило, путь сканирования пакета по умолчанию, такой как springboot,xxxxxxApplication.javaПакет и все его подпакеты, но бины в некоторых сторонних банках явно не сканируются, и эта аннотация пригодится.Конечно, вы можете сказать, я использую@ComponentScanНет, разница между этими двумя аннотациями заключается в следующем:@ComponentScanПредпосылка состоит в том, что компонент, который вы хотите, уже существует в контейнере компонентов, и@EnableConfigurationPropertiesЭто позволяет контейнеру автоматически находить нужный класс и регистрировать его как bean-компонент. То есть помимо использования информации о конфигурации, предоставляемой Spring Boot по умолчанию, мы также можем указать информацию о конфигурации через файл конфигурации.

  • ConditionalOnWebApplication:Цель этой аннотации — автоматическая настройка класса для вступления в силу в веб-приложении.
  • ConditionalOnClass:только существуютCharacterEncodingFilterКласс автоматической настройки вступит в силу только в том случае, если этот класс используется.
  • ConditionalOnProperty:Определяет, есть ли в конфигурационном файле конфигурация spring.http.encoding.Если она существует, ее значение включено Вступит в силу.Если этого класса конфигурации не существует, он также вступит в силу.
  • @ConditionalOnMissingBean:Только если класс, указанный в аннотации, не существует в контейнере Spring, объявление конфигурации или компонента с использованием этой аннотации будет создано в контейнере.

Можно обнаружить, что следующие аннотацииConditionalXXXXЭти аннотации являются условными аннотациями, сформулированными Spring, и класс автоконфигурации вступит в силу только при соблюдении условий.

СледующийcharacterEncodingFilterметод, создайтеCharacterEncodingFilterОбъект, то есть фильтр кодировки символов, одновременно устанавливает связанные свойства, а затем возвращает объект, через@BeanАннотация для добавления возвращаемого объекта в контейнер Spring. Таким образом настраивается компонент фильтра кодировки символов, и обычно нам нужно настроить в web.xml следующее:

 <filter>
       <filter-name>springUtf8Encoding</filter-name>
       <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
       <init-param>
           <param-name>encoding</param-name>
           <param-value>utf-8</param-value>
       </init-param>
       <init-param>
           <param-name>forceEncoding</param-name>
           <param-value>true</param-value>
       </init-param> 
    </filter>
    <filter-mapping>
       <filter-name>springUtf8Encoding</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>

На этом мы закончили разбор принципа, давайте инкапсулируем аналогичный самостоятельно.spring-boot-starter-aop

Инкапсулировать стартер

1, Спецификация разработки SpringBoot Starter

  • 1. Использование имениspring-boot-starter-xxxxxxэто наше конкретное имя пакета, если оно интегрированоSpring Cloudзатем используйтеspring-cloud-starter-xxx
  • 2, обычно нужно подготовить дваjarФайлы, один из которых не содержит никакого кода, отвечает только за импорт связанных jar-файлов, а другой содержит основной код

подобноnacosСтартер, интегрированный с Spring Cloud, выглядит следующим образом:

БолееStarterспецификация продукции, мы можем просмотретьОфициальная документация сайта

2, этапы разработки Starter

Мы создаем именнуюokay-spring-boot-starterи ввести связанные зависимости:

	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
    	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
   </dependencies>
    <dependencyManagement>
        <!-- 我们是基于Springboot的应用 -->
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.4.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Поскольку нам нужно использовать соответствующие аннотации, предоставляемые Springboot, и использовать функцию автоматической настройки, предоставляемую Springboot, мы должны ввестиspring-boot-autoconfigureиspring-boot-dependenciesдве зависимости.

3, создайте класс автоматической конфигурации

Вообще говоря, мы можем захотеть предварительно внедрить некоторые из наших собственных bean-компонентов при запуске Springboot.На данный момент нам нужно создать свой собственный класс автоматической конфигурации, обычно используяxxxxAutoConfiguration. Здесь аналогично вышеописанномуHttpEncodingAutoConfiguration, мы подражаемHttpEncodingAutoConfigurationсоздать новыйOkayStarterAutoConfigurationкласс конфигурации;

@Configuration
@EnableConfigurationProperties(OkayProperties.class)
@ConditionalOnClass(Okay.class)
@ConditionalOnWebApplication
public class OkayStarterAutoConfiguration {

    
    @Bean
    @ConditionalOnMissingBean
    /**
     * 当存在okay.config.enable=true的配置时,这个Okay bean才生效
     */
    @ConditionalOnProperty(prefix = "okay.config", name = "enable", havingValue = "true")
    public Okay defaultStudent(OkayProperties okayProperties) {
        Okay okay = new Okay();
        okay.setPlatform(okayProperties.getPlatform());
        okay.setChannel(okayProperties.getChannel());
        okay.setEnable(okayProperties.getEnable());
        return okay;
    }
}

Значение каждой аннотации здесь было объяснено выше, поэтому я не буду объяснять здесь слишком много;

создать новыйOkayProperties, объявляет, какие элементы конфигурации может настроить пользователь пускового устройства.

@ConfigurationProperties(prefix = "okay.config")
public class OkayProperties {

    private String platform;

    private String channel;

    private Boolean enable;

    public String getPlatform() {
        return platform;
    }

    public void setPlatform(String platform) {
        this.platform = platform;
    }

    public String getChannel() {
        return channel;
    }

    public void setChannel(String channel) {
        this.channel = channel;
    }

    public Boolean getEnable() {
        return enable;
    }

    public void setEnable(Boolean enable) {
        this.enable = enable;
    }

    @Override
    public String toString() {
        return "OkayProperties{" +
                "platform='" + platform + '\'' +
                ", channel='" + channel + '\'' +
                ", enable=" + enable +
                '}';
    }
}

существуетresourcesСоздайте новый в каталогеMETA-INFкаталог и создатьspring.factoriesдокумент

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  cn.haoxiaoyong.okay.starter.config.OkayStarterAutoConfiguration

Это в основном то же самое, что и исходный код, который мы объяснили выше!

Используйте наш собственный стартер

Создайте новый проект Springboot и введите наши собственные зависимости maven:

	<dependency>
        <groupId>cn.haoxiaoyong.okay</groupId>
        <artifactId>okay-spring-boot-starter</artifactId>
        <version>0.0.2-SNAPSHO</version>
    </dependency>

И настроить в конфигурационном файле application.yml

Вы видите, как умно это будет автоматически предложено!

okay:
  config:
    platform: pdd
    channel: ws
    enable: true
@RestController
@Slf4j
public class OkController {

    @Autowired
    Okay okay;

    @RequestMapping("okay")
    public String testOkay() {
        log.info(okay.getChannel() + "  " + okay.getPlatform() + "  " + okay.getEnable());

        return okay.getChannel() + "  " + okay.getPlatform() + "  " + okay.getEnable();
    }
}

Ввод браузера: localhost:8082/хорошо, консоль выводит:

image.png

Этот пример просто показывает логический эффект, этоИспользуйте пользовательский Starter и сделайте простую кровать карты

Пример адреса:GitHub.com/смешно с…