spring还有这种编码?A default binder has been requested, but there is no binder available

spring还有这种编码?A default binder has been requested, but there is no binder available,第1张

问题描述

在集成spring cloud stream框架时,会出现下面错误:

A default binder has been requested, but there is no binder available??

网上大多数都是让在application.yml 文件中引入一个默认的 binder就可以解决,但是我这个不是这个原因导致的,所以记录下

问题分析

我没有用这块的功能啊,为啥非要我指定一个binder?

我们知道spring cloud stream是用于构建高度可扩展的基于事件驱动的微服务,其目的是为了简化消息在 Spring Cloud 应用程序中的开发,就是说是给消息做发布订阅用的。

里面大量用到java的函数编程,简化代码逻辑,使得编码简单易上手,但是问题往往就出现在这不经意间。

跟踪报错位置,发现在绑定生产者时出现了异常


就是inMemorySwaggerResourcesProvider-out-0这个里面的配置文件中没有指定binder导致启动出现了异常,这个明显跟stream没有任何关系啊,看着让人费解


这个是项目中集成了swagger后,swagger自己会将这个实例注入到容器中,跟stream是没有关系的,我把swagger的依赖给去掉了,现在项目就可以正常启动了,那么就是swagger的问题,但是为啥swagger会影响stream的消息通道构建呢?

只能进行debugger分析:

通过链路分析,最终定位到org.springframework.cloud.function.context.catalog.BeanFactoryAwareFunctionRegistry,这个类大概就是函数类的注册工厂,他把所有注入到容器的Function.class、 Consumer.class、Supplier.class全部都拿来找binder,直接看代码:

String discoverDefaultDefinitionIfNecessary(String definition) {
	if (StringUtils.isEmpty(definition) || definition.endsWith("|")) {
		// the underscores are for Kotlin function registrations (see KotlinLambdaToFunctionAutoConfiguration)
		String[] functionNames = Stream.of(this.applicationContext.getBeanNamesForType(Function.class))
			.filter(n -> !n.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) && !n
				.equals(RoutingFunction.FUNCTION_NAME)).toArray(String[]::new);
		String[] consumerNames = Stream.of(this.applicationContext.getBeanNamesForType(Consumer.class))
			.filter(n -> !n.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) && !n
				.equals(RoutingFunction.FUNCTION_NAME)).toArray(String[]::new);
		String[] supplierNames = Stream.of(this.applicationContext.getBeanNamesForType(Supplier.class))
			.filter(n -> !n.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) && !n
				.equals(RoutingFunction.FUNCTION_NAME)).toArray(String[]::new);

		/*
		 * we may need to add BiFunction and BiConsumer at some point
		 */
		List<String> names = Stream
			.concat(Stream.of(functionNames), Stream.concat(Stream.of(consumerNames), Stream.of(supplierNames)))
			.collect(Collectors.toList());

		if (definition.endsWith("|")) {
			Set<String> fNames = this.getNames(null);
			definition = this.determinImpliedDefinition(fNames, definition);
		}
		else if (!ObjectUtils.isEmpty(names)) {
			if (names.size() > 1) {
				logger.debug("Found more then one function beans in BeanFactory: " + names
					+ ". If you did not intend to use functions, ignore this message. However, if you did "
					+ "intend to use functions in the context of spring-cloud-function, consider "
					+ "providing 'spring.cloud.function.definition' property pointing to a function bean(s) "
					+ "you intend to use. For example, 'spring.cloud.function.definition=myFunction'");
				return null;
			}
			definition = names.get(0);
		}
		else {
			definition = this.discoverDefaultDefinitionFromRegistration();
		}

		if (StringUtils.hasText(definition) && this.applicationContext.containsBean(definition)) {
			Type functionType = discoverFunctionType(this.applicationContext.getBean(definition), definition);
			if (!FunctionTypeUtils.isSupplier(functionType) && !FunctionTypeUtils
				.isFunction(functionType) && !FunctionTypeUtils.isConsumer(functionType)) {
				logger.debug("Discovered functional instance of bean '" + definition + "' as a default function, however its "
						+ "function argument types can not be determined. Discarding.");
				definition = null;
			}
		}
	}
	if (!StringUtils.hasText(definition)) {
		String[] functionRegistrationNames = Stream.of(applicationContext.getBeanNamesForType(FunctionRegistration.class))
				.filter(n -> !n.endsWith(FunctionRegistration.REGISTRATION_NAME_SUFFIX) && !n
					.equals(RoutingFunction.FUNCTION_NAME)).toArray(String[]::new);
		if (functionRegistrationNames != null) {
			if (functionRegistrationNames.length == 1) {
				definition = functionRegistrationNames[0];
			}
			else {
				logger.debug("Found more then one function registration beans in BeanFactory: " + functionRegistrationNames
						+ ". If you did not intend to use functions, ignore this message. However, if you did "
						+ "intend to use functions in the context of spring-cloud-function, consider "
						+ "providing 'spring.cloud.function.definition' property pointing to a function bean(s) "
						+ "you intend to use. For example, 'spring.cloud.function.definition=myFunction'");
			}
		}
	}
	return definition;
}


没错!InMemorySwaggerResourcesProvider就是Supplier的一个实现,正好被扫描到了,并且该项目中又只有一个,便取了第一个去注册生产者去了,问题是这个又不是干这个活的,必然就报错了

spring-cloud-function-context 里面也太霸道了,竟然直接使用了java的顶层接口做设计,这样会影响其他框架的集成啊

结论

SCS需要配置spring.cloud.function.definition=myFunction,作为默认的函数,不然就得引入其他Supplier的类到容器中,保证names.size() > 1,不过这种代码我觉得不应该出现在spring中啊,太难排查了

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/728003.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存