Análisis del código fuente de Spring Cloud Stream

Spring Cloud Stream es un marco para microservicios basados ​​en mensajes.
  La aplicación interactúa con el carpeta  en Spring Cloud Stream a través de entradas  o salidas  y se vincula a través de nuestra configuración, y la carpeta de Spring Cloud Stream es responsable de interactuar con el middleware de mensajes. Por lo tanto, solo necesitamos descubrir cómo interactuar con Spring Cloud Stream para utilizar convenientemente el enfoque basado en mensajes.   Mediante el uso de Spring Integration para conectar el middleware del intermediario de mensajes para lograr la gestión de eventos de mensajes. Spring Cloud Stream proporciona una implementación de configuración automatizada personalizada para productos de middleware de mensajes de algunos proveedores, haciendo referencia a los tres conceptos centrales de publicación-suscripción, grupo de consumidores y partición. Actualmente sólo se admiten RabbitMQ y Kafka .

Comencemos el análisis, primero introduzcamos el archivo pom.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>eureka.stream</groupId>
	<artifactId>stream</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springstream</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Brixton.SR5</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Copiar código

Entre ellos, spring-cloud-starter-stream-rabbit es el marco de transmisión introducido y también puede admitir spring-cloud-starter-stream-kafka. Aquí utilizamos conejo para el análisis.

Primero creemos un ejemplo simple.

Primero cree la clase de canalización de entrada y salida de mensajes StreamSendClient.

import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;


public interface StreamSendClient {

    @Output("testMessage")
    MessageChannel output();
    
    @Input("testMessage")
    MessageChannel input();
}

Copiar código

Cree otra clase de procesamiento de mensajes SinkReceiver. Agregue la anotación @EnableBinding arriba. La clase definida por la anotación es StreamSendClient.

@EnableBinding({StreamSendClient.class})
public class SinkReceiver {

    @StreamListener("testMessage")
    public void reveive(Object payload){
        System.out.println("Received:" + payload);
    }
}

Copiar código

Cree la clase de inicio StreamApplication.

@SpringBootApplication
public class StreamApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext run = SpringApplication.run(StreamApplication.class, args);
		StreamSendClient streamClient = (StreamSendClient)run.getBean("com.springcloud.eurekaclient.StreamSendClient");
		streamClient.output().send(MessageBuilder.withPayload("from streamClient").build());
	}

}

Copiar código

Después de la ejecución, la información impresa se puede encontrar en la consola: Recibido: de streamClient. Al mismo tiempo, también puede ver que la cola en la consola RabbitMQ contiene testMessage.

Comencemos el análisis.

Primero, no se agregan nuevas anotaciones a la clase de inicio. Hay una anotación @EnableBinding en SinkReceiver.

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@Import({ChannelBindingServiceConfiguration.class, BindingBeansRegistrar.class, BinderFactoryConfiguration.class,
		SpelExpressionConverterConfiguration.class})
@EnableIntegration
public @interface EnableBinding {

	/**
	 * A list of interfaces having methods annotated with {@link Input} and/or
	 * {@link Output} to indicate bindable components.
	 */
	Class<?>[] value() default {};

}

Copiar código

Saber:

1. Esta clase es una clase @Component

2. Esta clase importa ChannelBindingServiceConfiguration.class, BindingBeansRegistrar.class, BinderFactoryConfiguration.class, SpelExpressionConverterConfiguration.class.

3. La anotación EnableIntegration está activada. Spring Integration se posiciona como un Enterprise Service Bus (ESB). En Spring Integration, los canales se abstraen en dos formas de expresión: PollableChannel y SubscribableChannel, los cuales heredan MessageChannel.

Análisis de clase ChannelBindingServiceConfiguration:

@Configuration
@EnableConfigurationProperties(ChannelBindingServiceProperties.class)
public class ChannelBindingServiceConfiguration {

	private static final String ERROR_CHANNEL_NAME = "error";

	@Autowired
	private MessageBuilderFactory messageBuilderFactory;

	@Autowired(required = false)
	private ObjectMapper objectMapper;

	/**
	 * User defined custom message converters
	 */
	@Autowired(required = false)
	private List<AbstractFromMessageConverter> customMessageConverters;

	@Bean
	// This conditional is intentionally not in an autoconfig (usually a bad idea) because
	// it is used to detect a ChannelBindingService in the parent context (which we know
	// already exists).
	@ConditionalOnMissingBean(ChannelBindingService.class)
	public ChannelBindingService bindingService(ChannelBindingServiceProperties channelBindingServiceProperties,
			BinderFactory<MessageChannel> binderFactory) {
		return new ChannelBindingService(channelBindingServiceProperties, binderFactory);
	}


	@Bean
	public BindableChannelFactory channelFactory(CompositeMessageChannelConfigurer compositeMessageChannelConfigurer) {
		return new DefaultBindableChannelFactory(compositeMessageChannelConfigurer);
	}

	@Bean
	public CompositeMessageChannelConfigurer compositeMessageChannelConfigurer(
			MessageConverterConfigurer messageConverterConfigurer) {
		List<MessageChannelConfigurer> configurerList = new ArrayList<>();
		configurerList.add(messageConverterConfigurer);
		return new CompositeMessageChannelConfigurer(configurerList);
	}

	@Bean
	@DependsOn("bindingService")
	public OutputBindingLifecycle outputBindingLifecycle() {
		return new OutputBindingLifecycle();
	}

	@Bean
	@DependsOn("bindingService")
	public InputBindingLifecycle inputBindingLifecycle() {
		return new InputBindingLifecycle();
	}

	@Bean
	@DependsOn("bindingService")
	public ContextStartAfterRefreshListener contextStartAfterRefreshListener() {
		return new ContextStartAfterRefreshListener();
	}

