欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解決啟用 Spring-Cloud-OpenFeign 配置可刷新項(xiàng)目無(wú)法啟動(dòng)的問題

 更新時(shí)間:2021年10月02日 08:31:48   作者:干貨滿滿張哈希  
這篇文章主要介紹了解決啟用 Spring-Cloud-OpenFeign 配置可刷新項(xiàng)目無(wú)法啟動(dòng)的問題,本文重點(diǎn)給大家介紹Spring-Cloud-OpenFeign的原理及問題解決方法,需要的朋友可以參考下

本篇文章涉及底層設(shè)計(jì)以及原理,以及問題定位,比較深入,篇幅較長(zhǎng),所以拆分成上下兩篇:

  • 上:?jiǎn)栴}簡(jiǎn)單描述以及 Spring Cloud RefreshScope 的原理
  • 下:當(dāng)前 spring-cloud-openfeign + spring-cloud-sleuth 帶來(lái)的 bug 以及如何修復(fù)

最近在項(xiàng)目中想實(shí)現(xiàn) OpenFeign 的配置可以動(dòng)態(tài)刷新(主要是 Feign 的 Options 配置),例如:

feign:
    client:
     config:
       default:
         # 鏈接超時(shí)
         connectTimeout: 500
         # 讀取超時(shí)
         readTimeout: 8000

我們可能會(huì)觀察到調(diào)用某個(gè) FeignClient 的超時(shí)時(shí)間不合理,需要臨時(shí)修改下,我們不想因?yàn)檫@種事情重啟進(jìn)程或者刷新整個(gè) ApplicationContext,所以將這部分配置放入 spring-cloud-config 中并使用動(dòng)態(tài)刷新的機(jī)制進(jìn)行刷新。官方提供了這個(gè)配置方法,參考:官方文檔 - Spring @RefreshScope Support

即在項(xiàng)目中增加配置:

feign.client.refresh-enabled: true

但是在我們的項(xiàng)目中,增加了這個(gè)配置后,啟動(dòng)失敗,報(bào)找不到相關(guān) Bean 的錯(cuò)誤:

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

問題分析

通過(guò)這個(gè) Bean 名稱,其實(shí)可以看出來(lái)這個(gè) Bean 是我們開始提到要?jiǎng)討B(tài)刷新的 Feign.Options,里面有連接超時(shí)、讀取超時(shí)等配置。名字后面的部分是我們創(chuàng)建的 FeignClient 上面 @FeignClient 注解里面的 contextId。

在創(chuàng)建 FeignClient 的時(shí)候,需要加載這個(gè) Feign.Options Bean,每個(gè) FeignClient 都有自己的 ApplicationContext,這個(gè) Feign.Options Bean 就是屬于每個(gè) FeignClient 單獨(dú)的 ApplicationContext 的。這個(gè)是通過(guò) Spring Cloud 的 NamedContextFactory 實(shí)現(xiàn)的。對(duì)于 NamedContextFactory 的深入分析,可以參考我的這篇文章:

對(duì)于 OpenFeign 的配置開啟動(dòng)態(tài)刷新,其實(shí)就是對(duì)于 FeignClient 就是要刷新每個(gè) FeignClient 的 Feign.Options 這個(gè) Bean。那么如何實(shí)現(xiàn)呢?我們先來(lái)看 spring-cloud 的動(dòng)態(tài)刷新 Bean 的實(shí)現(xiàn)方式。首先我們要搞清楚,什么是 Scope。

Bean 的 Scope

從字面意思上面理解,Scope 即 Bean 的作用域。從實(shí)現(xiàn)上面理解,Scope 即我們?cè)讷@取 Bean 的時(shí)候,這個(gè) Bean 是如何獲取的。

Spring 框架中自帶兩個(gè)耳熟能詳?shù)?Scope,即 singleton 和 prototype。singleton 即每次從 BeanFactory 獲取一個(gè) Bean 的時(shí)候(getBean),對(duì)于同一個(gè) Bean 每次返回的都是同一個(gè)對(duì)象,即單例模式。prototype 即每次從 BeanFactory 獲取一個(gè) Bean 的時(shí)候,對(duì)于同一個(gè) Bean 每次都新創(chuàng)建一個(gè)對(duì)象返回,即工廠模式。

