feign はサーキット ブレーカー メカニズムを組み合わせています: feign クライアントのタイプ クラスのフォールバック インスタンスが見つかりません

spring-Cloud のサーキット ブレーカー メカニズムを使用すると、メソッド呼び出し時にエラーが報告されます。サーキット ブレーカー クラスは次のとおりです。

RemoteNettyServerServiceFallbackFactory.java
@Component
public class RemoteNettyServerServiceFallbackFactory implements FallbackFactory<RemoteNettyServerService> {

	@Override
	public RemoteNettyServerService create(Throwable throwable) {
		RemoteNettyServerServiceFallbackImpl remoteNettyServerServiceFallback = new RemoteNettyServerServiceFallbackImpl();
		remoteNettyServerServiceFallback.setCause(throwable);
		return remoteNettyServerServiceFallback;
	}
}

@component を追加するという解決策をインターネット上でたくさん見ましたが、それでも機能しません。

RemoteNettyServerServiceFallbackImpl.java

@Component
@Slf4j
public class RemoteNettyServerServiceFallbackImpl implements RemoteNettyServerService {

	@Setter
	private Throwable cause;


	@Override
	public Map<String, Object> startCharge(ChargeRemoteStartRequest chargeRemoteStartRequest , String from) {
		log.error("feign 开始充电:{}", cause);
		return null;
	}
}

app-api-biz がモジュール間で netty-server-api モジュールを呼び出していることにもお気づきかと思いますが、app-api-biz のスタートアップ クラスを開始しただけでは、現在のアプリ モジュールの下にあるアノテーションのみをスキャンできますが、 netty-server-api の注釈。

これら 2 つのパッケージは異なります。したがって、@component アノテーションの追加はスキャンできません。では、どうすればよいでしょうか? springboot の自動読み込み設定のアノテーションは次のとおりであることがわかっています。

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

    String[] excludeName() default {};
}

最も重要なことは@Import(AutoConfigurationImportSelector.class)、 SpringBoot の助けを借りてAutoConfigurationImportSelector@EnableAutoConfigurationSpringBoot アプリケーションが@Configuration現在作成され、SpringBoot によって使用されている IoC コンテナにすべての適格な構成をロードできるようになることです。

Springフレームワークの独自ツールクラスSpringFactoriesLoaderのサポートにより、@EnableAutoConfigurationインテリジェントな自動設定機能がついに完成!

AutoConfigurationImportSelector クラスでは、  SpringFactoriesLoader.loadFactoryNames()
spring-boot-autoconfigure.jar/META-INF/spring.factories 内の各 xxxAutoConfiguration ファイルをコンテナーにロードすることによって、 spring.factories ファイル内の各 xxxAutoConfiguration ファイルには通常、次の条件アノテーションがあることがわかります。

  • @ConditionalOnClass: クラスがクラスパスに存在する場合に有効になります。
  • @ConditionalOnMissingClass: クラスがクラスパスに存在しない場合に有効になります。
  • @ConditionalOnBean: このタイプの Bean が DI コンテナーに存在する場合に有効になります
  • @ConditionalOnMissingBean: このタイプの Bean が DI コンテナーに存在しない場合に有効になります。
  • @ConditionalOnSingleCandidate: DI コンテナ内にこのタイプの Bean が 1 つだけ、または @Primary が 1 つだけある場合に有効になります。
  • @ConditionalOnExpression: SpEL 式の結果が true の場合
  • @ConditionalOnProperty: パラメーターの設定または値が一致している場合に有効になります。
  • @ConditionalOnResource: 指定されたファイルが存在する場合に有効になります
  • @ConditionalOnJndi: 指定された JNDI が存在する場合に有効になります。
  • @ConditionalOnJava: 指定された Java バージョンが存在する場合に有効になります
  • @ConditionalOnWebApplication: Web アプリケーション環境で効果的
  • @ConditionalOnNotWebApplication: 非 Web アプリケーション環境で効果的
AutoConfigurationImportSelector.java下的一段代码:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

SpringFactoriesLoader は、Spring フレームワークのプライベート拡張スキームです (Java の SPI スキーム java.util.ServiceLoader に似ています)。その主な機能は、指定された構成ファイルから構成をロードすることです。spring-factories は典型的な Java プロパティ ファイルですが、KeyMETA-INF/spring-factoriesと値は、Java 型の完全なクラス名です。

の場合@EnableAutoConfiguration、SpringFactoriesLoader の目的は少し異なります。その本来の目的は SPI 拡張シナリオを提供することであり、この@EnableAutoConfigurationシナリオでは、構成検索のためのより機能的なサポートを提供します。つまり、検索キーとして@EnableAutoConfiguration完全なクラス名に基づいて、org.springframework.boot.autoconfig.EnableAutoConfiguration対応するクラスのセット@Configuration

SpringFactoriesLoader は抽象クラスです。クラスで定義された静的プロパティは、リソースをロードするためのパスを定義しますpublic static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"。さらに、次の 3 つの静的メソッドがあります。

  • loadFactories: 指定されたfactoryClassをロードし、インスタンス化します。
  • loadFactoryNames: 指定されたfactoryClassの名前コレクションをロードします。
  • instantiateFactory: 指定されたfactoryClassをインスタンス化します。

loadFactoryNames メソッドと instantiateFactory メソッドは、loadFactories メソッドで呼び出されます。

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();

    private SpringFactoriesLoader() {
    }

    public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryType, "'factoryType' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
        }

        List<T> result = new ArrayList(factoryImplementationNames.size());
        Iterator var5 = factoryImplementationNames.iterator();

        while(var5.hasNext()) {
            String factoryImplementationName = (String)var5.next();
            result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
        }

        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

    private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
        try {
            Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
            if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
                throw new IllegalArgumentException("Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
            } else {
                return ReflectionUtils.accessibleConstructor(factoryImplementationClass, new Class[0]).newInstance();
            }
        } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]", var4);
        }
    }
}

loadFactories メソッドは、最初にクラス ローダーを取得し、次にloadFactoryNamesメソッドを呼び出して指定されたすべてのリソースの名前コレクションを取得し、次にinstantiateFactoryメソッドを呼び出してこれらのリソース クラスをインスタンス化し、結果コレクションに追加します。最後に、AnnotationAwareOrderComparator.sortメソッドを呼び出してコレクションを並べ替えます。

これらの知識ポイントを理解したら、問題を振り返って解決することができます。

この場合、サービス間呼び出しが可能であり、nettyモジュールが起動していなくても、API呼び出し時にはフォールバックが使用され、このクラスが使用されます。

終わり

おすすめ

転載: blog.csdn.net/weixin_38340874/article/details/108773719