	@Bean
	public static StreamListenerAnnotationBeanPostProcessor bindToAnnotationBeanPostProcessor(
			@Lazy BinderAwareChannelResolver binderAwareChannelResolver,
			@Lazy MessageHandlerMethodFactory messageHandlerMethodFactory) {
		return new StreamListenerAnnotationBeanPostProcessor(binderAwareChannelResolver,
				messageHandlerMethodFactory);
	}

}

Copiar código

ChannelBindingServiceConfiguration carga beans importantes:

1. ChannelBindingService: Responsable de crear el MessageChannel de productores y consumidores, así como el intercambio (Exchange), Queue, etc. en RabbitMQ.

2. inputBindingLifecycle, outputBindingLifecycle: principalmente responsable de llamar a ChannelBindingService para crear después del inicio.

3. StreamListenerAnnotationBeanPostProcessor: Responsable de crear una asociación entre el método anotado con @StreamListener y el canal de consumo RabbitMQ. Es decir, cuando se envía un mensaje de Rabbitmq, el método de ejecución tiene un método anotado con @StreamListener.

Análisis de la clase BindingBeansRegistrar:

BindingBeansRegistrar analiza principalmente las clases en la implementación de @EnableBinding (StreamSendClient.class).

public class BindingBeansRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		AnnotationAttributes attrs = AnnotatedElementUtils.getMergedAnnotationAttributes(
				ClassUtils.resolveClassName(metadata.getClassName(), null),
				EnableBinding.class);
		for (Class<?> type : collectClasses(attrs, metadata.getClassName())) {
			BindingBeanDefinitionRegistryUtils.registerChannelBeanDefinitions(type,
					type.getName(), registry);
			BindingBeanDefinitionRegistryUtils.registerChannelsQualifiedBeanDefinitions(
					ClassUtils.resolveClassName(metadata.getClassName(), null), type,
					registry);
		}
	}

	private Class<?>[] collectClasses(AnnotationAttributes attrs, String className) {
		EnableBinding enableBinding = AnnotationUtils.synthesizeAnnotation(attrs,
				EnableBinding.class, ClassUtils.resolveClassName(className, null));
		return enableBinding.value();
	}

}

Copiar código

A través del método CollectClasses, obtenga enableBinding.value() en EnableBinding, que es la clase StreamSendClient en el ejemplo anterior. Método BindingBeanDefinitionRegistryUtils.registerChannelBeanDefinitions después

public static void registerChannelBeanDefinitions(Class<?> type,
			final String channelInterfaceBeanName, final BeanDefinitionRegistry registry) {
		ReflectionUtils.doWithMethods(type, new MethodCallback() {
			@Override
			public void doWith(Method method) throws IllegalArgumentException,
					IllegalAccessException {
				Input input = AnnotationUtils.findAnnotation(method, Input.class);
				if (input != null) {
					String name = getChannelName(input, method);
					registerInputChannelBeanDefinition(input.value(), name,
							channelInterfaceBeanName, method.getName(), registry);
				}
				Output output = AnnotationUtils.findAnnotation(method, Output.class);
				if (output != null) {
					String name = getChannelName(output, method);
					registerOutputChannelBeanDefinition(output.value(), name,
							channelInterfaceBeanName, method.getName(), registry);
				}
			}

		});
	}

Copiar código

public static void registerChannelBeanDefinitions(Class<?> type,
			final String channelInterfaceBeanName, final BeanDefinitionRegistry registry) {
		ReflectionUtils.doWithMethods(type, new MethodCallback() {
			@Override
			public void doWith(Method method) throws IllegalArgumentException,
					IllegalAccessException {
				Input input = AnnotationUtils.findAnnotation(method, Input.class);
				if (input != null) {
					String name = getChannelName(input, method);
					registerInputChannelBeanDefinition(input.value(), name,
							channelInterfaceBeanName, method.getName(), registry);
				}
				Output output = AnnotationUtils.findAnnotation(method, Output.class);
				if (output != null) {
					String name = getChannelName(output, method);
					registerOutputChannelBeanDefinition(output.value(), name,
							channelInterfaceBeanName, method.getName(), registry);
				}
			}

		});
	}

Copiar código

        public static void registerInputChannelBeanDefinition(String qualifierValue,
			String name, String channelInterfaceBeanName,
			String channelInterfaceMethodName, BeanDefinitionRegistry registry) {
		registerChannelBeanDefinition(Input.class, qualifierValue, name,
				channelInterfaceBeanName, channelInterfaceMethodName, registry);
	}

	private static void registerChannelBeanDefinition(
			Class<? extends Annotation> qualifier, String qualifierValue, String name,
			String channelInterfaceBeanName, String channelInterfaceMethodName,
			BeanDefinitionRegistry registry) {

		RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
		rootBeanDefinition.setFactoryBeanName(channelInterfaceBeanName);
		rootBeanDefinition.setUniqueFactoryMethodName(channelInterfaceMethodName);
		rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(qualifier,
				qualifierValue));
		registry.registerBeanDefinition(name, rootBeanDefinition);
	}

Copiar código

Busque las anotaciones @input y @output en la clase StreamSendClient e inyecte los dos métodos en beans. El nombre del bean es el valor de la anotación @Input en StreamSendClient, que es testMessage. Y configure la clase de fábrica que define BeanDefinition para generar el Bean en StreamSendClient, y configure el método setUniqueFactoryMethodName para generar el Bean (testMessage) en input().

BindingBeanDefinitionRegistryUtils.registerChannelsQualifiedBeanDefinitions方法:

public static void registerChannelsQualifiedBeanDefinitions(Class<?> parent,
			Class<?> type, final BeanDefinitionRegistry registry) {

		if (type.isInterface()) {
			RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(
					BindableProxyFactory.class);
			rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(
					Bindings.class, parent));
			rootBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(
					type);
			registry.registerBeanDefinition(type.getName(), rootBeanDefinition);
		}
		else {
			RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(type);
			rootBeanDefinition.addQualifier(new AutowireCandidateQualifier(
					Bindings.class, parent));
			registry.registerBeanDefinition(type.getName(), rootBeanDefinition);
		}
	}