同時(shí),我們還可以根據(jù)自己需要去擴(kuò)展 Scope,定義獲取 Bean 的方式。舉一個(gè)簡(jiǎn)單的例子,我們自定義一個(gè) TestScope。自定義的 Scope 需要先定義一個(gè)實(shí)現(xiàn) org.springframework.beans.factory.config.Scope 接口的類,來(lái)定義在這個(gè) Scope 下的 Bean 的獲取相關(guān)的操作。

public interface Scope {
    //獲取這個(gè) bean,在 BeanFactory.getBean 的時(shí)候會(huì)被調(diào)用
    Object get(String name, ObjectFactory<?> objectFactory);
    //在調(diào)用 BeanFactory.destroyScopedBean 的時(shí)候,會(huì)調(diào)用這個(gè)方法
    @Nullable
	Object remove(String name);
	//注冊(cè) destroy 的 callback
	//這個(gè)是可選實(shí)現(xiàn),提供給外部注冊(cè)銷毀 bean 的回調(diào)??梢栽?remove 的時(shí)候,執(zhí)行這里傳入的 callback。
	void registerDestructionCallback(String name, Runnable callback);
	//如果一個(gè) bean 不在 BeanFactory 中,而是根據(jù)上下文創(chuàng)建的,例如每個(gè) http 請(qǐng)求創(chuàng)建一個(gè)獨(dú)立的 bean,這樣從 BeanFactory 中就拿不到了,就會(huì)從這里拿
	//這個(gè)也是可選實(shí)現(xiàn)
	Object resolveContextualObject(String key);
	//可選實(shí)現(xiàn),類似于 session id 用戶區(qū)分不同上下文的
	String getConversationId();
}

我們來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 Scope:

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;
    }
}

這個(gè) Scope 只是實(shí)現(xiàn)了 get 方法。直接通過(guò)傳入的 objectFactory 創(chuàng)建一個(gè)新的 bean。這種 Scope 下每次調(diào)用 BeanFactory.getFactory 都會(huì)返回一個(gè)新的 Bean,自動(dòng)裝載到不同 Bean 的這種 Scope 下的 Bean 也是不同的實(shí)例。編寫測(cè)試:

@Configuration
public static class Config {
    @Bean
    //自定義 Scope 的名字是 testScope
    @org.springframework.context.annotation.Scope(value = "testScope")
    public A a() {
        return new A();
    }
    //自動(dòng)裝載進(jìn)來(lái)
    @Autowired
    private A a;
}

public static class A {
    public void test() {
        System.out.println(this);
    }
}
public static void main(String[] args) {
    //創(chuàng)建一個(gè) ApplicationContext
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    //注冊(cè)我們自定義的 Scope
    annotationConfigApplicationContext.getBeanFactory().registerScope("testScope", new TestScope());
    //注冊(cè)我們需要的配置 Bean
    annotationConfigApplicationContext.register(Config.class);
    //調(diào)用 refresh 初始化 ApplicationContext
    annotationConfigApplicationContext.refresh();
    //獲取 Config 這個(gè) Bean
    Config config = annotationConfigApplicationContext.getBean(Config.class);
    //調(diào)用自動(dòng)裝載的 Bean
    config.a.test();
    //從 BeanFactory 調(diào)用 getBean 獲取 A
    annotationConfigApplicationContext.getBean(A.class).test();
    annotationConfigApplicationContext.getBean(A.class).test();
}

執(zhí)行代碼,叢輸出上可以看出,這三個(gè) A 都是不同的對(duì)象:

com.hopegaming.spring.cloud.parent.ScopeTest$A@5241cf67
com.hopegaming.spring.cloud.parent.ScopeTest$A@716a7124
com.hopegaming.spring.cloud.parent.ScopeTest$A@77192705

我們?cè)賮?lái)修改我們的 Bean,讓它成為一個(gè) Disposable Bean:

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:

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;
    }
}

在測(cè)試代碼中,增加調(diào)用 destroyScopedBean 銷毀 bean:

annotationConfigApplicationContext.getBeanFactory().destroyScopedBean("a");

運(yùn)行代碼,可以看到對(duì)應(yīng)的輸出:

registerDestructionCallback is called
a is removed
com.hopegaming.spring.cloud.parent.ScopeTest$A@716a7124 is destroyed
callback finished

對(duì)于 DisposableBean 或者其他有相關(guān)生命周期類型的 Bean,BeanFactory 會(huì)通過(guò) registerDestructionCallback 將生命周期需要的操作回調(diào)傳進(jìn)來(lái)。使用 BeanFactory.destroyScopedBean 銷毀 Bean 的時(shí)候,會(huì)調(diào)用 Scope 的 remove 方法,我們可以在操作完成時(shí),調(diào)用 callback 回調(diào)完成 Bean 生命周期。

接下來(lái)我們嘗試實(shí)現(xiàn)一種單例的 Scope,方式非常簡(jiǎn)單,主要基于 ConcurrentHashMap:

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;
    }
}

我們使用兩個(gè) ConcurrentHashMap 緩存這個(gè) Scope 下的 Bean,以及對(duì)應(yīng)的 Destroy Callback。在這種實(shí)現(xiàn)下,就類似于單例模式的實(shí)現(xiàn)了。再使用下面的測(cè)試程序測(cè)試下:

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 類中注冊(cè) Bean 的方法名稱為 a,所以 Bean 名稱也為 a
    annotationConfigApplicationContext.getBeanFactory().destroyScopedBean("a");
    config.a.test();
    annotationConfigApplicationContext.getBean(A.class).test();
}

我們?cè)阡N毀 Bean 之前,使用自動(dòng)裝載和 BeanFactory.getBean 分別去請(qǐng)求獲取 A 這個(gè) Bean 并調(diào)用 test 方法。然后銷毀這個(gè) Bean。在這之后,再去使用自動(dòng)裝載的和 BeanFactory.getBean 分別去請(qǐng)求獲取 A 這個(gè) Bean 并調(diào)用 test 方法??梢詮妮敵鲋锌闯?, BeanFactory.getBean 請(qǐng)求的是新的 Bean 了,但是自動(dòng)裝載的里面還是已銷毀的那個(gè) bean。那么如何實(shí)現(xiàn)讓自動(dòng)裝載的也是新的 Bean,也就是重新注入呢?

這就涉及到了 Scope 注解上面的另一個(gè)配置,即指定代理模式:

@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;
}

其中第三個(gè)配置,ScopedProxyMode 是配置獲取這個(gè) Bean 的時(shí)候,獲取的是原始 Bean 對(duì)象還是代理的 Bean 對(duì)象(這也同時(shí)影響了自動(dòng)裝載):

public enum ScopedProxyMode {
    //走默認(rèn)配置,沒有其他外圍配置則是 NO
	DEFAULT,
    //使用原始對(duì)象作為 Bean
	NO,
    //使用 JDK 的動(dòng)態(tài)代理
	INTERFACES,
    //使用 CGLIB 動(dòng)態(tài)代理
	TARGET_CLASS
}

我們來(lái)測(cè)試下指定 Scope Bean 的實(shí)際對(duì)象為代理的效果,我們修改下上面的測(cè)試代碼,使用 CGLIB 動(dòng)態(tài)代理。修改代碼:

@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;
}

編寫測(cè)試主方法:

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 實(shí)例的類型
    System.out.println(config.a.getClass());
    System.out.println(annotationConfigApplicationContext.getBean(A.class).getClass());
    //這時(shí)候我們需要注意,代理 Bean 的名稱有所變化,需要通過(guò) ScopedProxyUtils 獲取
    annotationConfigApplicationContext.getBeanFactory().destroyScopedBean(ScopedProxyUtils.getTargetBeanName("a"));
    config.a.test();
    annotationConfigApplicationContext.getBean(A.class).test();
}

執(zhí)行程序,輸出為:

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

