Серия Commons of Spring Cloud - 1. Предыстория и подготовка базовых знаний

Spring Cloud

Эта статья основана на зависимостях выпуска Spring Cloud 2020.0.

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

Что такое Spring Cloud Commons

Платформа Spring Cloud включает в себя следующие функции:

  • Распределенное управление многоверсионной конфигурацией
  • Регистрация и обнаружение службы
  • маршрутизация
  • Вызов микросервиса
  • балансировки нагрузки
  • выключатель
  • Распределенный обмен сообщениями

Spring Cloud Commons содержит интерфейсы, которые реализуют все основные загружаемые компоненты, а также то, как и что загружает Spring Cloud Boot. в:

  • контекст весеннего облака: включите то, что нужно загрузить приложениям Spring CloudApplicationContextСодержание
  • Spring Cloud Common: включает в себя следующие основные компоненты и конфигурацию их загрузки:
    • Интерфейс регистрации сервиса:org.springframework.cloud.serviceregistryСумка
    • Интерфейс обнаружения службы:org.springframework.cloud.discoveryСумка
    • Интерфейс балансировки нагрузки:org.springframework.cloud.loadbalancerСумка
    • Интерфейс автоматического выключателя:org.springframework.cloud.circuitbreakerСумка
  • spring cloud loadbalancer: похож на ленту и заменяет ленту. Компоненты, реализующие описанный выше интерфейс балансировки нагрузки

В этой серии мы поговорим оspring cloud commonэтот модуль,spring cloud loadbalancerиspring cloud contextБудет в другой отдельной серии.

Дополнение к базовым знаниям Spring и Spring Boot

Когда мы смотрим на исходный код модуля Spring Cloud, нам нужно помнить, что любой модуль Spring Cloud расширяется на основе Spring Boot, Это расширение обычно осуществляется через механизм SPI spring.factories.Любой исходный код модуля Spring Cloud может быть понят как точка входа.

Spring.factories Механизм SPI

spring-coreПроект предоставляет множество механизмов SPI фреймворка Spring, один из которых очень часто используется и гибко используется в Spring-boot, основан наspring.factoriesМеханизм СПИ.

Так что же такое SPI (поставщик услуг)? При проектировании системы для взаимодействия между модулями часто разрабатывается унифицированный интерфейс для вызовов между модулями. В объектно-ориентированном проектировании мы обычно рекомендуем интерфейсное программирование между модулями.Классы реализации не жестко закодированы между модулями, но укажите, какая реализация указана вне программы. Механизм SPI по умолчанию в Java осуществляется черезServiceLoaderДобиться, проще говоря, путемMETA-INF/servicesСоздайте новый файл в каталоге с именем полного имени интерфейса, а содержимым является полное имя класса реализации интерфейса, а затем программа передает код:

//指定加载的接口类,以及用来加载类的类加载器,如果类加载器为 null 则用根类加载器加载
ServiceLoader<SpiService> serviceLoader = ServiceLoader.load(SpiService.class, someClassLoader);
Iterator<SpiService> iterator = serviceLoader.iterator();
while (iterator.hasNext()){
    SpiService spiService = iterator.next();
}

Получить указанный класс реализации.

В Spring Framework этот классSpringFactoriesLoader, должен быть вMETA-INF/spring.factoriesУкажите интерфейс и соответствующий класс реализации в файле, например, в Spring Cloud Commons:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.client.HostInfoEnvironmentPostProcessor

который указываетEnvironmentPostProcessorреализацияHostInfoEnvironmentPostProcessor.

При этом Spring Boot пройдетSpringFactoriesLoader.loadXXXПодобный метод считывает всеEnvironmentPostProcessorкласс реализации и сгенерируйте bean-компонент в ApplicationContext:

EnvironmentPostProcessorApplicationListener

//这个类也是通过spring.factories中指定ApplicationListener的实现而实现加载的,这里省略
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
    //创建这个Bean的时候,会调用
    public EnvironmentPostProcessorApplicationListener() {
		this(EnvironmentPostProcessorsFactory
				.fromSpringFactories(EnvironmentPostProcessorApplicationListener.class.getClassLoader()));
	}
}