Copiar código

Inyecte un objeto Bean cuyo beanName sea StreamSendClient, cuyo calificador se llame Bindings y cuya BeanClass sea BindableProxyFactory (Bindable). BindableProxyFactory implementa la interfaz Bindable.

Echemos un vistazo al código fuente de BindableProxyFactory aquí.

public class BindableProxyFactory implements MethodInterceptor, FactoryBean<Object>, Bindable, InitializingBean {

	private Map<String, ChannelHolder> inputHolders = new HashMap<>();

	private Map<String, ChannelHolder> outputHolders = new HashMap<>();

	//实现了动态代理,所以当获取input和output方法获取channel时,会通过这里获得
	public synchronized Object invoke(MethodInvocation invocation) throws Throwable {
		MessageChannel messageChannel = null;
		Method method = invocation.getMethod();
		if (MessageChannel.class.isAssignableFrom(method.getReturnType())) {
			Input input = AnnotationUtils.findAnnotation(method, Input.class);
			if (input != null) {
				String name = BindingBeanDefinitionRegistryUtils.getChannelName(input, method);
				messageChannel = this.inputHolders.get(name).getMessageChannel();
			}
			Output output = AnnotationUtils.findAnnotation(method, Output.class);
			if (output != null) {
				String name = BindingBeanDefinitionRegistryUtils.getChannelName(output, method);
				messageChannel =  this.outputHolders.get(name).getMessageChannel();
			}
		}
		//ignore
		return messageChannel;
	}

	//实现了InitializingBean,在Bean生成后,会将input和output两个注解生成对应的channel
        //类,默认是DirectChannel类    
	public void afterPropertiesSet() throws Exception {
		ReflectionUtils.doWithMethods(type, new ReflectionUtils.MethodCallback() {
			@Override
			public void doWith(Method method) throws IllegalArgumentException {
				Assert.notNull(channelFactory, "Channel Factory cannot be null");
				Input input = AnnotationUtils.findAnnotation(method, Input.class);
				if (input != null) {
					String name = BindingBeanDefinitionRegistryUtils.getChannelName(input, method);
					validateChannelType(method.getReturnType());
					MessageChannel sharedChannel = locateSharedChannel(name);
					if (sharedChannel == null) {
						inputHolders.put(name, new ChannelHolder(channelFactory.createSubscribableChannel(name), true));
					}
					else {
						inputHolders.put(name, new ChannelHolder(sharedChannel, false));
					}
				}
			}
		});
		ReflectionUtils.doWithMethods(type, new ReflectionUtils.MethodCallback() {
			@Override
			public void doWith(Method method) throws IllegalArgumentException {
				Output output = AnnotationUtils.findAnnotation(method, Output.class);
				if (output != null) {
					String name = BindingBeanDefinitionRegistryUtils.getChannelName(output, method);
					validateChannelType(method.getReturnType());
					MessageChannel sharedChannel = locateSharedChannel(name);
					if (sharedChannel == null) {
						outputHolders.put(name, new ChannelHolder(channelFactory.createSubscribableChannel(name), true));
					}
					else {
						outputHolders.put(name, new ChannelHolder(sharedChannel, false));
					}
				}
			}

		});
	}

	private void validateChannelType(Class<?> channelType) {
		Assert.isTrue(SubscribableChannel.class.equals(channelType) || MessageChannel.class.equals(channelType),
				"A bound channel should be either a '" + MessageChannel.class.getName() + "', " +
						" or a '" + SubscribableChannel.class.getName() + "'");
	}

	private MessageChannel locateSharedChannel(String name) {
		return this.sharedChannelRegistry != null ?
				this.sharedChannelRegistry.get(getNamespacePrefixedChannelName(name)) : null;
	}

	private String getNamespacePrefixedChannelName(String name) {
		return this.channelNamespace + "." + name;
	}

	@Override
	public synchronized Object getObject() throws Exception {
		if (this.proxy == null) {
			ProxyFactory factory = new ProxyFactory(this.type, this);
			this.proxy = factory.getProxy();
		}
		return this.proxy;
	}

	@Override
	public Class<?> getObjectType() {
		return this.type;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

	
	public void bindInputs(ChannelBindingService channelBindingService) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("Binding inputs for %s:%s", this.channelNamespace, this.type));
		}
		for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.inputHolders.entrySet()) {
			String inputChannelName = channelHolderEntry.getKey();
			ChannelHolder channelHolder = channelHolderEntry.getValue();
			if (channelHolder.isBindable()) {
				if (log.isDebugEnabled()) {
					log.debug(String.format("Binding %s:%s:%s", this.channelNamespace, this.type, inputChannelName));
				}
				channelBindingService.bindConsumer(channelHolder.getMessageChannel(), inputChannelName);
			}
		}
	}

	@Override
	public void bindOutputs(ChannelBindingService channelBindingService) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("Binding outputs for %s:%s", this.channelNamespace, this.type));
		}
		for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.outputHolders.entrySet()) {
			ChannelHolder channelHolder = channelHolderEntry.getValue();
			String outputChannelName = channelHolderEntry.getKey();
			if (channelHolderEntry.getValue().isBindable()) {
				if (log.isDebugEnabled()) {
					log.debug(String.format("Binding %s:%s:%s", this.channelNamespace, this.type, outputChannelName));
				}
				channelBindingService.bindProducer(channelHolder.getMessageChannel(), outputChannelName);
			}
		}
	}

	@Override
	public void unbindInputs(ChannelBindingService channelBindingService) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("Unbinding inputs for %s:%s", this.channelNamespace, this.type));
		}
		for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.inputHolders.entrySet()) {
			if (channelHolderEntry.getValue().isBindable()) {
				if (log.isDebugEnabled()) {
					log.debug(String.format("Unbinding %s:%s:%s", this.channelNamespace, this.type, channelHolderEntry.getKey()));
				}
				channelBindingService.unbindConsumers(channelHolderEntry.getKey());
			}
		}
	}

	@Override
	public void unbindOutputs(ChannelBindingService channelBindingService) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("Unbinding outputs for %s:%s", this.channelNamespace, this.type));
		}
		for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.outputHolders.entrySet()) {
			if (channelHolderEntry.getValue().isBindable()) {
				if (log.isDebugEnabled()) {
					log.debug(String.format("Binding %s:%s:%s", this.channelNamespace, this.type, channelHolderEntry.getKey()));
				}
				channelBindingService.unbindProducers(channelHolderEntry.getKey());
			}
		}
	}

