This article is based on the dependencies of the Spring Cloud release 2020.0

This series will take an in-depth look at each component of Spring Cloud, starting with the abstraction of all elements of Spring Cloud Commons, delving into design ideas and source code, and combining practical usage examples for in-depth understanding. This series is suitable for those who have some experience with Spring or Spring Boot.

What is Spring Cloud Commons

The Spring Cloud framework includes the following features:

  • Distributed multi-version configuration management
  • Service registration and discovery
  • routing
  • Microservice invocation
  • Load balancing
  • The circuit breaker
  • Distributed message

Spring Cloud Commons contains interfaces to implement the basic components that all of this loads, as well as how and what Spring Cloud startup loads. Among them:

  • Spring Cloud Context: Includes what spring Cloud applications need to loadApplicationContextThe content of the
  • Spring Cloud Common: Includes the following basic components and their load configuration:
    • Service Registration Interface:org.springframework.cloud.serviceregistrypackage
    • Service discovery Interface:org.springframework.cloud.discoverypackage
    • Load balancing interface:org.springframework.cloud.loadbalancerpackage
    • Breaker interface:org.springframework.cloud.circuitbreakerpackage
  • Spring Cloud loadbalancer: Similar to and a replacement for ribbon. A component that implements the load balancing interface described above

In this series we will cover the Spring Cloud Common module, Spring Cloud Loadbalancer and Spring Cloud Context in a separate series.

Spring and Spring Boot background knowledge supplement

When we look at the source code of a Spring Cloud module, we need to keep in mind that any Spring Cloud module is based on the Spring Boot extension, which is typically done through the Spring.Factories SPI mechanism. Any Spring Cloud module source code can be understood from this point of view

Spring. Factories SPI mechanisms

The Spring-core project provides a variety of SPI mechanisms for the Spring framework. One of the most commonly used and flexible spring-boot mechanisms is the spring-based.

What is an SPI (Service Provider)? In system design, in order to cooperate between modules, a unified interface is often designed for calling between modules. In object-oriented design, we generally recommend interface programming between modules, not hard-coding implementation classes between modules, but specifying which implementation is placed outside the program. Java’s default SPI mechanism is implemented using ServiceLoader, which is simply created by creating a file in the meta-INF /services directory called the fully qualified name of the interface implementation class.

// Specify the interface class to load, and the classloader used to load the class, ServiceLoader<SpiService> ServiceLoader = Serviceloader.load (SpiService. Class, someClassLoader); Iterator<SpiService> iterator = serviceLoader.iterator(); while (iterator.hasNext()){ SpiService spiService = iterator.next(); }Copy the code

Gets the specified implementation class.

In the Spring framework, this class is the SpringFactoriesLoader. You need to specify the interface and the corresponding implementation class in the meta-INF/Spring. factories file, such as in Spring Cloud Commons:

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

The realization of which specifies the EnvironmentPostProcessor HostInfoEnvironmentPostProcessor.

. At the same time, the Spring in the Boot will pass SpringFactoriesLoader loadXXX similar method reads all EnvironmentPostProcessor implementation class and generate the Bean into the ApplicationContext:

EnvironmentPostProcessorApplicationListener

// This class is also loaded with an implementation of the ApplicationListener specified in Spring.Factories, Here to omit the public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {// When this Bean is created, Invokes the public EnvironmentPostProcessorApplicationListener () {this (EnvironmentPostProcessorsFactory .fromSpringFactories(EnvironmentPostProcessorApplicationListener.class.getClassLoader())); }}Copy the code

EnvironmentPostProcessorsFactory

static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) { return new ReflectionEnvironmentPostProcessorsFactory (. / / by SpringFactoriesLoader loadFactoryNames access file specifies the implementation class and initializes SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader)); }Copy the code

Special use of Spring. factories – EnableAutoConfiguration

The meta-INF /spring.factories file does not necessarily specify the interface and the corresponding implementation class, for example:

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

Where EnableAutoConfiguration is an annotation, LoadBalancerAutoConfiguration and BlockingLoadBalancerClientAutoConfiguration are configuration class is not EnableAutoConfiguration implementation. So what does this mean? EnableAutoConfiguration is an annotation, LoadBalancerAutoConfiguration and BlockingLoadBalancerClientAutoConfiguration are configuration class. Spring. factories here is another special use for logging Bean classes to be loaded. EnableAutoConfiguration These beans will be loaded when the annotation is used. This is another use of spring.factories.

EnableAutoConfiguration is the core annotation for Spring-Boot auto-loading. With this annotation, Spring-Boot can automatically load various @Configuration annotation classes. So how does this mechanism work?

See the source code of 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 {}; String[] excludeName() default {}; }Copy the code

We see the @import annotation. This annotation is a very common annotation of the Spring framework and is a major part of Spring’s Java-based annotation configuration.

@import The role of annotations

