Эта статья основана на зависимостях выпуска 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 Cloud
ApplicationContext
Содержание - 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");
}
}
два@Configuration
,вConfigA``@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, как показано ниже:
Мы реализуем структуру из одного родительского контекста и трех соответствующих дочерних контекстов.
Сначала определите родительский контекст:
Класс бобов:
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;
}
Чем меньше значение, тем выше фронт.