本文基于 Spring Cloud 2020.0 发布版的依赖
本系列会深入分析 Spring Cloud 的每一个组件,从Spring Cloud Commons
这个 Spring Cloud 所有元素的抽象说起,深入设计思路与源码,并结合实际使用例子深入理解。本系列适合有一定 Spring 或者 Spring Boot 使用经验的人阅读。
Spring Cloud Commons
Spring Cloud框架包括如下功能:
Spring Cloud Commons包含实现这一切要加载的基础组件的接口,以及Spring Cloud启动如何加载,加载哪些东西。其中:
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 将会在另一个单独的系列。
我们在看一个 Spring Cloud 模块源代码时,需要记住任何一个 Spring Cloud 模块都是基于 Spring Boot 扩展而来的,这个扩展一般是通过 spring.factories SPI 机制。任何一个 Spring Cloud 模块源代码都可以以这个为切入点进行理解
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();
}
获取指定的实现类。
在 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;
}
值越小,越靠前。
Spring Cloud系列之Commons - 1. 背景与基础知识准备
原文:https://www.cnblogs.com/zhxdick/p/14347916.html