EnvironmentPostProcessorsFactory

static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
	return new ReflectionEnvironmentPostProcessorsFactory(
	        //通过 SpringFactoriesLoader.loadFactoryNames 获取文件中指定的实现类并初始化
			SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
}

Специальное использование spring.factories — EnableAutoConfiguration

META-INF/spring.factoriesИнтерфейс и соответствующий класс реализации не обязательно указываются в файле, например:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration,\

вEnableAutoConfigurationэто записка,LoadBalancerAutoConfigurationиBlockingLoadBalancerClientAutoConfigurationявляются классами конфигурации, а неEnableAutoConfigurationреализация. Итак, что это значит?EnableAutoConfigurationэто записка,LoadBalancerAutoConfigurationиBlockingLoadBalancerClientAutoConfigurationВсе это классы конфигурации.spring.factoriesВот еще одно специальное использование, записывающее загружаемый класс bean-компонента.EnableAutoConfigurationЭти bean-компоненты загружаются при использовании аннотации. Этоspring.factoriesдругое использование .

EnableAutoConfigurationЭто основная аннотация для автозагрузки Spring-boot. С помощью этой аннотации Spring-boot может автоматически загружать различные@ConfigurationАннотированный класс. Так как же реализован этот механизм?

посмотри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 {};
	//排除的Bean名称
	String[] excludeName() default {};
}

мы видели@Importэта аннотация. Эта аннотация является очень распространенной аннотацией среды Spring и основным компонентом конфигурации Spring на основе аннотаций Java.

Роль аннотации @Import

@ImportАннотации обеспечивают@BeanФункция аннотации, как и оригиналаSpringНа основе файла конфигурации xml<import>Функция тегов для организации нескольких разбросанных файлов xml, конечно, здесь заключается в организации нескольких разбросанных файлов xml.@Configurationтип. Функции и использование этой аннотации включают

1. Введите другие @Configuration

Предположим, у вас есть следующий интерфейс и два класса реализации:

package com.test
interface ServiceInterface {
    void test();
}

class ServiceA implements ServiceInterface {

    @Override
    public void test() {
        System.out.println("ServiceA");
    }
}

class ServiceB implements ServiceInterface {

    @Override
    public void test() {
        System.out.println("ServiceB");
    }
}

два@ConfigurationConfigA``@Import``ConfigB:

package com.test
@Import(ConfigB.class)
@Configuration
class ConfigA {
    @Bean
    @ConditionalOnMissingBean
    public ServiceInterface getServiceA() {
        return new ServiceA();
    }
}

@Configuration
class ConfigB {
    @Bean
    @ConditionalOnMissingBean
    public ServiceInterface getServiceB() {
        return new ServiceB();
    }
}

пройти черезConfigAСоздайтеAnnotationConfigApplicationContext,ПолучатьServiceInterface, чтобы увидеть, какая реализация:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigA.class);
    ServiceInterface bean = ctx.getBean(ServiceInterface.class);
    bean.test();
}

Результат:ServiceB.доказывать@ImportОпределение класса загружается вместо собственного.

2. Напрямую инициализировать bean-компоненты других классов

существуетSpring 4.2после,@ImportВы можете напрямую указать класс сущности и загрузить определение класса вcontextсередина. Например, в приведенном выше кодеConfigAиз@Importпревратиться в@Import(ServiceB.class), будет генерироватьServiceBизBeanв контекст контейнера, а затем запуститеmainметод, вывод:ServiceB.доказывать@Importопределения классов загружаются в первую очередь, чем их собственные.

3. Укажите класс, реализующий ImportSelector (и DeferredServiceImportSelector) для персонализированной загрузки.

указанная реализацияImportSelectorкласс, черезAnnotationMetadataсвойства внутри динамически загружаемых классов.AnnotationMetadataдаImportАтрибут класса аннотации (если класс является классом аннотации, он распространяется на класс, не являющийся аннотацией, к которому применяется этот класс аннотации).

необходимо реализоватьselectImportsметод, который возвращает@Configuationили конкретныйBeanполное имя классаStringмножество.