Copiar código

Análisis de clase BinderFactoryConfiguration

@Configuration
public class BinderFactoryConfiguration {

	@Bean
	@ConditionalOnMissingBean(BinderFactory.class)
	public BinderFactory<?> binderFactory(BinderTypeRegistry binderTypeRegistry,
			ChannelBindingServiceProperties channelBindingServiceProperties) {
		Map<String, BinderConfiguration> binderConfigurations = new HashMap<>();
		Map<String, BinderProperties> declaredBinders = channelBindingServiceProperties.getBinders();
		boolean defaultCandidatesExist = false;
		Iterator<Map.Entry<String, BinderProperties>> binderPropertiesIterator = declaredBinders.entrySet().iterator();
		while (!defaultCandidatesExist && binderPropertiesIterator.hasNext()) {
			defaultCandidatesExist = binderPropertiesIterator.next().getValue().isDefaultCandidate();
		}
		for (Map.Entry<String, BinderProperties> binderEntry : declaredBinders.entrySet()) {
			BinderProperties binderProperties = binderEntry.getValue();
			if (binderTypeRegistry.get(binderEntry.getKey()) != null) {
				binderConfigurations.put(binderEntry.getKey(),
						new BinderConfiguration(binderTypeRegistry.get(binderEntry.getKey()),
								binderProperties.getEnvironment(), binderProperties.isInheritEnvironment(),
								binderProperties.isDefaultCandidate()));
			}
			else {
				Assert.hasText(binderProperties.getType(),
						"No 'type' property present for custom binder " + binderEntry.getKey());
				BinderType binderType = binderTypeRegistry.get(binderProperties.getType());
				Assert.notNull(binderType, "Binder type " + binderProperties.getType() + " is not defined");
				binderConfigurations.put(binderEntry.getKey(),
						new BinderConfiguration(binderType, binderProperties.getEnvironment(),
								binderProperties.isInheritEnvironment(), binderProperties.isDefaultCandidate()));
			}
		}
		if (!defaultCandidatesExist) {
			for (Map.Entry<String, BinderType> entry : binderTypeRegistry.getAll().entrySet()) {
				binderConfigurations.put(entry.getKey(),
						new BinderConfiguration(entry.getValue(), new Properties(), true, true));
			}
		}
		DefaultBinderFactory<?> binderFactory = new DefaultBinderFactory<>(binderConfigurations);
		binderFactory.setDefaultBinder(channelBindingServiceProperties.getDefaultBinder());
		return binderFactory;
	}
}

Copiar código

Lo principal es crear una fábrica de DefaultBinderFactory.

El contenido de los tres beans de carga ChannelBindingServiceConfiguration, BindingBeansRegistrar y BinderFactoryConfiguration se han presentado de forma aproximada. Ahora hablemos del proceso de carga:

1. Los objetos Bean outputBindingLifecycle y inputBindingLifecycle cargados por la clase ChannelBindingServiceConfiguration. Usamos inputBindingLifecycle para el análisis y outputBindingLifecycle es similar.

	@Bean
	@DependsOn("bindingService")
	public OutputBindingLifecycle outputBindingLifecycle() {
		return new OutputBindingLifecycle();
	}

	@Bean
	@DependsOn("bindingService")
	public InputBindingLifecycle inputBindingLifecycle() {
		return new InputBindingLifecycle();
	}

Copiar código

La clase inputBindingLifecycle implementa la interfaz SmartLifecycle y el método de inicio se ejecutará después de que comience la primavera.

public class InputBindingLifecycle implements SmartLifecycle, ApplicationContextAware {
	public void start() {
		if (!running) {
			// retrieve the ChannelBindingService lazily, avoiding early initialization
			try {
				ChannelBindingService channelBindingService = this.applicationContext
						.getBean(ChannelBindingService.class);
				Map<String, Bindable> bindables = this.applicationContext
						.getBeansOfType(Bindable.class);
				for (Bindable bindable : bindables.values()) {
                                        //bindables.values即为@@EnableBinding({StreamSendClient.class})类,BeanClass为BindableProxyFactory
					bindable.bindInputs(channelBindingService);
				}
			}
			catch (BeansException e) {
				throw new IllegalStateException(
						"Cannot perform binding, no proper implementation found", e);
			}
			this.running = true;
		}
	}
}

BindableProxyFactory.bindInputs方法如下:
	public void bindInputs(ChannelBindingService channelBindingService) {
		if (log.isDebugEnabled()) {
			log.debug(String.format("Binding inputs for %s:%s", this.channelNamespace, this.type));
		}
		for (Map.Entry<String, ChannelHolder> channelHolderEntry : this.inputHolders.entrySet()) {
			String inputChannelName = channelHolderEntry.getKey();
			ChannelHolder channelHolder = channelHolderEntry.getValue();
			if (channelHolder.isBindable()) {
				if (log.isDebugEnabled()) {
					log.debug(String.format("Binding %s:%s:%s", this.channelNamespace, this.type, inputChannelName));
				}
                              //这里继续进入
                              channelBindingService.bindConsumer(channelHolder.getMessageChannel(), inputChannelName);
			}
		}

	public Collection<Binding<MessageChannel>> bindConsumer(MessageChannel inputChannel, String inputChannelName) {
		....................
		validate(consumerProperties);
		for (String target : channelBindingTargets) {
                        //继续进入binder.bindConsumer方法
			Binding<MessageChannel> binding = binder.bindConsumer(target, channelBindingServiceProperties.getGroup(inputChannelName), inputChannel, consumerProperties);
			bindings.add(binding);
		}
		this.consumerBindings.put(inputChannelName, bindings);
		return bindings;
	}
	}

