1. configserver 项目
通过 https://start.spring.io/ 新建 configserver 项目。
需要导入如下依赖,可以在上述网址中选择依赖组件。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
在主类中新增 @EnableConfigServer
注解。
@EnableConfigServer
@SpringBootApplication
public class ConfigserverApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigserverApplication.class, args);
}
}
使用本地文件存储配置文件的方式,在 application.properties
文件中新增如下配置。
spring.application.name=configserver
server.port=8888
spring.profiles.active=native
spring.cloud.config.server.native.search-locations=classpath:/config
在 resources
目录下新增 config
子目录,并创建 application-dev.yml
和 application-test.yml
文件,文件内容如下所示。
// application-dev.yml
name: "dev-config"
// application-test.yml
name: "test-config"
configserver 项目整体结构如图1 所示。
运行 configserver 项目。
2. config-client-demo 项目
同样可以通过 https://start.spring.io/ 新建 config-client-demo 项目。
需要导入如下依赖,可以在上述网址中选择依赖组件。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
在 application.properties
文件中新增如下配置。
# 开启所有 Actuator Endpoint
management.endpoints.web.exposure.include=*
新增 bootstrap.yml
文件,并加入 Spring Cloud Config 相关配置。
spring:
cloud:
config:
uri: http://localhost:8888 # configserver 地址
# name-profile 组合对应 configserver 中具体的配置文件,这里映射的是 application-dev.yml
name: application
profile: dev
新增 ConfigProperties
类存储对应的配置,并加上 @RefreshScope
用来刷新配置。
@Data
@Component
@RefreshScope
public class ConfigProperties {
@Value("${name}")
private String name;
}
新增 TestConfigController
用来提供测试接口。
@RestController
@RequiredArgsConstructor
@RequestMapping("/test/config")
public class TestConfigController {
@Autowired
private ConfigProperties configProperties;
@Value("${name}")
private String name;
@GetMapping("/name")
public String name() {
System.out.println("------------------------");
System.out.println(name);
System.out.println(configProperties.getName());
System.out.println("------------------------");
return name;
}
}
config-client-demo 项目整体结构如图3 所示。
调用 http://localhost:8080/test/config/name
接口,打印结果如下所示。
3. 修改配置
修改 configserver 中的 application-dev.yml
配置,并重启 configserver 服务。
name: "dev-config-update"
在 config-client-demo 中调用 http://localhost:8080/actuator/refresh
接口用来刷新配置,然后调用 http://localhost:8080/test/config/name
接口,打印结果如下所示。
可以发现,有 @RefreshScope
注解的配置被刷新了,否则配置没有被刷新。
4. 源码解读
4.1 refresh 接口
下面我们从 RefreshEndpoint
类开始跟踪。
@Endpoint(id = "refresh")
public class RefreshEndpoint {
private ContextRefresher contextRefresher;
// http://localhost:8080/actuator/refresh 接口执行逻辑
public Collection<String> refresh() {
Set<String> keys = this.contextRefresher.refresh();
return keys;
}
}
后续调用链路为:
- ①
ContextRefresher
的refresh()
- ②
RefreshScope
的refreshAll()
- ③
GenericScope
的destroy()
4.2 GenericScope 类
public class GenericScope implements Scope, BeanFactoryPostProcessor,
BeanDefinitionRegistryPostProcessor, DisposableBean {
/**
* Prefix for the scoped target.
*/
public static final String SCOPED_TARGET_PREFIX = "scopedTarget.";
private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
new StandardScopeCache());
// 清空 Scope 中的缓存对象,这里指 RefreshScope
public void destroy() {
Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
for (BeanLifecycleWrapper wrapper : wrappers) {
wrapper.destroy();
}
}
// 获取 name 在 Scope 中的 Spring Bean
public Object get(String name, ObjectFactory<?> objectFactory) {
// 底层调用的是 putIfAbsent 方法
// 如果 cache 存在对应的 name 的 BeanLifecycleWrapper 则直接返回,否则添加
BeanLifecycleWrapper value = this.cache.put(name,
new BeanLifecycleWrapper(name, objectFactory));
try {
return value.getBean();
}
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
for (String name : registry.getBeanDefinitionNames()) {
BeanDefinition definition = registry.getBeanDefinition(name);
if (definition instanceof RootBeanDefinition) {
RootBeanDefinition root = (RootBeanDefinition) definition;
if (root.getDecoratedDefinition() != null && root.hasBeanClass()
&& root.getBeanClass() == ScopedProxyFactoryBean.class) {
if (getName().equals(root.getDecoratedDefinition().getBeanDefinition()
.getScope())) {
// 把 ScopedProxyFactoryBean 替换为 LockedScopedProxyFactoryBean
root.setBeanClass(LockedScopedProxyFactoryBean.class);
root.getConstructorArgumentValues().addGenericArgumentValue(this);
root.setSynthetic(true);
}
}
}
}
}
private static class BeanLifecycleWrapper {
private final String name;
private final ObjectFactory<?> objectFactory;
private Object bean;
// 通过 objectFactory 获取 Spring Bean 对象,并缓存在 bean 字段中
public Object getBean() {
if (this.bean == null) {
synchronized (this.name) {
if (this.bean == null) {
this.bean = this.objectFactory.getObject();
}
}
}
return this.bean;
}
}
}
4.2 ScopedProxyUtils 类
public abstract class ScopedProxyUtils {
private static final String TARGET_NAME_PREFIX = "scopedTarget.";
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
// 为 originalBeanName 创建一个 scoped proxy 的 RootBeanDefinition
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
// 给 ScopedProxyFactoryBean 中的 targetBeanName 字段赋值
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
// Copy autowire settings from original bean definition.
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
// The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
// targetBeanName 关联目标 bean,注册到 Spring 容器中
registry.registerBeanDefinition(targetBeanName, targetDefinition);
// 返回 originalBeanName 关联的 scoped proxy definition
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
/**
* 生成目标bean的名称
*
* originalBeanName 是 scoped proxy 的名称(代理类)
* targetBeanName 是 scoped proxy 持有的目标bean 的名称(被代理类)
*/
//
public static String getTargetBeanName(String originalBeanName) {
return TARGET_NAME_PREFIX + originalBeanName;
}
}
生成 scoped proxy
的调用链路:
- ①
AbstractApplicationContext
类中的refresh()
方法 - ②
AbstractApplicationContext
类中的invokeBeanFactoryPostProcessors()
方法 - ③
PostProcessorRegistrationDelegate
类中的invokeBeanFactoryPostProcessors()
方法 - ④
PostProcessorRegistrationDelegate
类中的invokeBeanDefinitionRegistryPostProcessors()
方法 - ⑤
ConfigurationClassPostProcessor
类中的postProcessBeanDefinitionRegistry()
方法 - ⑥
ConfigurationClassPostProcessor
类中的processConfigBeanDefinitions()
方法 - ⑦
ConfigurationClassParser
类中的parse()
方法 - ⑧
ConfigurationClassParser
类中的processConfigurationClass()
方法 - ⑨
ComponentScanAnnotationParser
类中的parse()
方法 - ⑩
ClassPathBeanDefinitionScanner
类中的doScan()
方法 - ⑾
AnnotationConfigUtils
类中的applyScopedProxyMode()
方法 - ⑿
ScopedProxyCreator
类中的createScopedProxy()
方法 - ⒀
ScopedProxyUtils
类中的createScopedProxy()
方法
4.3 ScopedProxyFactoryBean 类
public class ScopedProxyFactoryBean extends ProxyConfig
implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {
// 从 Spring 容器中通过 getBean() 方法获取 TargetSource
private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();
// ScopedProxyUtils 中的 getTargetBeanName() 方法生成
private String targetBeanName;
// 代理对象的缓存
private Object proxy;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
this.scopedTargetSource.setBeanFactory(beanFactory);
// 通过 ProxyFactory 生成代理对象
ProxyFactory pf = new ProxyFactory();
pf.copyFrom(this);
// 设置代理对象的 TargetSource 为 scopedTargetSource (SimpleBeanTargetSource)
pf.setTargetSource(this.scopedTargetSource);
Class<?> beanType = beanFactory.getType(this.targetBeanName);
if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
}
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
pf.addInterface(AopInfrastructureBean.class);
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}
public Object getObject() {
if (this.proxy == null) {
throw new FactoryBeanNotInitializedException();
}
return this.proxy;
}
}
public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {
// 在代理方法中获取目标对象
public Object getTarget() throws Exception {
// 直接从 Spring 容器中获取
return getBeanFactory().getBean(getTargetBeanName());
}
}
LockedScopedProxyFactoryBean
是 GenericScope
的内部类。
public static class LockedScopedProxyFactoryBean<S extends GenericScope>
extends ScopedProxyFactoryBean implements MethodInterceptor {
private final S scope;
private String targetBeanName;
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
Object proxy = getObject();
if (proxy instanceof Advised) {
// 把当前 MethodInterceptor 放到 Advice 的首位
// 即让代理方法执行时,先执行此 MethodInterceptor 的 invoke 方法
Advised advised = (Advised) proxy;
advised.addAdvice(0, this);
}
}
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
if (AopUtils.isEqualsMethod(method) || AopUtils.isToStringMethod(method)
|| AopUtils.isHashCodeMethod(method)
|| isScopedObjectGetTargetObject(method)) {
return invocation.proceed();
}
Object proxy = getObject();
try {
if (proxy instanceof Advised) {
Advised advised = (Advised) proxy;
ReflectionUtils.makeAccessible(method);
return ReflectionUtils.invokeMethod(method,
advised.getTargetSource().getTarget(), // 调用 SimpleBeanTargetSource 的 getTarget() 从 Spring 容器获取 Bean
invocation.getArguments());
}
return invocation.proceed();
}
}
}
5. 配置刷新分析
再次贴出 TestConfigController
的代码。
@RestController
@RequiredArgsConstructor
@RequestMapping("/test/config")
public class TestConfigController {
@Autowired
private ConfigProperties configProperties;
@Value("${name}")
private String name;
@GetMapping("/name")
public String name() {
System.out.println("------------------------");
System.out.println(name);
System.out.println(configProperties.getName());
System.out.println("------------------------");
return name;
}
}
debug 结果如下所示。
从上图可知,configProperties
是一个 CglibAopProxy
代理类,因此调用其 getName()
方法会进入 DynamicAdvisedInterceptor
的 intercept()
方法。
调用链路如下所示:
- ①
DynamicAdvisedInterceptor
类的intercept()
方法。 - ②
LockedScopedProxyFactoryBean
类的invoke()
方法。 - ③
AbstractBeanFactory
类的getBean()
方法,通过ScopedProxyUtils
中的getTargetBeanName()
方法生成的name
。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
// 获取到 RefreshScope
final Scope scope = this.scopes.get(scopeName);
try {
// 调用 RefreshScope 父类 GenericScope 的 get() 方法
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
}
return (T) bean;
}
}
通过 4.1 refresh 接口 可以知道在调用 /refresh 接口后,会触发 GenericScope
的 destroy()
逻辑,从而清空缓存。
之后, GenericScope
的 get()
方法会重新通过 objectFactory
即 createBean()
生成新的对象。
网友评论