package com.test;
class ServiceImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //可以是@Configuration注解修饰的类,也可以是具体的Bean类的全限定名称
        return new String[]{"com.test.ConfigB"};
    }
}

@Import(ServiceImportSelector.class)
@Configuration
class ConfigA {
    @Bean
    @ConditionalOnMissingBean
    public ServiceInterface getServiceA() {
        return new ServiceA();
    }
}

бежать сноваmainметод, вывод:ServiceB.доказывать@ImportОпределение класса загружается вместо собственного. Как правило, если структура основана наAnnotationMetadataПараметры класса динамической загрузки, вообще пишут доп.EnableАннотация, используемая вместе. Например:

package com.test;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(ServiceImportSelector.class)
@interface EnableService {
    String name();
}

class ServiceImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //这里的importingClassMetadata针对的是使用@EnableService的非注解类
        //因为`AnnotationMetadata`是`Import`注解所在的类属性,如果所在类是注解类,则延伸至应用这个注解类的非注解类为止
        Map<String , Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
        String name = (String) map.get("name");
        if (Objects.equals(name, "B")) {
            return new String[]{"com.test.ConfigB"};
        }
        return new String[0];
    }
}

После этого вConfigAдобавить заметки в@EnableService(name = "B")

package com.test;
@EnableService(name = "B")
@Configuration
class ConfigA {
    @Bean
    @ConditionalOnMissingBean
    public ServiceInterface getServiceA() {
        return new ServiceA();
    }
}

бежать сноваmainметод, вывод:ServiceB.

также может быть достигнутоDeferredImportSelectorинтерфейс, такselectImportsВсе возвращенные классы загружаются последними, а не как@ImportКак уже говорилось, сначала загрузите. Например:

package com.test;
class DefferredServiceImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
        String name = (String) map.get("name");
        if (Objects.equals(name, "B")) {
            return new String[]{"com.test.ConfigB"};
        }
        return new String[0];
    }
}

ИсправлятьEnableServiceаннотация:

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(DefferredServiceImportSelector.class)
@interface EnableService {
    String name();
}

такConfigAиметь приоритет надDefferredServiceImportSelectorвозвращениеConfigBзагрузить, выполнитьmainметод, вывод:ServiceA

4. Укажите класс, реализующий ImportBeanDefinitionRegistrar для персонализированной загрузки.

иImportSelectorИспользование похоже на Цель, но если мы хотим переопределитьBean, такие как динамическое внедрение свойств, изменениеBeanтип иScopeПодождите, это нужно реализовать, указавImportBeanDefinitionRegistrarреализация класса. Например:

определениеServiceC

package com.test;
class ServiceC implements ServiceInterface {

    private final String name;

    ServiceC(String name) {
        this.name = name;
    }

    @Override
    public void test() {
        System.out.println(name);
    }
}

определениеServiceImportBeanDefinitionRegistrarдинамическая регистрацияServiceC,ИсправлятьEnableService

package com.test;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(ServiceImportBeanDefinitionRegistrar.class)
@interface EnableService {
    String name();
}

class ServiceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
        String name = (String) map.get("name");
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(ServiceC.class)
                //增加构造参数
                .addConstructorArgValue(name);
        //注册Bean
        registry.registerBeanDefinition("serviceC", beanDefinitionBuilder.getBeanDefinition());
    }
}

ImportBeanDefinitionRegistrarсуществует@BeanПосле того, как аннотация загружена, ее необходимо изменитьConfigAудалить@ConditionalOnMissingBeanаннотированныйBean, иначе он обязательно сгенерируетConfigAизServiceInterface

package com.test;
@EnableService(name = "TestServiceC")
@Configuration
class ConfigA {
//    @Bean
//    @ConditionalOnMissingBean
//    public ServiceInterface getServiceA() {
//        return new ServiceA();
//    }
}

бежать заmain, вывод:TestServiceC

Принцип реализации автоматической загрузки ядра Spring Boot

Мы упоминали выше@EnableAutoConfigurationВ примечании:

@Import(AutoConfigurationImportSelector.class)

