本文基于 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; }
值越小,越靠前。