- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章解决启用 Spring-Cloud-OpenFeign 配置可刷新项目无法启动的问题由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本篇文章涉及底层设计以及原理,以及问题定位,比较深入,篇幅较长,所以拆分成上下两篇:
最近在项目中想实现 OpenFeign 的配置可以动态刷新(主要是 Feign 的 Options 配置),例如:
1
2
3
4
5
6
7
8
|
feign:
client:
config:
default
:
# 链接超时
connectTimeout:
500
# 读取超时
readTimeout:
8000
|
我们可能会观察到调用某个 FeignClient 的超时时间不合理,需要临时修改下,我们不想因为这种事情重启进程或者刷新整个 ApplicationContext,所以将这部分配置放入 spring-cloud-config 中并使用动态刷新的机制进行刷新。官方提供了这个配置方法,参考:官方文档 - Spring @RefreshScope Support 。
即在项目中增加配置:
1
|
feign.client.refresh-enabled:
true
|
但是在我们的项目中,增加了这个配置后,启动失败,报找不到相关 Bean 的错误:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'feign.Request.Options-testService1Client' available at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:863) at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1344) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:309) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1160) at org.springframework.cloud.openfeign.FeignContext.getInstance(FeignContext.java:57) at org.springframework.cloud.openfeign.FeignClientFactoryBean.getOptionsByName(FeignClientFactoryBean.java:363) at org.springframework.cloud.openfeign.FeignClientFactoryBean.configureUsingConfiguration(FeignClientFactoryBean.java:195) at org.springframework.cloud.openfeign.FeignClientFactoryBean.configureFeign(FeignClientFactoryBean.java:158) at org.springframework.cloud.openfeign.FeignClientFactoryBean.feign(FeignClientFactoryBean.java:132) at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:382) at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:371) at org.springframework.cloud.openfeign.FeignClientsRegistrar.lambda$registerFeignClient$0(FeignClientsRegistrar.java:235) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1231) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1173) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ... 74 more 。
通过这个 Bean 名称,其实可以看出来这个 Bean 是我们开始提到要动态刷新的 Feign.Options,里面有连接超时、读取超时等配置。名字后面的部分是我们创建的 FeignClient 上面 @FeignClient 注解里面的 contextId.
在创建 FeignClient 的时候,需要加载这个 Feign.Options Bean,每个 FeignClient 都有自己的 ApplicationContext,这个 Feign.Options Bean 就是属于每个 FeignClient 单独的 ApplicationContext 的。这个是通过 Spring Cloud 的 NamedContextFactory 实现的。对于 NamedContextFactory 的深入分析,可以参考我的这篇文章:
对于 OpenFeign 的配置开启动态刷新,其实就是对于 FeignClient 就是要刷新每个 FeignClient 的 Feign.Options 这个 Bean。那么如何实现呢?我们先来看 spring-cloud 的动态刷新 Bean 的实现方式。首先我们要搞清楚,什么是 Scope.
从字面意思上面理解,Scope 即 Bean 的作用域。从实现上面理解,Scope 即我们在获取 Bean 的时候,这个 Bean 是如何获取的.
Spring 框架中自带两个耳熟能详的 Scope,即 singleton 和 prototype。singleton 即每次从 BeanFactory 获取一个 Bean 的时候(getBean),对于同一个 Bean 每次返回的都是同一个对象,即单例模式。prototype 即每次从 BeanFactory 获取一个 Bean 的时候,对于同一个 Bean 每次都新创建一个对象返回,即工厂模式.
同时,我们还可以根据自己需要去扩展 Scope,定义获取 Bean 的方式。举一个简单的例子,我们自定义一个 TestScope。自定义的 Scope 需要先定义一个实现 org.springframework.beans.factory.config.Scope 接口的类,来定义在这个 Scope 下的 Bean 的获取相关的操作.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
interface
Scope {
//获取这个 bean,在 BeanFactory.getBean 的时候会被调用
Object get(String name, ObjectFactory<?> objectFactory);
//在调用 BeanFactory.destroyScopedBean 的时候,会调用这个方法
@Nullable
Object remove(String name);
//注册 destroy 的 callback
//这个是可选实现,提供给外部注册销毁 bean 的回调。可以在 remove 的时候,执行这里传入的 callback。
void
registerDestructionCallback(String name, Runnable callback);
//如果一个 bean 不在 BeanFactory 中,而是根据上下文创建的,例如每个 http 请求创建一个独立的 bean,这样从 BeanFactory 中就拿不到了,就会从这里拿
//这个也是可选实现
Object resolveContextualObject(String key);
//可选实现,类似于 session id 用户区分不同上下文的
String getConversationId();
}
|
我们来实现一个简单的 Scope:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
static
class
TestScope
implements
Scope {
@Override
public
Object get(String name, ObjectFactory<?> objectFactory) {
return
objectFactory.getObject();
}
@Override
public
Object remove(String name) {
return
null
;
}
@Override
public
void
registerDestructionCallback(String name, Runnable callback) {
}
@Override
public
Object resolveContextualObject(String key) {
return
null
;
}
@Override
public
String getConversationId() {
return
null
;
}
}
|
这个 Scope 只是实现了 get 方法。直接通过传入的 objectFactory 创建一个新的 bean。这种 Scope 下每次调用 BeanFactory.getFactory 都会返回一个新的 Bean,自动装载到不同 Bean 的这种 Scope 下的 Bean 也是不同的实例。编写测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Configuration
public
static
class
Config {
@Bean
//自定义 Scope 的名字是 testScope
@org
.springframework.context.annotation.Scope(value =
"testScope"
)
public
A a() {
return
new
A();
}
//自动装载进来
@Autowired
private
A a;
}
public
static
class
A {
public
void
test() {
System.out.println(
this
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
static
void
main(String[] args) {
//创建一个 ApplicationContext
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new
AnnotationConfigApplicationContext();
//注册我们自定义的 Scope
annotationConfigApplicationContext.getBeanFactory().registerScope(
"testScope"
,
new
TestScope());
//注册我们需要的配置 Bean
annotationConfigApplicationContext.register(Config.
class
);
//调用 refresh 初始化 ApplicationContext
annotationConfigApplicationContext.refresh();
//获取 Config 这个 Bean
Config config = annotationConfigApplicationContext.getBean(Config.
class
);
//调用自动装载的 Bean
config.a.test();
//从 BeanFactory 调用 getBean 获取 A
annotationConfigApplicationContext.getBean(A.
class
).test();
annotationConfigApplicationContext.getBean(A.
class
).test();
}
|
执行代码,丛输出上可以看出,这三个 A 都是不同的对象:
1
2
3
|
com.hopegaming.spring.cloud.parent.ScopeTest$A
@5241cf67
com.hopegaming.spring.cloud.parent.ScopeTest$A
@716a7124
com.hopegaming.spring.cloud.parent.ScopeTest$A
@77192705
|
我们再来修改我们的 Bean,让它成为一个 Disposable Bean:
1
2
3
4
5
6
7
8
9
10
|
public
static
class
A
implements
DisposableBean {
public
void
test() {
System.out.println(
this
);
}
@Override
public
void
destroy()
throws
Exception {
System.out.println(
this
+
" is destroyed"
);
}
}
|
再修改下我们的自定义 Scope:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public
static
class
TestScope
implements
Scope {
private
Runnable callback;
@Override
public
Object get(String name, ObjectFactory<?> objectFactory) {
return
objectFactory.getObject();
}
@Override
public
Object remove(String name) {
System.out.println(name +
" is removed"
);
this
.callback.run();
System.out.println(
"callback finished"
);
return
null
;
}
@Override
public
void
registerDestructionCallback(String name, Runnable callback) {
System.out.println(
"registerDestructionCallback is called"
);
this
.callback = callback;
}
@Override
public
Object resolveContextualObject(String key) {
System.out.println(
"resolveContextualObject is called"
);
return
null
;
}
@Override
public
String getConversationId() {
System.out.println(
"getConversationId is called"
);
return
null
;
}
}
|
在测试代码中,增加调用 destroyScopedBean 销毁 bean:
1
|
annotationConfigApplicationContext.getBeanFactory().destroyScopedBean(
"a"
);
|
运行代码,可以看到对应的输出:
registerDestructionCallback is called a is removed com.hopegaming.spring.cloud.parent.ScopeTest$A@716a7124 is destroyed callback finished 。
对于 DisposableBean 或者其他有相关生命周期类型的 Bean,BeanFactory 会通过 registerDestructionCallback 将生命周期需要的操作回调传进来。使用 BeanFactory.destroyScopedBean 销毁 Bean 的时候,会调用 Scope 的 remove 方法,我们可以在操作完成时,调用 callback 回调完成 Bean 生命周期.
接下来我们尝试实现一种单例的 Scope,方式非常简单,主要基于 ConcurrentHashMap:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public
static
class
TestScope
implements
Scope {
private
final
ConcurrentHashMap<String, Object> map =
new
ConcurrentHashMap<>();
private
final
ConcurrentHashMap<String, Runnable> callback =
new
ConcurrentHashMap<>();
@Override
public
Object get(String name, ObjectFactory<?> objectFactory) {
System.out.println(
"get is called"
);
return
map.compute(name, (k, v) -> {
if
(v ==
null
) {
v = objectFactory.getObject();
}
return
v;
});
}
@Override
public
Object remove(String name) {
this
.map.remove(name);
System.out.println(name +
" is removed"
);
this
.callback.get(name).run();
System.out.println(
"callback finished"
);
return
null
;
}
@Override
public
void
registerDestructionCallback(String name, Runnable callback) {
System.out.println(
"registerDestructionCallback is called"
);
this
.callback.put(name, callback);
}
@Override
public
Object resolveContextualObject(String key) {
return
null
;
}
@Override
public
String getConversationId() {
return
null
;
}
}
|
我们使用两个 ConcurrentHashMap 缓存这个 Scope 下的 Bean,以及对应的 Destroy Callback。在这种实现下,就类似于单例模式的实现了。再使用下面的测试程序测试下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
static
void
main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new
AnnotationConfigApplicationContext();
annotationConfigApplicationContext.getBeanFactory().registerScope(
"testScope"
,
new
TestScope());
annotationConfigApplicationContext.register(Config.
class
);
annotationConfigApplicationContext.refresh();
Config config = annotationConfigApplicationContext.getBean(Config.
class
);
config.a.test();
annotationConfigApplicationContext.getBean(A.
class
).test();
//Config 类中注册 Bean 的方法名称为 a,所以 Bean 名称也为 a
annotationConfigApplicationContext.getBeanFactory().destroyScopedBean(
"a"
);
config.a.test();
annotationConfigApplicationContext.getBean(A.
class
).test();
}
|
我们在销毁 Bean 之前,使用自动装载和 BeanFactory.getBean 分别去请求获取 A 这个 Bean 并调用 test 方法。然后销毁这个 Bean。在这之后,再去使用自动装载的和 BeanFactory.getBean 分别去请求获取 A 这个 Bean 并调用 test 方法。可以从输出中看出, BeanFactory.getBean 请求的是新的 Bean 了,但是自动装载的里面还是已销毁的那个 bean。那么如何实现让自动装载的也是新的 Bean,也就是重新注入呢?
这就涉及到了 Scope 注解上面的另一个配置,即指定代理模式:
1
2
3
4
5
6
7
8
9
10
|
@Target
({ElementType.TYPE, ElementType.METHOD})
@Retention
(RetentionPolicy.RUNTIME)
@Documented
public
@interface
Scope {
@AliasFor
(
"scopeName"
)
String value()
default
""
;
@AliasFor
(
"value"
)
String scopeName()
default
""
;
ScopedProxyMode proxyMode()
default
ScopedProxyMode.DEFAULT;
}
|
其中第三个配置,ScopedProxyMode 是配置获取这个 Bean 的时候,获取的是原始 Bean 对象还是代理的 Bean 对象(这也同时影响了自动装载):
1
2
3
4
5
6
7
8
9
10
|
public
enum
ScopedProxyMode {
//走默认配置,没有其他外围配置则是 NO
DEFAULT,
//使用原始对象作为 Bean
NO,
//使用 JDK 的动态代理
INTERFACES,
//使用 CGLIB 动态代理
TARGET_CLASS
}
|
我们来测试下指定 Scope Bean 的实际对象为代理的效果,我们修改下上面的测试代码,使用 CGLIB 动态代理。修改代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Configuration
public
static
class
Config {
@Bean
@org
.springframework.context.annotation.Scope(value =
"testScope"
//指定代理模式为基于 CGLIB
, proxyMode = ScopedProxyMode.TARGET_CLASS
)
public
A a() {
return
new
A();
}
@Autowired
private
A a;
}
|
编写测试主方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
static
void
main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new
AnnotationConfigApplicationContext();
annotationConfigApplicationContext.getBeanFactory().registerScope(
"testScope"
,
new
TestScope());
annotationConfigApplicationContext.register(Config.
class
);
annotationConfigApplicationContext.refresh();
Config config = annotationConfigApplicationContext.getBean(Config.
class
);
config.a.test();
annotationConfigApplicationContext.getBean(A.
class
).test();
//查看 Bean 实例的类型
System.out.println(config.a.getClass());
System.out.println(annotationConfigApplicationContext.getBean(A.
class
).getClass());
//这时候我们需要注意,代理 Bean 的名称有所变化,需要通过 ScopedProxyUtils 获取
annotationConfigApplicationContext.getBeanFactory().destroyScopedBean(ScopedProxyUtils.getTargetBeanName(
"a"
));
config.a.test();
annotationConfigApplicationContext.getBean(A.
class
).test();
}
|
执行程序,输出为:
get is called registerDestructionCallback is called com.hopegaming.spring.cloud.parent.ScopeTest$A@3dd69f5a get is called com.hopegaming.spring.cloud.parent.ScopeTest$A@3dd69f5a class com.hopegaming.spring.cloud.parent.ScopeTest$A$$EnhancerBySpringCGLIB$$2fa625ee class com.hopegaming.spring.cloud.parent.ScopeTest$A$$EnhancerBySpringCGLIB$$2fa625ee scopedTarget.a is removed com.hopegaming.spring.cloud.parent.ScopeTest$A@3dd69f5a is destroyed callback finished get is called registerDestructionCallback is called com.hopegaming.spring.cloud.parent.ScopeTest$A@3aa3193a get is called com.hopegaming.spring.cloud.parent.ScopeTest$A@3aa3193a 。
从输出中可以看出:
那么 Scope 是如何实现这些的呢?我们接下来简单分析下源码 。
如果一个 Bean 没有声明任何 Scope,那么他的 Scope 就会被赋值成 singleton,也就是默认的 Bean 都是单例的。这个对应 BeanFactory 注册 Bean 之前需要生成 Bean 定义,在 Bean 定义的时候会赋上这个默认值,对应源码:
AbstractBeanFactory 。
1
2
3
4
5
6
7
8
9
|
protected
RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd,
@Nullable
BeanDefinition containingBd)
throws
BeanDefinitionStoreException {
//省略我们不关心的源码
if
(!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(SCOPE_SINGLETON);
}
//省略我们不关心的源码
}
|
在声明一个 Bean 具有特殊 Scope 之前,我们需要定义这个自定义 Scope 并把它注册到 BeanFactory 中。这个 Scope 名称必须全局唯一,因为之后区分不同 Scope 就是通过这个名字进行区分的。注册 Scope 对应源码:
AbstractBeanFactory 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Override
public
void
registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName,
"Scope identifier must not be null"
);
Assert.notNull(scope,
"Scope must not be null"
);
//不能为 singleton 和 prototype 这两个预设的 scope
if
(SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
throw
new
IllegalArgumentException(
"Cannot replace existing scopes 'singleton' and 'prototype'"
);
}
//放入 scopes 这个 map 中,key 为名称,value 为自定义 Scope
Scope previous =
this
.scopes.put(scopeName, scope);
//可以看出,后面放入的会替换前面的,这个我们要尽量避免出现。
if
(previous !=
null
&& previous != scope) {
if
(logger.isDebugEnabled()) {
logger.debug(
"Replacing scope '"
+ scopeName +
"' from ["
+ previous +
"] to ["
+ scope +
"]"
);
}
}
else
{
if
(logger.isTraceEnabled()) {
logger.trace(
"Registering scope '"
+ scopeName +
"' with implementation ["
+ scope +
"]"
);
}
}
}
|
当声明一个 Bean 具有特殊的 Scope 之后,获取这个 Bean 的时候,就会有特殊的逻辑,参考通过 BeanFactory 获取 Bean 的核心源码代码:
AbstractBeanFactory 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
@SuppressWarnings
(
"unchecked"
)
protected
<T> T doGetBean(
String name,
@Nullable
Class<T> requiredType,
@Nullable
Object[] args,
boolean
typeCheckOnly)
throws
BeansException {
//省略我们不关心的源码
// 创建 Bean 实例
if
(mbd.isSingleton()) {
//创建或者返回单例实例
}
else
if
(mbd.isPrototype()) {
//每次创建一个新实例
}
else
{
//走到这里代表这个 Bean 属于自定义 Scope
String scopeName = mbd.getScope();
//必须有 Scope 名称
if
(!StringUtils.hasLength(scopeName)) {
throw
new
IllegalStateException(
"No scope name defined for bean ´"
+ beanName +
"'"
);
}
//通过 Scope 名称获取对应的 Scope,自定义 Scope 需要手动注册进来
Scope scope =
this
.scopes.get(scopeName);
if
(scope ==
null
) {
throw
new
IllegalStateException(
"No Scope registered for scope name '"
+ scopeName +
"'"
);
}
try
{
//调用自定义 Scope 的 get 方法获取 Bean
Object scopedInstance = scope.get(beanName, () -> {
//同时将创建 Bean 需要的生命周期的回调传入,用于创建 Bean
beforePrototypeCreation(beanName);
try
{
return
createBean(beanName, mbd, args);
}
finally
{
afterPrototypeCreation(beanName);
}
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch
(IllegalStateException ex) {
throw
new
ScopeNotActiveException(beanName, scopeName, ex);
}
}
//省略我们不关心的源码
}
|
同时,如果我们定义 Scope Bean 的代理方式为 CGLIB,那么在获取 Bean 定义的时候,就会根据原始 Bean 定义创建 Scope 代理的 Bean 定义,对应源码:
ScopedProxyUtils 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public
static
BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry,
boolean
proxyTargetClass) {
//原始目标 Bean 名称
String originalBeanName = definition.getBeanName();
//获取原始目标 Bean 定义
BeanDefinition targetDefinition = definition.getBeanDefinition();
//获取代理 Bean 名称
String targetBeanName = getTargetBeanName(originalBeanName);
//创建类型为 ScopedProxyFactoryBean 的 Bean
RootBeanDefinition proxyDefinition =
new
RootBeanDefinition(ScopedProxyFactoryBean.
class
);
//根据原始目标 Bean 定义的属性,配置代理 Bean 定义的相关属性,省略这部分源码
//根据原始目标 Bean 的自动装载属性,复制到代理 Bean 定义
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if
(targetDefinition
instanceof
AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
//设置原始 Bean 定义为不自动装载并且不为 Primary
//这样通过 BeanFactory 获取 Bean 以及自动装载的都是代理 Bean 而不是原始目标 Bean
targetDefinition.setAutowireCandidate(
false
);
targetDefinition.setPrimary(
false
);
//使用新名称注册 Bean
registry.registerBeanDefinition(targetBeanName, targetDefinition);
return
new
BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
private
static
final
String TARGET_NAME_PREFIX =
"scopedTarget."
;
//这个就是获取代理 Bean 名称的工具方法,我们上面 Destroy Bean 的时候也有用到
public
static
String getTargetBeanName(String originalBeanName) {
return
TARGET_NAME_PREFIX + originalBeanName;
}
|
这个代理 Bean 有啥作用呢?其实主要用处就是每次调用 Bean 的任何方法的时候,都会通过 BeanFactory 获取这个 Bean 进行调用。参考源码:
代理类 ScopedProxyFactoryBean 。
1
2
3
4
5
6
|
public
class
ScopedProxyFactoryBean
extends
ProxyConfig
implements
FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {
private
final
SimpleBeanTargetSource scopedTargetSource =
new
SimpleBeanTargetSource();
//这个就是通过 SimpleBeanTargetSource 生成的实际代理,对于 Bean 的方法调用都会通过这个 proxy 进行调用
private
Object proxy;
}
|
SimpleBeanTargetSource 就是实际的代理源,他的实现非常简单,核心方法就是使用 Bean 名称通过 BeanFactory 获取这个 Bean:
1
2
3
4
5
6
|
public
class
SimpleBeanTargetSource
extends
AbstractBeanFactoryBasedTargetSource {
@Override
public
Object getTarget()
throws
Exception {
return
getBeanFactory().getBean(getTargetBeanName());
}
}
|
通过 BeanFactory 获取这个 Bean,通过上面源码分析可以知道,对于自定义 Scope 的 Bean 就会调用自定义 Scope 的 get 方法.
然后是 Bean 的销毁,在 BeanFactory 创建这个 Bean 对象的时候,就会调用自定义 Scope 的 registerDestructionCallback 将 Bean 销毁的回调传入:
AbstractBeanFactory 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
protected
void
registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() !=
null
? getAccessControlContext() :
null
);
if
(!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if
(mbd.isSingleton()) {
//对于 singleton
registerDisposableBean(beanName,
new
DisposableBeanAdapter(
bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
}
else
{
//对于自定义 Scope
Scope scope =
this
.scopes.get(mbd.getScope());
if
(scope ==
null
) {
throw
new
IllegalStateException(
"No Scope registered for scope name '"
+ mbd.getScope() +
"'"
);
}
//调用 registerDestructionCallback
scope.registerDestructionCallback(beanName,
new
DisposableBeanAdapter(
bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
}
}
}
|
在我们想销毁 Scope Bean 的时候,需要调用的是 BeanFactory 的 destroyScopedBean 方法,这个方法会调用自定义 Scope 的 remove:
AbstractBeanFactory 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@Override
public
void
destroyScopedBean(String beanName) {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//仅针对自定义 Scope Bean 使用
if
(mbd.isSingleton() || mbd.isPrototype()) {
throw
new
IllegalArgumentException(
"Bean name '"
+ beanName +
"' does not correspond to an object in a mutable scope"
);
}
String scopeName = mbd.getScope();
Scope scope =
this
.scopes.get(scopeName);
if
(scope ==
null
) {
throw
new
IllegalStateException(
"No Scope SPI registered for scope name '"
+ scopeName +
"'"
);
}
//调用自定义 Scope 的 remove 方法
Object bean = scope.remove(beanName);
if
(bean !=
null
) {
destroyBean(beanName, bean, mbd);
}
}
|
到此这篇关于解决启用 Spring-Cloud-OpenFeign 配置可刷新项目无法启动的问题的文章就介绍到这了,更多相关Spring-Cloud-OpenFeign 配置内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/zhxdick/p/15359268.html 。
最后此篇关于解决启用 Spring-Cloud-OpenFeign 配置可刷新项目无法启动的问题的文章就讲到这里了,如果你想了解更多关于解决启用 Spring-Cloud-OpenFeign 配置可刷新项目无法启动的问题的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我只是不喜欢 Logback 的 XML 或 Groovy 配置,而更喜欢用 Java 进行配置(这也是因为我将在初始化后的不同时间在运行时更改配置)。 似乎对 Logback 进行 Java 配置的
我的 sphinx 配置是: ================================ config/sphinx.yml development: bin_path: "/usr/loc
我们计划在生产服务器中部署我们的系统。我有兴趣了解更多有关优化网站性能的信息。 Sitecore 有哪些优化建议? (缓存,网络配置中的其他设置) 我们可以在 IIS 中做哪些优化? 找不到关于这些主
我有一个 Django 应用程序,可以处理网站的两个(或更多)部分,例如网站的“admin”和“api”部分。我还为网站的其余部分提供了普通的 html 页面,其中不需要 Django。 例如,我希望
我刚刚开始研究Docker。我有一个 Node 应用程序,可以调整大小和图像,然后在完成后向 aws 发送 SQS 消息。我已成功创建应用程序的 docker 镜像,并从本地计算机复制它,但遇到了无法
如何配置 checkstyle(在 Ant nt Maven 中)任务?我尝试了一点,但没有正确收到报告。这是我的 Ant 脚本。
我正在使用 Quartz 和 Spring 框架重写一个遗留项目。原始配置是 XML 格式,现在我将其转换为 Java Config。 xml 配置使用 jobDetail 设置触发器 bean 的作
tl;rd: 使用主键对数据库进行分区 索引大小问题。 数据库大小每天增长约 1-3 GB 突袭设置。 您有使用 Hypertable 的经验吗? 长版: 我刚刚建立/购买了一个家庭服务器: 至强 E
在安装 gcp 应用程序后,我们尝试使用 GCP 的图形 api 配置 Azure Active Directory saml 配置。我们正在遵循相同的 AWS graph api saml 设置 U
我刚刚了解了 spring security 并想使用 java hibernate 配置连接到数据库,但我发现的示例或教程很少。我通过使用 xml 配置找到了更多。我在这里使用 Spring 4.0
我们最近切换到 Java 8 以使用 java.time API(LocalDate、LocalDateTime,...)。因此,我们将 Hibernate 依赖项更新到版本 4.3.10。我们编写了
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是《quarkus实战》系列的第六篇,咱
我是 NGINX 的新手,我正在尝试对我们的 ERP 网络服务器进行负载平衡。我有 3 个网络服务器在由 websphere 提供支持的端口 80 上运行,这对我来说是一个黑盒子: * web01.e
我们想使用 gerrit 进行代码审查,但我们在 webview 中缺少一些设置。 是否可以禁止提交者审查/验证他们自己的 提交? 是否有可能两个审稿人给 +1 一个累积它 到+2,以便可以提交? 谢
配置根据运行模式应用于 AEM 实例。在多个运行模式和多个配置的情况下,AEM 如何确定要选择的配置文件?假设以下配置在 AEM 项目中可用, /apps /myproject - con
我正在使用 Neo4j 服务器。我遇到了负载相对较低的问题。但是,响应时间相当长。我认为为请求提供服务的线程数太少了。有没有办法调整为 HTTP 请求提供服务的线程池的大小。那可能吗? 最佳答案 线程
我在/etc/default/celeryd 中有以下配置 CELERYD_NODES = "worker1 worker2 worker3" CELERYD_CHDIR = "path to pro
Plone 在其页面中显示来 self 的母语(巴西葡萄牙语)的特殊字符。但是,当我使用我创建的 spt 页面时,它会显示转义序列,例如: Educa\xc3\xa7\xc3\xa3o 代替 Educ
我正在尝试开始使用 Emacs/Clojure。安装 emacs 扩展的正确方法是什么。我正在尝试安装以下插件: https://bitbucket.org/kotarak/vimclojure 我已
我有一个简单的 C 项目结构: proj/ src/ docs/ build/ tests/ lib/ 尝试编写合适的 CMake 文件。 到目前为止我的尝试:http://pas
我是一名优秀的程序员,十分优秀!