принадлежать@ImportТретье использование аннотаций связано с конкретнымиImportSelectorзагрузить, осознатьselectImportsИнтерфейс возвращает полное имя класса, который необходимо загрузить автоматически. здесьAutoConfigurationImportSelectorРеализация:AutoConfigurationImportSelector

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    //`spring.boot.enableautoconfiguration`这个属性没有指定为false那就是启用了Spring Boot自动装载,否则就是没启用。没启用的话,返回空数组
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	//获取要加载的类,详情见下面源代码
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

//获取要加载的类
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	//`spring.boot.enableautoconfiguration`这个属性没有指定为false那就是启用了Spring Boot自动装载,否则就是没启用。没启用的话,返回空数组
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	//获取注解
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	//从spring.factories读取所有key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	//去重
	configurations = removeDuplicates(configurations);
	//根据EnableAutoConfiguration注解的属性去掉要排除的类
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = getConfigurationClassFilter().filter(configurations);
	//发布AutoConfigurationImportEvent事件
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

Какова иерархия ApplicationContext в Spring Boot

ApplicationContext — это контейнер Spring для управления bean-компонентами и их жизненным циклом. Уровни ApplicationContext определяют границы bean-компонентов и bean-компонентов, которые можно использовать повторно. Для уровня ApplicationContext см.официальная документация, здесь мы используем простой пример, чтобы проиллюстрировать уровень ApplicationContext и границы bean-компонентов в нем. Например, некоторые bean-компоненты могут совместно использоваться несколькими ApplicationContext, а некоторые bean-компоненты действуют только в определенном ApplicationContext. Различные ApplicationContexts могут объявлять одно и то же имя или тот же тип бобов, как это. Мы реализуем структуру ApplicationContext, как показано ниже:

image

Мы реализуем структуру из одного родительского контекста и трех соответствующих дочерних контекстов.

Сначала определите родительский контекст:

Класс бобов:

package com.test.spring.context.bean;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class RootBean {
    private Stirng name;
}

Класс контекста:

import com.hopegaming.scaffold.spring.context.bean.RootBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@PropertySource(value = "classpath:/root.yaml", factory = YamlPropertyLoaderFactory.class)
public class RootContext {
    @Bean
    public RootBean getFatherBean() {
        RootBean rootBean = new RootBean();
        rootBean.setName("root");
        return rootBean;
    }
}

root.yml:

# 配置这些主要是将actuator相关接口暴露出来。
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    jmx:
      exposure:
        exclude: '*'
    web:
      exposure:
        include: '*'

Поскольку мы используем yml, нам нужно настроить его здесь.YamlPropertyLoaderFactoryДля загрузки конфигурации yml:

package com.test.spring.context.config;

import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;

import java.io.IOException;

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }

        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).get(0);
    }
}

Определите класс общедоступного компонента дочернего контекста:

package com.test.spring.context.bean;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class ChildBean {
    private RootBean fatherBean;
    private String name;
}

Определите дочерний контекст1:

package com.test.spring.context.config.child1;

import com.hopegaming.scaffold.spring.context.bean.ChildBean;
import com.hopegaming.scaffold.spring.context.bean.RootBean;
import com.hopegaming.scaffold.spring.context.config.YamlPropertyLoaderFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication(scanBasePackages = {"com.test.spring.context.controller"})
@PropertySource(value = "classpath:/bean-config-1.yaml", factory = YamlPropertyLoaderFactory.class)
public class ChildContext1 {
    @Bean
    public ChildBean getChildBean(@Value("${spring.application.name}") String name, RootBean fatherBean) {
        ChildBean childBean = new ChildBean();
        childBean.setFatherBean(fatherBean);
        childBean.setName(name);
        return childBean;
    }
}

bean-config-1.yaml:

server:
  port: 8080
spring:
  application:
    name: child1

Далее идут ChildContext2, ChildContext3:

package com.test.spring.context.config.child2;

import com.hopegaming.scaffold.spring.context.bean.ChildBean;
import com.hopegaming.scaffold.spring.context.bean.RootBean;
import com.hopegaming.scaffold.spring.context.config.YamlPropertyLoaderFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication(scanBasePackages = {"com.test.spring.context.controller"})
@PropertySource(value = "classpath:/bean-config-2.yaml", factory = YamlPropertyLoaderFactory.class)
public class ChildContext2 {
    @Bean
    public ChildBean getChildBean(@Value("${spring.application.name}") String name, RootBean fatherBean) {
        ChildBean childBean = new ChildBean();
        childBean.setFatherBean(fatherBean);
        childBean.setName(name);
        return childBean;
    }
}