The @import annotation provides the @bean annotation functionality, as well as the original Spring ability to organize multiple scattered XML files based on the < Import > tag in the XML Configuration file, in this case the @Configuration class. The functions and uses of this annotation include

1. Introduce other @Configuration

Suppose you have the following interface and two implementation classes:

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"); }}Copy the code

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(); }}Copy the code

By means of ConfigA create AnnotationConfigApplicationContext ServiceInterface, see what kind of implementation:

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

The output is: ServiceB. Prove that the @import class definition is loaded before itself.

2. Directly initialize beans of other classes

After Spring 4.2, @import can specify the entity class directly and load the class definition into the context. For example, changing @import from ConfigA in the above code to @import (serviceb.class) generates the ServiceB Bean into the container context, and then runs the main method with the output: ServiceB. Class definition loading to prove that @import takes precedence over itself.

3. Specify the implementation ImportSelector (and DefferredServiceImportSelector), used for personalized loading

AnnotationMetadata specifies a class that implements ImportSelector and dynamically loads the class via AnnotationMetadata properties. AnnotationMetadata is the attribute of the class in which the Import annotation is located (or, if the annotation class is annotated, extends to the non-annotated class in which the annotation class is applied).

You need to implement the selectImports method, which returns a String array of @Configuation or the fully qualified names of the concrete Bean class to be loaded.

package com.test; class ServiceImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata ImportingClassMetadata) {return new String[]{" com.test.configb "}; } } @Import(ServiceImportSelector.class) @Configuration class ConfigA { @Bean @ConditionalOnMissingBean public ServiceInterface getServiceA() { return new ServiceA(); }}Copy the code

Run the main method again and output: ServiceB. Prove that the @import class definition is loaded before itself. AnnotationMetadata allows classes to be dynamically loaded using AnnotationMetadata. Such as:

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 here is for non-annotated classes that use @enableservice // Because 'AnnotationMetadata' is the annotation class attribute of 'Import', if the annotation class is annotated, Extends to non-annotation classes that apply this annotation class 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]; }}Copy the code

Add the @enableservice (name = “B”) annotation to ConfigA.

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

Run the main method again and output: ServiceB.

You can also implement the DeferredImportSelector interface so that the classes returned by selectImports are loaded last, rather than first, as with the @import annotation. Such as:

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]; }}Copy the code

Modify EnableService comments:

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

So ConfigA will take precedence over DefferredServiceImportSelector return ConfigB loading, execute the main method, output: ServiceA

4. Specify the implementation ImportBeanDefinitionRegistrar class for personalized loading

Like ImportSelector usage and use, but if we want to redefine the Bean, inject properties such as dynamic, change the type of Bean and Scope, etc., will be achieved by specifying ImportBeanDefinitionRegistrar class implements. Such as:

Define 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); }}Copy the code

Define ServiceImportBeanDefinitionRegistrar dynamically register ServiceC, modify 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) / / increase the structural parameters .addConstructorArgValue(name); / / registered Bean registry. RegisterBeanDefinition (" serviceC ", beanDefinitionBuilder getBeanDefinition ()); }}Copy the code

ImportBeanDefinitionRegistrar load after the @ Bean annotations, so to modify ConfigA removed which was @ ConditionalOnMissingBean annotations of Bean, Otherwise, the ServiceInterface for ConfigA must be generated

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

After that, run main and output: TestServiceC

Spring Boot core automatic loading principle

We mentioned the @enableAutoConfiguration annotation above:

@Import(AutoConfigurationImportSelector.class)
Copy the code

The third use of the @import annotation, which is loaded through a specific ImportSelector, implements the selectImports interface that returns the fully qualified name of the class to be loaded automatically. The AutoConfigurationImportSelector implementation: AutoConfigurationImportSelector