会进入RabbitMessageChannelBinder.bindConsumer方法
	public Binding<MessageChannel> doBindConsumer(String name, String group, MessageChannel inputChannel,
			ExtendedConsumerProperties<RabbitConsumerProperties> properties) {
		
		String prefix = properties.getExtension().getPrefix();
		String exchangeName = applyPrefix(prefix, name);
		TopicExchange exchange = new TopicExchange(exchangeName);
                //创建交换器
		declareExchange(exchangeName, exchange);

		String queueName = applyPrefix(prefix, baseQueueName);
		boolean partitioned = !anonymousConsumer && properties.isPartitioned();
		boolean durable = !anonymousConsumer && properties.getExtension().isDurableSubscription();
		Queue queue;

		......................
                //创建队列
		declareQueue(queueName, queue);

                if (partitioned) {
			String bindingKey = String.format("%s-%d", name, properties.getInstanceIndex());
			declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with(bindingKey));
		}
		else {
			declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with("#"));
		}
                Binding<MessageChannel> binding = doRegisterConsumer(baseQueueName, group, inputChannel, queue, properties);
		.................
		return binding;
	}

Copiar código

Puede ver que el intercambio RabbitMq (Exchange) y la cola se crean a través de inputBindingLifecycle.

De la misma manera, se creará un productor después de comenzar con outputBindingLifecycle.

2. El objeto Bean StreamListenerAnnotationBeanPostProcessor cargado por la clase ChannelBindingServiceConfiguration. StreamListenerAnnotationBeanPostProcessor implementa la interfaz BeanPostProcessor. Se ejecutará el método postProcessAfterInitialization.

public class StreamListenerAnnotationBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, SmartInitializingSingleton {

    	public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
		Class<?> targetClass = AopUtils.isAopProxy(bean) ? AopUtils.getTargetClass(bean) : bean.getClass();
		ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
			@Override
			public void doWith(final Method method) throws IllegalArgumentException, IllegalAccessException {
                                // 步骤1
				StreamListener streamListener = AnnotationUtils.findAnnotation(method, StreamListener.class);
				if (streamListener != null) {
					Method targetMethod = checkProxy(method, bean);
					Assert.hasText(streamListener.value(), "The binding name cannot be null");
                                        //步骤2
					final InvocableHandlerMethod invocableHandlerMethod = messageHandlerMethodFactory.createInvocableHandlerMethod(bean, targetMethod);
					if (!StringUtils.hasText(streamListener.value())) {
						throw new BeanInitializationException("A bound component name must be specified");
					}
					if (mappedBindings.containsKey(streamListener.value())) {
						throw new BeanInitializationException("Duplicate @" + StreamListener.class.getSimpleName() +
								" mapping for '" + streamListener.value() + "' on " + invocableHandlerMethod.getShortLogMessage() +
								" already existing for " + mappedBindings.get(streamListener.value()).getShortLogMessage());
					}
					mappedBindings.put(streamListener.value(), invocableHandlerMethod);
                                        //步骤3
					SubscribableChannel channel = applicationContext.getBean(streamListener.value(),
							SubscribableChannel.class);
					final String defaultOutputChannel = extractDefaultOutput(method);
					if (invocableHandlerMethod.isVoid()) {
						Assert.isTrue(StringUtils.isEmpty(defaultOutputChannel), "An output channel cannot be specified for a method that " +
								"does not return a value");
					}
					else {
						Assert.isTrue(!StringUtils.isEmpty(defaultOutputChannel), "An output channel must be specified for a method that " +
								"can return a value");
					}
                                        //步骤4
					StreamListenerMessageHandler handler = new StreamListenerMessageHandler(invocableHandlerMethod);
					handler.setApplicationContext(applicationContext);
					handler.setChannelResolver(binderAwareChannelResolver);
					if (!StringUtils.isEmpty(defaultOutputChannel)) {
						handler.setOutputChannelName(defaultOutputChannel);
					}
					handler.afterPropertiesSet();
                                        //步骤5
					channel.subscribe(handler);
				}
			}
		});
		return bean;
	}
}

Copiar código

Proceso del método postProcessAfterInitialization:

1. Busque el método que contiene @StreamListener ("testMessage")

2. Cree una clase proxy InvocableHandlerMethod, que ejecuta el método específico que creamos.

3. El valor de streamListener.value () es testMessage, lo que realmente obtiene el Bean es el objeto Bean testMessage obtenido por la clase de fábrica StreamSendClient y el método input (). La forma específica de generar este Bean se mencionó anteriormente en el método BindingBeanDefinitionRegistryUtils.registerChannelBeanDefinitions. Encuentre el Bean SubscribableChannel correspondiente a las anotaciones @input y @output generadas previamente por el objeto BindableProxyFactory a través del método afterPropertiesSet.

PD: al obtener el objeto testMessage Bean, primero busque la clase StreamSendClient y luego llame al método de entrada. Porque rootBeanDefinition.setFactoryBeanName(StreamSendClient); se configuró anteriormente.

rootBeanDefinition.setUniqueFactoryMethodName(entrada);

4. Cree un objeto StreamListenerMessageHandler y utilice InvocableHandlerMethod como parámetro de entrada en el constructor.

5. Agregue SubscribableChannel y suscríbase a StreamListenerMessageHandler.

De esta manera, cuando llega el mensaje, InvocableHandlerMethod en StreamListenerMessageHandler encontrará un método específico para manejarlo.

========Línea de separación: mensaje de Rabbit que encuentra el código fuente del método correspondiente análisis en profundidad=======================