server:
  port: 8081
spring:
  application:
    name: child2

management:
  endpoint:
    health:
      show-details: always
  endpoints:
    jmx:
      exposure:
        exclude: '*'
    web:
      exposure:
        include: '*'


package com.test.spring.context.config.child3;

import com.hopegaming.scaffold.spring.context.bean.ChildBean;
import com.hopegaming.scaffold.spring.context.bean.RootBean;
import com.hopegaming.scaffold.spring.context.config.YamlPropertyLoaderFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication(scanBasePackages = {"com.test.spring.context.controller"})
@PropertySource(value = "classpath:/bean-config-3.yaml", factory = YamlPropertyLoaderFactory.class)
public class ChildContext3 {
    @Bean
    public ChildBean getChildBean(@Value("${spring.application.name}") String name, RootBean fatherBean) {
        ChildBean childBean = new ChildBean();
        childBean.setFatherBean(fatherBean);
        childBean.setName(name);
        return childBean;
    }
}


server:
  port: 8082
spring:
  application:
    name: child3

management:
  endpoint:
    health:
      show-details: always
  endpoints:
    jmx:
      exposure:
        exclude: '*'
    web:
      exposure:
        include: '*'

тестовый интерфейсTestController:

package com.test.spring.context.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Locale;

@RestController
public class TestController {
    @Autowired
    private ChildBean childBean;

    @RequestMapping("/test")
    public ChildBean getChildBean() {
        return childBean;
    }
}

Стартовый класс:

package com.test.spring.context;

import com.hopegaming.scaffold.spring.context.config.child1.ChildContext1;
import com.hopegaming.scaffold.spring.context.config.child2.ChildContext2;
import com.hopegaming.scaffold.spring.context.config.child3.ChildContext3;
import com.hopegaming.scaffold.spring.context.config.root.RootContext;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

public class ContextMain {
    public static void main(String[] args) {
        SpringApplicationBuilder appBuilder =
                new SpringApplicationBuilder()
                        .sources(RootContext.class)
                        //第一个子context用child,剩下的都用sibling
                        .child(ChildContext1.class)
                        .sibling(ChildContext2.class)
                        .sibling(ChildContext3.class);
        ConfigurableApplicationContext applicationContext = appBuilder.run();
    }
}

После запуска посетитеhttp://127.0.0.1:8080/testвозвращение:

{"fatherBean":{"name":"root"},"name":"child1"}

доступhttp://127.0.0.1:8081/testвозвращение:

{"fatherBean":{"name":"root"},"name":"child2"}

доступhttp://127.0.0.1:8082/testвозвращение:

{"fatherBean":{"name":"root"},"name":"child3"}

доступhttp://127.0.0.1:8080/actuator/beansВозврат будет аналогичен следующему (неважно, что бобы опущены):

{
	"contexts": {
		"application-1": {
			"beans": {
				"getChildBean": {
					"aliases": [],
					"scope": "singleton",
					"type": "com.hopegaming.scaffold.spring.context.bean.ChildBean",
					"resource": "com.hopegaming.scaffold.spring.context.config.child2.ChildContext2",
					"dependencies": [
						"getFatherBean"
					]
				},
				"childContext2": {
					"aliases": [],
					"scope": "singleton",
					"type": "com.hopegaming.scaffold.spring.context.config.child2.ChildContext2$$EnhancerBySpringCGLIB$$26f80b15",
					"resource": null,
					"dependencies": []
				}
				.......
			},
			"parentId": "application"
		},
		"application": {
			"beans": {
				"getFatherBean": {
					"aliases": [],
					"scope": "singleton",
					"type": "com.hopegaming.scaffold.spring.context.bean.RootBean",
					"resource": "com.hopegaming.scaffold.spring.context.config.root.RootContext",
					"dependencies": []
				},
				"rootContext": {
					"aliases": [],
					"scope": "singleton",
					"type": "com.hopegaming.scaffold.spring.context.config.root.RootContext$$EnhancerBySpringCGLIB$$18d9c26f",
					"resource": null,
					"dependencies": []
				}
				.......
			},
			"parentId": null
		}
	}
}