從輸出中可以看出:

  • 每次對(duì)于自動(dòng)裝載的 Bean 的調(diào)用,都會(huì)調(diào)用自定義 Scope 的 get 方法重新獲取 Bean
  • 每次通過(guò) BeanFactory 獲取 Bean,也會(huì)調(diào)用自定義 Scope 的 get 方法重新獲取 Bean
  • 獲取的 Bean 實(shí)例,是一個(gè) CGLIB 代理對(duì)象
  • 在 Bean 被銷毀后,無(wú)論是通過(guò) BeanFactory 獲取 Bean 還是自動(dòng)裝載的 Bean,都是新的 Bean

那么 Scope 是如何實(shí)現(xiàn)這些的呢?我們接下來(lái)簡(jiǎn)單分析下源碼

Scope 基本原理

如果一個(gè) Bean 沒有聲明任何 Scope,那么他的 Scope 就會(huì)被賦值成 singleton,也就是默認(rèn)的 Bean 都是單例的。這個(gè)對(duì)應(yīng) BeanFactory 注冊(cè) Bean 之前需要生成 Bean 定義,在 Bean 定義的時(shí)候會(huì)賦上這個(gè)默認(rèn)值,對(duì)應(yīng)源碼:

AbstractBeanFactory

protected RootBeanDefinition getMergedBeanDefinition(
	String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
	throws BeanDefinitionStoreException {
    //省略我們不關(guān)心的源碼
	if (!StringUtils.hasLength(mbd.getScope())) {
		mbd.setScope(SCOPE_SINGLETON);
	}
	//省略我們不關(guān)心的源碼
}

在聲明一個(gè) Bean 具有特殊 Scope 之前,我們需要定義這個(gè)自定義 Scope 并把它注冊(cè)到 BeanFactory 中。這個(gè) Scope 名稱必須全局唯一,因?yàn)橹髤^(qū)分不同 Scope 就是通過(guò)這個(gè)名字進(jìn)行區(qū)分的。注冊(cè) Scope 對(duì)應(yīng)源碼:

AbstractBeanFactory

@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 這兩個(gè)預(yù)設(shè)的 scope
	if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
		throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
	}
	//放入 scopes 這個(gè) map 中,key 為名稱,value 為自定義 Scope
	Scope previous = this.scopes.put(scopeName, scope);
	//可以看出,后面放入的會(huì)替換前面的,這個(gè)我們要盡量避免出現(xiàn)。
	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 + "]");
		}
	}
}

當(dāng)聲明一個(gè) Bean 具有特殊的 Scope 之后,獲取這個(gè) Bean 的時(shí)候,就會(huì)有特殊的邏輯,參考通過(guò) BeanFactory 獲取 Bean 的核心源碼代碼:

AbstractBeanFactory