Sabemos que en el método RabbitMessageChannelBinder.bindConsumer hace un momento

RabbitMessageChannelBinder.bindConsumer方法
	public Binding<MessageChannel> doBindConsumer(String name, String group, MessageChannel inputChannel,
			ExtendedConsumerProperties<RabbitConsumerProperties> properties) {
		
		String prefix = properties.getExtension().getPrefix();
		String exchangeName = applyPrefix(prefix, name);
		TopicExchange exchange = new TopicExchange(exchangeName);
                //创建交换器
		declareExchange(exchangeName, exchange);

		String queueName = applyPrefix(prefix, baseQueueName);
		boolean partitioned = !anonymousConsumer && properties.isPartitioned();
		boolean durable = !anonymousConsumer && properties.getExtension().isDurableSubscription();
		Queue queue;

		......................
                //创建队列
		declareQueue(queueName, queue);

                if (partitioned) {
			String bindingKey = String.format("%s-%d", name, properties.getInstanceIndex());
			declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with(bindingKey));
		}
		else {
			declareBinding(queue.getName(), BindingBuilder.bind(queue).to(exchange).with("#"));
		}
                Binding<MessageChannel> binding = doRegisterConsumer(baseQueueName, group, inputChannel, queue, properties);
		.................
		return binding;
	}

Copiar código

El último método doRegisterConsumer es donde se encuentra el método de procesamiento correspondiente del mensaje RabbitMq.

	private Binding<MessageChannel> doRegisterConsumer(final String name, String group, MessageChannel moduleInputChannel, Queue queue,
			final ExtendedConsumerProperties<RabbitConsumerProperties> properties) {
		DefaultBinding<MessageChannel> consumerBinding;
		SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer(
				this.connectionFactory);
		listenerContainer.setAcknowledgeMode(properties.getExtension().getAcknowledgeMode());
		listenerContainer.setChannelTransacted(properties.getExtension().isTransacted());
		listenerContainer.setDefaultRequeueRejected(properties.getExtension().isRequeueRejected());
		int concurrency = properties.getConcurrency();
		concurrency = concurrency > 0 ? concurrency : 1;
		listenerContainer.setConcurrentConsumers(concurrency);
		int maxConcurrency = properties.getExtension().getMaxConcurrency();
		if (maxConcurrency > concurrency) {
			listenerContainer.setMaxConcurrentConsumers(maxConcurrency);
		}
		listenerContainer.setPrefetchCount(properties.getExtension().getPrefetch());
		listenerContainer.setRecoveryInterval(properties.getExtension().getRecoveryInterval());
		listenerContainer.setTxSize(properties.getExtension().getTxSize());
		listenerContainer.setTaskExecutor(new SimpleAsyncTaskExecutor(queue.getName() + "-"));
		listenerContainer.setQueues(queue);
		int maxAttempts = properties.getMaxAttempts();
		if (maxAttempts > 1 || properties.getExtension().isRepublishToDlq()) {
			RetryOperationsInterceptor retryInterceptor = RetryInterceptorBuilder.stateless()
					.maxAttempts(maxAttempts)
					.backOffOptions(properties.getBackOffInitialInterval(),
							properties.getBackOffMultiplier(),
							properties.getBackOffMaxInterval())
					.recoverer(determineRecoverer(name, properties.getExtension().getPrefix(), properties.getExtension().isRepublishToDlq()))
					.build();
			listenerContainer.setAdviceChain(new Advice[] { retryInterceptor });
		}
		listenerContainer.setAfterReceivePostProcessors(this.decompressingPostProcessor);
		listenerContainer.setMessagePropertiesConverter(RabbitMessageChannelBinder.inboundMessagePropertiesConverter);
		listenerContainer.afterPropertiesSet();
		AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(listenerContainer);
		adapter.setBeanFactory(this.getBeanFactory());
		DirectChannel bridgeToModuleChannel = new DirectChannel();
		bridgeToModuleChannel.setBeanFactory(this.getBeanFactory());
		bridgeToModuleChannel.setBeanName(name + ".bridge");
		adapter.setOutputChannel(bridgeToModuleChannel);
		adapter.setBeanName("inbound." + name);
		DefaultAmqpHeaderMapper mapper = new DefaultAmqpHeaderMapper();
		mapper.setRequestHeaderNames(properties.getExtension().getRequestHeaderPatterns());
		mapper.setReplyHeaderNames(properties.getExtension().getReplyHeaderPatterns());
		adapter.setHeaderMapper(mapper);
		adapter.afterPropertiesSet();
		consumerBinding = new DefaultBinding<MessageChannel>(name, group, moduleInputChannel, adapter) {
			@Override
			protected void afterUnbind() {
				cleanAutoDeclareContext(properties.getExtension().getPrefix(), name);
			}
		};
		ReceivingHandler convertingBridge = new ReceivingHandler();
		convertingBridge.setOutputChannel(moduleInputChannel);
		convertingBridge.setBeanName(name + ".convert.bridge");
		convertingBridge.afterPropertiesSet();
		bridgeToModuleChannel.subscribe(convertingBridge);
		adapter.start();
		return consumerBinding;
	}

Copiar código

1. Puede ver que Spring Stream ha creado SimpleMessageListenerContainer. La función de esta clase es que RabbitMQ recibe mensajes y deja que el método correspondiente los procese. En SpringRabbit, puede encontrar la anotación @RabbitListener. Para un análisis detallado, puede ver el anterior artículo sobre SpringBoot integrando Rabbit .

Aquí solo necesitamos saber que SimpleMessageListenerContainer es un contenedor responsable de interactuar con RabbitMQ y los métodos de escucha locales.