В этом примере вы должны иметь определенное представление об уровне ApplicationContext.

Условия загрузки бобов

мы будем часто видеть@Conditionalсоответствующие аннотации, такие как@ConditionalOnBeanи@ConditionalOnClassИ так далее, эти аннотации обеспечивают гибкость загрузки различных классов на основе определенных условий во время автозагрузки.@ConditionalАннотация — это функция, предоставляемая spring-context.На основе этой аннотации Spring Boot предоставляет более конкретные аннотации условной конфигурации, в том числе:

  • @ConditionalOnBean, если BeanFactory текущего ApplicationContext уже содержит эти bean-компоненты, условие выполняется. В отличие@ConditionalOnMissingBean, условие выполняется, если BeanFactory текущего ApplicationContext не содержит эти компоненты.
  • @ConditionalOnClass, условие выполняется, если эти классы присутствуют в текущем пути к классам. В отличие@ConditionalOnMissingClass, если эти классы отсутствуют в текущем пути к классам, выполняется условие
  • @ConditionalOnProperty, указывает, существует ли атрибут и удовлетворяет ли значениеhavingValueЗаданное значение (если не задано, то неfalseв порядке),matchIfMissingПредставляет, выполняется ли условие, если атрибут не существует.

Приведенные выше аннотации используются чаще, остальные для примераConditionalOnCloudPlatformОни обычно не используются и не будут упоминаться здесь.

Если есть несколько похожих@ConditionalАннотация действует на тот же метод или класс,Эти условия нагружения являются отношениями «И»..

Порядок загрузки конфигурации

Из-за сложности условий загрузки bean-компонента иногда мы хотим сначала загрузить некоторые классы конфигурации, а некоторые — после загрузки определенной конфигурации. Например:

@Configuration
public class FirstConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Service service1() {
        ......
    }
}


@Configuration
public class SecondConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Service service1() {
        ......
    }
}

Предполагая, что эти два класса находятся в разных пакетах jar, мы не можем определить, какой класс был создан последним.Service, в это время нам нужно использовать некоторые аннотации, определяющие порядок загрузки конфигурации. Обратите внимание, что порядок загрузки конфигурации здесь является только порядком загрузки определения bean-компонента, в основном для ограничения порядка оценки условий загрузки bean-компонента, упомянутых выше, а не порядка создания bean-компонента. Порядок создания bean-компонента в основном определяется зависимостями bean-компонента и@DependsOnОграничения по аннотациям.

Соответствующие примечания заключаются в следующем:

  • @AutoConfigureAfterУказывает, что текущая конфигурация загружается после определенной конфигурации.
  • @AutoConfigureBeforeУказывает, что текущая конфигурация загружается перед определенной конфигурацией.
  • @AutoConfigureOrderпохожий на@OrderОбратите внимание, укажите порядковый номер загрузки текущей Конфигурации, по умолчанию 0, чем меньше загрузка, тем первым.

Заказ бобов

Для bean-компонентов одного типа (bean-компонентов, реализующих один и тот же интерфейс) мы можем использовать List для автозагрузки, например:

public interface Service {
    void test();
}
@Componenet
public class ServiceA implements Service {
    @Override
    public void test() {
        System.out.println("ServiceA");
    }
}
@Componenet
public class ServiceB implements Service {
    @Override
    public void test() {
        System.out.println("ServiceB");
    }
}

@Componenet
public class Test {
    @Autowired
    private List<Service> services;
}

private List<Service> servicesтам будетserviceAиserviceBЭти два боба, но кто впереди, а кто сзади? в состоянии пройти@OrderАннотация указана.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {

	/**
	 * The order value.
	 * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
	 * @see Ordered#getOrder()
	 */
	int value() default Ordered.LOWEST_PRECEDENCE;

}

Чем меньше значение, тем выше фронт.