@SuppressWarnings("unchecked")
protected <T> T doGetBean(
	String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
	throws BeansException {
	//省略我們不關(guān)心的源碼
	// 創(chuàng)建 Bean 實(shí)例
    if (mbd.isSingleton()) {
    	//創(chuàng)建或者返回單例實(shí)例
    } else if (mbd.isPrototype()) {
    	//每次創(chuàng)建一個(gè)新實(shí)例
    } else {
    	//走到這里代表這個(gè) Bean 屬于自定義 Scope
    	String scopeName = mbd.getScope();
    	//必須有 Scope 名稱
    	if (!StringUtils.hasLength(scopeName)) {
    		throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
    	}
    	//通過(guò) Scope 名稱獲取對(duì)應(yīng)的 Scope,自定義 Scope 需要手動(dòng)注冊(cè)進(jìn)來(lái)
    	Scope scope = this.scopes.get(scopeName);
    	if (scope == null) {
    		throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    	}
    	try {
    		//調(diào)用自定義 Scope 的 get 方法獲取 Bean
    		Object scopedInstance = scope.get(beanName, () -> {
    			//同時(shí)將創(chuàng)建 Bean 需要的生命周期的回調(diào)傳入,用于創(chuàng)建 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);
    	}
    }
    //省略我們不關(guān)心的源碼
}

同時(shí),如果我們定義 Scope Bean 的代理方式為 CGLIB,那么在獲取 Bean 定義的時(shí)候,就會(huì)根據(jù)原始 Bean 定義創(chuàng)建 Scope 代理的 Bean 定義,對(duì)應(yīng)源碼:

ScopedProxyUtils

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
			BeanDefinitionRegistry registry, boolean proxyTargetClass) {

    //原始目標(biāo) Bean 名稱
	String originalBeanName = definition.getBeanName();
	//獲取原始目標(biāo) Bean 定義
	BeanDefinition targetDefinition = definition.getBeanDefinition();
	//獲取代理 Bean 名稱
	String targetBeanName = getTargetBeanName(originalBeanName);

    //創(chuàng)建類型為 ScopedProxyFactoryBean 的 Bean
	RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
    //根據(jù)原始目標(biāo) Bean 定義的屬性,配置代理 Bean 定義的相關(guān)屬性,省略這部分源碼
	
    //根據(jù)原始目標(biāo) Bean 的自動(dòng)裝載屬性,復(fù)制到代理 Bean 定義
	proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
	proxyDefinition.setPrimary(targetDefinition.isPrimary());
	if (targetDefinition instanceof AbstractBeanDefinition) {
		proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
	}

	//設(shè)置原始 Bean 定義為不自動(dòng)裝載并且不為 Primary
	//這樣通過(guò) BeanFactory 獲取 Bean 以及自動(dòng)裝載的都是代理 Bean 而不是原始目標(biāo) Bean
	targetDefinition.setAutowireCandidate(false);
	targetDefinition.setPrimary(false);

	//使用新名稱注冊(cè) Bean
	registry.registerBeanDefinition(targetBeanName, targetDefinition);

	return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}

private static final String TARGET_NAME_PREFIX = "scopedTarget.";
//這個(gè)就是獲取代理 Bean 名稱的工具方法,我們上面 Destroy Bean 的時(shí)候也有用到
public static String getTargetBeanName(String originalBeanName) {
	return TARGET_NAME_PREFIX + originalBeanName;
}

這個(gè)代理 Bean 有啥作用呢?其實(shí)主要用處就是每次調(diào)用 Bean 的任何方法的時(shí)候,都會(huì)通過(guò) BeanFactory 獲取這個(gè) Bean 進(jìn)行調(diào)用。參考源碼:

代理類 ScopedProxyFactoryBean

public class ScopedProxyFactoryBean extends ProxyConfig
		implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {
    private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();
    //這個(gè)就是通過(guò) SimpleBeanTargetSource 生成的實(shí)際代理,對(duì)于 Bean 的方法調(diào)用都會(huì)通過(guò)這個(gè) proxy 進(jìn)行調(diào)用
    private Object proxy;
}

SimpleBeanTargetSource 就是實(shí)際的代理源,他的實(shí)現(xiàn)非常簡(jiǎn)單,核心方法就是使用 Bean 名稱通過(guò) BeanFactory 獲取這個(gè) Bean:

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {
	@Override
	public Object getTarget() throws Exception {
		return getBeanFactory().getBean(getTargetBeanName());
	}
}

通過(guò) BeanFactory 獲取這個(gè) Bean,通過(guò)上面源碼分析可以知道,對(duì)于自定義 Scope 的 Bean 就會(huì)調(diào)用自定義 Scope 的 get 方法。

然后是 Bean 的銷毀,在 BeanFactory 創(chuàng)建這個(gè) Bean 對(duì)象的時(shí)候,就會(huì)調(diào)用自定義 Scope 的 registerDestructionCallback 將 Bean 銷毀的回調(diào)傳入:

AbstractBeanFactory

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()) {
			//對(duì)于 singleton
			registerDisposableBean(beanName, new DisposableBeanAdapter(
					bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
		}
		else {
			//對(duì)于自定義 Scope
			Scope scope = this.scopes.get(mbd.getScope());
			if (scope == null) {
				throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
			}
			//調(diào)用 registerDestructionCallback
			scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
					bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
		}
	}
}

在我們想銷毀 Scope Bean 的時(shí)候,需要調(diào)用的是 BeanFactory 的 destroyScopedBean 方法,這個(gè)方法會(huì)調(diào)用自定義 Scope 的 remove:

AbstractBeanFactory

@Override
public void destroyScopedBean(String beanName) {
	RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
	//僅針對(duì)自定義 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 + "'");
	}
	//調(diào)用自定義 Scope 的 remove 方法
	Object bean = scope.remove(beanName);
	if (bean != null) {
		destroyBean(beanName, bean, mbd);
	}
}