@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { / / ` spring. The boot. Enableautoconfiguration ` this attribute is not specified to false that enabled the spring boot automatically loaded, otherwise it is not enabled. If not enabled, return the empty array if (! isEnabled(annotationMetadata)) { return NO_IMPORTS; } / / get to load classes, see the source code below AutoConfigurationEntry AutoConfigurationEntry = getAutoConfigurationEntry (annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } / / get to load the class protected AutoConfigurationEntry getAutoConfigurationEntry (AnnotationMetadata AnnotationMetadata) { / / ` spring. The boot. Enableautoconfiguration ` this attribute is not specified to false that enabled the spring boot automatically loaded, otherwise it is not enabled. If not enabled, return the empty array if (! isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // AnnotationAttributes attributes = annotationMetadata; / / from spring. Factories reads all the key for org. Springframework. Boot. Autoconfigure. EnableAutoConfiguration class List < String > configurations = getCandidateConfigurations(annotationMetadata, attributes); // Delete duplicates = removeDuplicates(configurations); Set<String> Exclusions = getExclusions(annotationMetadata, Attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); / / release events AutoConfigurationImportEvent fireAutoConfigurationImportEvents (configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }Copy the code

What is the hierarchy of ApplicationContext in Spring Boot

ApplicationContext is the container that Spring uses to hold the management Beans and their lifecycle. The layering of the ApplicationContext specifies the bean boundaries and beans that can be reused. Refer to the official documentation for the ApplicationContext hierarchy. Here we use a simple example to illustrate the ApplicationContext hierarchy and its bean boundaries. For example, some beans can be shared by more than one ApplicationContext, and some beans are valid only in one ApplicationContext. Different ApplicationContexts can declare beans of the same name or type. We will implement an ApplicationContext structure as shown below:

We’re going to implement one parent context and three corresponding child contexts.

Define the Parent context first:

The Bean class:

package com.test.spring.context.bean;

import lombok.Data;
import lombok.NoArgsConstructor;

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

The Context class:

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; }}Copy the code

Root. Yml:

These configurations mainly expose the associated interfaces of the actuator. management: endpoint: health: show-details: always endpoints: jmx: exposure: exclude: '*' web: exposure: include: '*'Copy the code

Because we use the yml, here we need to customize a YamlPropertyLoaderFactory used to load yml configuration:

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);
    }
}
Copy the code

Public Bean class to define child Context:

package com.test.spring.context.bean;

import lombok.Data;
import lombok.NoArgsConstructor;

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

Define ChildContext1:

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; }}Copy the code

Bean – the config – 1. Yaml:

server:
  port: 8080
spring:
  application:
    name: child1
Copy the code

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: '*'
Copy the code

Test interface 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; }}Copy the code

Start the class:

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) Sibling (childContext2.class).sibling(childContext2.class).sibling(childContext3.class); ConfigurableApplicationContext applicationContext = appBuilder.run(); }}Copy the code

After startup, visit http://127.0.0.1:8080/test to return:

{"fatherBean":{"name":"root"},"name":"child1"}
Copy the code

Visit http://127.0.0.1:8081/test to return:

{"fatherBean":{"name":"root"},"name":"child2"}
Copy the code

Visit http://127.0.0.1:8082/test to return:

{"fatherBean":{"name":"root"},"name":"child3"}
Copy the code

Visit http://127.0.0.1:8080/actuator/beans there will be similar to the following return (omitted beans) you don’t care about:

{
	"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
		}
	}
}
Copy the code

This gives you a sense of the ApplicationContext hierarchy

Bean loading conditions

We will often see @conditional-related annotations, such as @conditionalonbean and @conditionalonClass, which provide the flexibility to load different classes according to certain conditions when loading automatically. The @conditional annotation is a feature provided by Spring-Context. Based on this annotation, Spring Boot provides more specific Conditional configuration annotations, including:

  • @ConditionalOnBeanIf the BeanFactory of the current ApplicationContext already contains these beans, the condition is met. On the contrary@ConditionalOnMissingBeanIf the BeanFactory of the current ApplicationContext does not contain these beans, the condition is met.
  • @ConditionalOnClassIf these classes are present in the current classpath, the condition is met. On the contrary@ConditionalOnMissingClassIf these classes are not currently in the classpath, the condition is met
  • @ConditionalOnProperty, specifies whether the attribute exists and the value satisfieshavingValueThe specified value (not set is notfalseLine),matchIfMissingRepresents whether the condition is met or not if the attribute does not exist.

More than a few annotations is commonly used, the remaining ConditionalOnCloudPlatform these less commonly used, for example, aside here.

If there are multiple similar @Conditional annotations on the same method or class, these loading conditions are “And” relationships.

Configuration Loading sequence

Due to the complexity of Bean loading conditions, we sometimes want some Configuration classes to be loaded first and some to be loaded after a particular Configuration is loaded. Such as:

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


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

If the two classes are in different jars and there is no way to determine which Service is created, we need to use some annotations to determine the order in which the Configuration is loaded. Note that the Configuration load order here is only the Bean definition load order, mainly to limit the order in which the Bean load criteria mentioned above are judged, not the order in which the Bean is created. The order in which beans are created is largely determined by Bean dependencies and limited by the @dependson annotation.

Relevant notes are as follows:

  • @AutoConfigureAfterSpecifies that the current Configuration is loaded after a Configuration.
  • @AutoConfigureBeforeSpecify that the current Configuration is loaded before a Configuration.
  • @AutoConfigureOrderSimilar to the@OrderSpecify the loading sequence number of the current Configuration. The default value is 0.

Bean sorting

For beans of the same type (beans that implement the same interface), we can use a List for automatic loading, for example:

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;
}
Copy the code

The private List

services will have serviceA and serviceB beans, but which one comes first and which one comes last? This can be specified via the @order annotation.

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

}
Copy the code

The smaller the value, the more forward it is.