本文基於 Spring Cloud 2020.0 發佈版的依賴html
本系列會深刻分析 Spring Cloud 的每個組件,從Spring Cloud Commons
這個 Spring Cloud 全部元素的抽象提及,深刻設計思路與源碼,並結合實際使用例子深刻理解。本系列適合有必定 Spring 或者 Spring Boot 使用經驗的人閱讀。java
Spring Cloud Commons
Spring Cloud框架包括以下功能:git
Spring Cloud Commons包含實現這一切要加載的基礎組件的接口,以及Spring Cloud啓動如何加載,加載哪些東西。其中:github
ApplicationContext
的內容org.springframework.cloud.serviceregistry
包org.springframework.cloud.discovery
包org.springframework.cloud.loadbalancer
包org.springframework.cloud.circuitbreaker
包這個系列咱們要講述的是 spring cloud common 這個模塊,spring cloud loadbalancer 還有 spring cloud context 將會在另外一個單獨的系列。web
咱們在看一個 Spring Cloud 模塊源代碼時,須要記住任何一個 Spring Cloud 模塊都是基於 Spring Boot 擴展而來的,這個擴展通常是經過 spring.factories SPI 機制。任何一個 Spring Cloud 模塊源代碼均可以以這個爲切入點進行理解spring
spring-core
項目中提供了 Spring 框架多種 SPI 機制,其中一種很是經常使用並靈活運用在了 Spring-boot 的機制就是基於 spring.factories
的 SPI 機制。編程
那麼什麼是 SPI(Service Provider)呢? 在系統設計中,爲了模塊間的協做,每每會設計統一的接口供模塊之間的調用。面向的對象的設計裏,咱們通常推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼,而是將指定哪一個實現置於程序以外指定。Java 中默認的 SPI 機制就是經過 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(); }
獲取指定的實現類。app
在 Spring 框架中,這個類是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)); }
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文件的功能,固然在這裏是組織多個分散的@Configuration
的類。這個註解的功能與用法包括
@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
的優先於自己的的類定義加載。
Bean
在Spring 4.2以後,@Import
能夠直接指定實體類,加載這個類定義到context
中。
例如把上面代碼中的ConfigA
的@Import
修改成@Import(ServiceB.class)
,就會生成ServiceB
的Bean
到容器上下文中,以後運行main
方法,輸出爲:ServiceB
.證實@Import
的優先於自己的的類定義加載.
ImportSelector
(以及DefferredServiceImportSelector
)的類,用於個性化加載指定實現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
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
上面咱們提到了@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 用來容納管理 beans 以及其生命週期的容器。ApplicationContext 的分層規定了bean的界限以及能夠複用的 bean。關於 ApplicationContext 層級能夠參考官方文檔,這裏咱們經過一個簡單的例子來講明下 ApplicationContext 層級以及其中的bean界限,例如某些 bean 能夠被多個 ApplicationContext 共享,同時某些 bean 只在某個 ApplicationContext 生效,不一樣 ApplicationContext 能夠聲明同名或者同類型的bean這樣。咱們將實現一個下圖所示的 ApplicationContext 結構:
咱們會實現,一個 parent context 與三個對應 child context 的結構。
首先定義Parent context:
Bean類:
package com.test.spring.context.bean; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor public class RootBean { private Stirng name; }
Context類:
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); } }
定義child context的公共Bean類:
package com.test.spring.context.bean; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor public class ChildBean { private RootBean fatherBean; private String name; }
定義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; } }
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
會有相似於下面的返回(省略了不關心的bean):
{ "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
,若是當前 ApplicationContext 的 BeanFactory 已經包含這些 Bean,則知足條件。與之相反的是 @ConditionalOnMissingBean
,若是當前 ApplicationContext 的 BeanFactory 不包含這些 Bean,則知足條件。@ConditionalOnClass
,若是當前 classpath 中有這些類,則知足條件。與之相反的是@ConditionalOnMissingClass
,若是當前 classpath 中沒有這些類,則知足條件@ConditionalOnProperty
,指定屬性是否存在,而且值知足havingValue
指定的值(沒設置就是不爲false
就行),matchIfMissing
表明若是屬性不存在表明條件知足仍是不知足。以上幾個註解是比較經常使用的,剩下的例如ConditionalOnCloudPlatform
這些不太經常使用,這裏先不提了。
若是有多個相似的@Conditional
註解做用於同一個方法或者類,這些加載條件是「And」的關係。
因爲 Bean 加載條件的複雜性,有時候咱們想某些 Configuration 類先加載,某些在特定的 Configuration 加載完以後再加載。例如:
@Configuration public class FirstConfiguration { @Bean @ConditionalOnMissingBean public Service service1() { ...... } }
@Configuration public class SecondConfiguration { @Bean @ConditionalOnMissingBean public Service service1() { ...... } }
假設這兩個類在不一樣 jar 包,咱們沒有辦法肯定最後建立的是哪個類的 Service
,這時候咱們就須要用到一些決定 Configuration 加載順序的註解。注意這裏的 Configuration 加載順序僅僅是 Bean 定義加載順序,主要是爲了限制上面提到的 Bean 加載條件的判斷順序,而不是建立 Bean 的順序。Bean 建立的順序主要由 Bean 依賴決定以及@DependsOn
註解限制。
相關的註解以下:
@AutoConfigureAfter
指定當前 Configuration 在 某個 Configuration 以後加載。@AutoConfigureBefore
指定當前 Configuration 在 某個 Configuration 以前加載。@AutoConfigureOrder
相似於@Order
註解,指定當前 Configuration 的加載序號,默認是 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
這兩個 Bean,可是誰在前誰在後呢?能夠經過@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; }
值越小,越靠前。