到此這篇關(guān)于解決啟用 Spring-Cloud-OpenFeign 配置可刷新項(xiàng)目無(wú)法啟動(dòng)的問題的文章就介紹到這了,更多相關(guān)Spring-Cloud-OpenFeign 配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中輸入被跳過(guò)情況的深入探究

    Java中輸入被跳過(guò)情況的深入探究

    這篇文章主要給大家介紹了關(guān)于Java中輸入被跳過(guò)情況的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 淺談Java中String的常用方法

    淺談Java中String的常用方法

    今天帶大家來(lái)復(fù)習(xí)一下Java中String的常用方法,文中有非常詳細(xì)的介紹,對(duì)正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • Mybatis-Plus設(shè)置全局或者局部ID自增的實(shí)現(xiàn)

    Mybatis-Plus設(shè)置全局或者局部ID自增的實(shí)現(xiàn)

    在使用Mybatis-Plus新增的時(shí)候,我們往往想要id隨著數(shù)據(jù)庫(kù)自增,本文主要介紹了Mybatis-Plus設(shè)置全局或者局部ID自增的實(shí)現(xiàn),具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Flyway詳解及Springboot集成Flyway的詳細(xì)教程

    Flyway詳解及Springboot集成Flyway的詳細(xì)教程

    Flayway是一款數(shù)據(jù)庫(kù)版本控制管理工具,,支持?jǐn)?shù)據(jù)庫(kù)版本自動(dòng)升級(jí),Migrations可以寫成sql腳本,也可以寫在java代碼里。這篇文章主要介紹了Flyway詳解及Springboot集成Flyway的詳細(xì)教程的相關(guān)資料,需要的朋友可以參考下
    2020-07-07
  • springboot整合vue項(xiàng)目(小試牛刀)

    springboot整合vue項(xiàng)目(小試牛刀)

    這篇文章主要介紹了springboot整合vue項(xiàng)目(小試牛刀),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • SpringBoot整合mybatis-plus進(jìn)階詳細(xì)教程

    SpringBoot整合mybatis-plus進(jìn)階詳細(xì)教程

    本文主要對(duì)mybatis-plus的條件構(gòu)造器、AR模式、插件、逆向工程、自定義全局操作、公共字段自動(dòng)填充等知識(shí)點(diǎn)進(jìn)行講解,需要的朋友參考下吧
    2021-09-09
  • Maven默認(rèn)中央倉(cāng)庫(kù)(settings.xml 配置詳解)

    Maven默認(rèn)中央倉(cāng)庫(kù)(settings.xml 配置詳解)

    這篇文章主要介紹了Maven默認(rèn)中央倉(cāng)庫(kù)(settings.xml 配置詳解),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • springboot實(shí)現(xiàn)多文件上傳功能

    springboot實(shí)現(xiàn)多文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了springboot實(shí)現(xiàn)多文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • Java網(wǎng)絡(luò)通信中URL與HTTP編程技術(shù)詳解

    Java網(wǎng)絡(luò)通信中URL與HTTP編程技術(shù)詳解

    要想實(shí)現(xiàn)網(wǎng)絡(luò)編程,除了可以使用Socket之外,我們還可以利用URL編程或HTTP編程技術(shù),所以今天這篇文章,就給大家介紹一下URL編程和HTTP編程技術(shù),看看這兩種技術(shù)有什么特點(diǎn),文中有詳細(xì)的代碼講解,需要的朋友可以參考下
    2023-11-11
  • Spring AOP攔截-三種方式實(shí)現(xiàn)自動(dòng)代理詳解

    Spring AOP攔截-三種方式實(shí)現(xiàn)自動(dòng)代理詳解

    這篇文章主要介紹了Spring AOP攔截-三種方式實(shí)現(xiàn)自動(dòng)代理詳解,還是比較不錯(cuò)的,這里分享給大家,供需要的朋友參考。
    2017-11-11

最新評(píng)論