2. Luego cree un objeto AmqpInboundChannelAdapter y el parámetro de entrada es SimpleMessageListenerContainer. Y creó un objeto DirectChannel llamado bridgeToModuleChannel y configuró el OutputChannel del adaptador en DirectChannel. Luego llame al método afterPropertiesSet del adaptador. El método afterPropertiesSet llamará a su propio método onInit.

	protected void onInit() {
		this.messageListenerContainer.setMessageListener(new ChannelAwareMessageListener() {

			@Override
			public void onMessage(Message message, Channel channel) throws Exception {
				Object payload = AmqpInboundChannelAdapter.this.messageConverter.fromMessage(message);
				Map<String, Object> headers =
						AmqpInboundChannelAdapter.this.headerMapper.toHeadersFromRequest(message.getMessageProperties());
				if (AmqpInboundChannelAdapter.this.messageListenerContainer.getAcknowledgeMode()
						== AcknowledgeMode.MANUAL) {
					headers.put(AmqpHeaders.DELIVERY_TAG, message.getMessageProperties().getDeliveryTag());
					headers.put(AmqpHeaders.CHANNEL, channel);
				}
				sendMessage(getMessageBuilderFactory().withPayload(payload).copyHeaders(headers).build());
			}

		});
		this.messageListenerContainer.afterPropertiesSet();
		super.onInit();
	}

Copiar código

Como puede ver en el método onInit, la escucha de mensajes de SimpleMessageListenerContainer se establece en un método personalizado que implementa la interfaz ChannelAwareMessageListener. En la integración de SpringBoot de RabbitMQ, el objeto de setMessageListener es MessagingMessageListenerAdapter.

3. Cree un objeto ReceivingHandler. El objeto ReceivingHandler implementa la interfaz MessageHandler. Y configure OutputChannel de ReceivingHandler en el canal de mensajes creado por BindableProxyFactory. Se analizó anteriormente que en el postProcessAfterInitialization del objeto StreamListenerAnnotationBeanPostProcessor, el canal de mensajes se agregó a StreamListenerMessageHandler y el Bean y el método de procesamiento específicos se crearon en StreamListenerMessageHandler.

4. Agregue el observador ReceivingHandler a bridgeToModuleChannel.

A continuación se analizan los pasos experimentados cuando llega un mensaje MQ:

1. Primero, se llamará al método onMessage de SimpleMessageListenerContainer. Para un análisis detallado, puede leer el artículo anterior sobre SpringBoot integrando Rabbit.

2. onMessage llamará al método sendMessage.

	public void onMessage(Message message, Channel channel) throws Exception {
				Object payload = AmqpInboundChannelAdapter.this.messageConverter.fromMessage(message);
				Map<String, Object> headers =
						AmqpInboundChannelAdapter.this.headerMapper.toHeadersFromRequest(message.getMessageProperties());
				if (AmqpInboundChannelAdapter.this.messageListenerContainer.getAcknowledgeMode()
						== AcknowledgeMode.MANUAL) {
					headers.put(AmqpHeaders.DELIVERY_TAG, message.getMessageProperties().getDeliveryTag());
					headers.put(AmqpHeaders.CHANNEL, channel);
				}
				sendMessage(getMessageBuilderFactory().withPayload(payload).copyHeaders(headers).build());
	}
        protected void sendMessage(Message<?> message) {
		................
		try {
                        //这里的OutputChannel就是之前的bridgeToModuleChannel
			this.messagingTemplate.send(getOutputChannel(), message);
		}
                ................
	}
	public void send(D destination, Message<?> message) {
		doSend(destination, message);
	}
	protected final void doSend(MessageChannel channel, Message<?> message) {
		......................

		boolean sent = (timeout >= 0 ? channel.send(message, timeout) : channel.send(message));

		..................
	}

Copiar código

El método channel.send(message) llamará al método de envío de AbstractMessageChannel

①:

	public final boolean send(Message<?> message, long timeout) {
		Assert.notNull(message, "message must not be null");
		Assert.notNull(message.getPayload(), "message payload must not be null");
		.............
			sent = this.doSend(message, timeout);
			if (countsEnabled) {
				channelMetrics.afterSend(metrics, sent);
				metricsProcessed = true;
			}

			if (debugEnabled) {
				logger.debug("postSend (sent=" + sent + ") on channel '" + this + "', message: " + message);
			}
			if (interceptorStack != null) {
				interceptors.postSend(message, this, sent);
				interceptors.afterSendCompletion(message, this, sent, null, interceptorStack);
			}
			return sent;

		....................
	}

Copiar código

El método doSend aquí llamará al método doSend de AbstractSubscribableChannel

②:

	protected boolean doSend(Message<?> message, long timeout) {
		try {
			return getRequiredDispatcher().dispatch(message);
		}
		catch (MessageDispatchingException e) {
			String description = e.getMessage() + " for channel '" + this.getFullChannelName() + "'.";
			throw new MessageDeliveryException(message, description, e);
		}
	}
        //UnicastingDispatcher类的dispatch方法
	public final boolean dispatch(final Message<?> message) {
		if (this.executor != null) {
			Runnable task = createMessageHandlingTask(message);
			this.executor.execute(task);
			return true;
		}
		return this.doDispatch(message);
	}

	private boolean doDispatch(Message<?> message) {
		if (this.tryOptimizedDispatch(message)) {
			return true;
		}
		....................
		return success;
	}
	protected boolean tryOptimizedDispatch(Message<?> message) {
		MessageHandler handler = this.theOneHandler;
		if (handler != null) {
			try {
				handler.handleMessage(message);
				return true;
			}
			catch (Exception e) {
				throw wrapExceptionIfNecessary(message, e);
			}
		}
		return false;
	}

Copiar código

Finalmente, se llamará al método handler.handleMessage. Desde la creación de ahora, podemos ver que el controlador de bridgeToModuleChannel es ReceivingHandler. Entonces se llamará al método ReceivingHandler.handleMessage. ReceivingHandler hereda de AbstractReplyProtainingMessageHandler, y AbstractReplyProtainingMessageHandler hereda de AbstractMessageHandler. Entonces se llamará al método AbstractMessageHandler.handleMessage.

③:

	public final void handleMessage(Message<?> message) {
	        ................
		try {
			............
			this.handleMessageInternal(message);
			............
		}
		catch (Exception e) {
			...........
		}
	}

Copiar código

Luego se ejecutará el método AbstractReplyProtainingMessageHandler.handleMessageInternal.

④:

	protected final void handleMessageInternal(Message<?> message) {
		Object result;
		if (this.advisedRequestHandler == null) {
			result = handleRequestMessage(message);
		}
		else {
			result = doInvokeAdvisedRequestHandler(message);
		}
		if (result != null) {
			sendOutputs(result, message);
		}
		else if (this.requiresReply && !isAsync()) {
			throw new ReplyRequiredException(message, "No reply produced by handler '" +
					getComponentName() + "', and its 'requiresReply' property is set to true.");
		}
		else if (!isAsync() && logger.isDebugEnabled()) {
			logger.debug("handler '" + this + "' produced no reply for request Message: " + message);
		}
	}

Copiar código

 Se ejecutará el método ReceivingHandler.handleRequestMessage para deserializar el mensaje, etc. sendOutputs se ejecutará más tarde.

	protected void sendOutputs(Object result, Message<?> requestMessage) {
		if (result instanceof Iterable<?> && shouldSplitOutput((Iterable<?>) result)) {
			for (Object o : (Iterable<?>) result) {
				this.produceOutput(o, requestMessage);
			}
		}
		else if (result != null) {
			this.produceOutput(result, requestMessage);
		}
	}

	protected void produceOutput(Object reply, final Message<?> requestMessage) {
		final MessageHeaders requestHeaders = requestMessage.getHeaders();

		Object replyChannel = null;
		if (getOutputChannel() == null) {
			............
	
		}

		if (this.async && reply instanceof ListenableFuture<?>) {
		    .......................
		}
		else {
			sendOutput(createOutputMessage(reply, requestHeaders), replyChannel, false);
		}
	}

Copiar código

	protected void sendOutput(Object output, Object replyChannel, boolean useArgChannel) {
		MessageChannel outputChannel = getOutputChannel();
		....................

		if (replyChannel instanceof MessageChannel) {
			if (output instanceof Message<?>) {
				this.messagingTemplate.send((MessageChannel) replyChannel, (Message<?>) output);
			}
			else {
				this.messagingTemplate.convertAndSend((MessageChannel) replyChannel, output);
			}
		}
		..................
	}

Copiar código

Aquí el getOutputChannel de ReceivingHandler.sendOutput es el canal de mensajes creado por BindableProxyFactory.

3. This.messagingTemplate.send((MessageChannel) ReplyChannel, (Message<?>) Output) se llamará más tarde, es decir, se repetirán los pasos anteriores ①, ②, ③ y ④. Ingrese el método AbstractReplyProtainingMessageHandler.handleMessageInternal.

	protected final void handleMessageInternal(Message<?> message) {
		Object result;
		if (this.advisedRequestHandler == null) {
			result = handleRequestMessage(message);
		}
		else {
			result = doInvokeAdvisedRequestHandler(message);
		}
		if (result != null) {
			sendOutputs(result, message);
		}
		else if (this.requiresReply && !isAsync()) {
			throw new ReplyRequiredException(message, "No reply produced by handler '" +
					getComponentName() + "', and its 'requiresReply' property is set to true.");
		}
		else if (!isAsync() && logger.isDebugEnabled()) {
			logger.debug("handler '" + this + "' produced no reply for request Message: " + message);
		}
	}

Copiar código

Pero el canal de mensajes en este momento lo crea BindableProxyFactory, y el observador en este momento es StreamListenerMessageHandler.

		protected Object handleRequestMessage(Message<?> requestMessage) {
			try {
				return invocableHandlerMethod.invoke(requestMessage);
			}
			catch (Exception e) {
				if (e instanceof MessagingException) {
					throw (MessagingException) e;
				}
				else {
					throw new MessagingException(requestMessage, "Exception thrown while invoking " + invocableHandlerMethod.getShortLogMessage(), e);
				}
			}
		}

Copiar código

Del análisis del código fuente anterior, podemos saber que invocableHandlerMethod ha encapsulado el Bean y el Método correspondientes. Esto completa el análisis para encontrar el método correspondiente de RabbitMQ a Spring Stream.

Resumen del código fuente del método correspondiente para encontrar el mensaje de Spring Stream Rabbit:

1. Spring Stream creó SimpleMessageListenerContainer para escuchar el servidor RabbitMQ.

2. Cree un objeto AmqpInboundChannelAdapter y el parámetro de entrada es SimpleMessageListenerContainer. Los dos están asociados, de modo que después de recibir la información, SimpleMessageListenerContainer llama a la información onMessage creada en el método onInit de AmqpInboundChannelAdapter.

3. Cree un objeto DirectChannel denominado bridgeToModuleChannel y establezca el OutputChannel del Adaptador en DirectChannel.

4. Cree un observador ReceivingHandler para observar bridgeToModuleChannel. Y configure OutputChannel de ReceivingHandler en el canal de mensajes creado por BindableProxyFactory.

5. Después de que RabbitMQ envíe información, irá al método ReceivingHandler.handleRequestMessage por primera vez para deserializar el mensaje, etc. sendOutputs se ejecutará más tarde.

6. Después de enviar Outputs nuevamente, se llamará al observador StreamListenerMessageHandler del canal de mensajes creado por BindableProxyFactory. El método StreamListenerMessageHandler.handleRequestMessage encapsulará el Bean y el método correspondientes a través de la llamada invocableHandlerMethod. Para encontrar el método de clase correspondiente.

Lo anterior es un análisis simple de Spring Cloud Stream: el uso de Spring Stream debe combinarse con Spring Integration.

 

Supongo que te gusta

Origin blog.csdn.net/lqzixi/article/details/130792603
Recomendado
Clasificación