一直被@StreamListener註解帶來的恐懼所支配。今天來揭開它的面紗。
MAVEN引入相關jar包(版本2.0.1)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
@StreamListener註解使用
@StreamListener
public void test(){
}
相關源碼分析
先點開@StreamListener註解源碼:
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@MessageMapping
@Documented
public @interface StreamListener {
/**
* The name of the binding target (e.g. channel) that the method subscribes to.
* @return the name of the binding target.
*/
@AliasFor("target")
String value() default "";
/**
* The name of the binding target (e.g. channel) that the method subscribes to.
* @return the name of the binding target.
*/
@AliasFor("value")
String target() default "";
/**
* A condition that must be met by all items that are dispatched to this method.
* @return a SpEL expression that must evaluate to a {@code boolean} value.
*/
String condition() default "";
/**
* When "true" (default), and a {@code @SendTo} annotation is present, copy the
* inbound headers to the outbound message (if the header is absent on the outbound
* message). Can be an expression ({@code #{...}}) or property placeholder. Must
* resolve to a boolean or a string that is parsed by {@code Boolean.parseBoolean()}.
* An expression that resolves to {@code null} is interpreted to mean {@code false}.
*
* The expression is evaluated during application initialization, and not for each
* individual message.
*
* Prior to version 1.3.0, the default value used to be "false" and headers were
* not propagated by default.
*
* Starting with version 1.3.0, the default value is "true".
*
* @since 1.2.3
*/
String copyHeaders() default "true";
}
這是它所在的包結構:
選中@StreamListener註解value()方法看誰調用了它:
可以看到下面的方法又調用了上面的方法,所以只需要展開下面的方法即可:
直接點擊run()方法,就到了處理@StreamListener註解的代碼入口(部分代碼):
public class StreamListenerAnnotationBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, SmartInitializingSingleton {
@Override
public final Object postProcessAfterInitialization(Object bean, final String beanName) throws BeansException {
Class<?> targetClass = AopUtils.isAopProxy(bean) ? AopUtils.getTargetClass(bean) : bean.getClass(); //找到bean對象的真實類型
Method[] uniqueDeclaredMethods = ReflectionUtils.getUniqueDeclaredMethods(targetClass); //找到bean對象的方法
//逐個遍歷並處理聲明瞭@StreamListener註解的方法
for (Method method : uniqueDeclaredMethods) {
//找聲明瞭@StreamListener註解的方法
StreamListener streamListener = AnnotatedElementUtils.findMergedAnnotation(method, StreamListener.class);
if (streamListener != null && !method.isBridge()) {
streamListenerCallbacks.add(() -> {
Assert.isTrue(method.getAnnotation(Input.class) == null, StreamListenerErrorMessages.INPUT_AT_STREAM_LISTENER); //被@StreamListener註解聲明的方法上不允許再聲明@Input註解
this.doPostProcess(streamListener, method, bean);
});
}
}
return bean;
}
}
我們看看doPostProcess()方法中做了啥
private void doPostProcess(StreamListener streamListener, Method method, Object bean) {
streamListener = postProcessAnnotation(streamListener, method);
Optional<StreamListenerSetupMethodOrchestrator> streamListenerSetupMethodOrchestratorAvailable =
streamListenerSetupMethodOrchestrators.stream()
.filter(t -> t.supports(method))
.findFirst();
Assert.isTrue(streamListenerSetupMethodOrchestratorAvailable.isPresent(),
"A matching StreamListenerSetupMethodOrchestrator must be present");
StreamListenerSetupMethodOrchestrator streamListenerSetupMethodOrchestrator = streamListenerSetupMethodOrchestratorAvailable.get();
streamListenerSetupMethodOrchestrator.orchestrateStreamListenerSetupMethod(streamListener, method, bean);
}
//啥也沒幹的一個方法,其實是一個擴展點
protected StreamListener postProcessAnnotation(StreamListener originalAnnotation, Method annotatedMethod) {
return originalAnnotation;
}
StreamListenerSetupMethodOrchestrator類是個接口,它的實現在StreamListenerAnnotationBeanPostProcessor類的內部類DefaultStreamListenerSetupMethodOrchestrator中:
private class DefaultStreamListenerSetupMethodOrchestrator implements StreamListenerSetupMethodOrchestrator
@Override
public void orchestrateStreamListenerSetupMethod(StreamListener streamListener, Method method, Object bean) {
String methodAnnotatedInboundName = streamListener.value(); //處理@StreamListener註解的value值
String methodAnnotatedOutboundName = StreamListenerMethodUtils.getOutboundBindingTargetName(method); //解析輸出通道
int inputAnnotationCount = StreamListenerMethodUtils.inputAnnotationCount(method);
int outputAnnotationCount = StreamListenerMethodUtils.outputAnnotationCount(method);
boolean isDeclarative = checkDeclarativeMethod(method, methodAnnotatedInboundName, methodAnnotatedOutboundName);
StreamListenerMethodUtils.validateStreamListenerMethod(method,
inputAnnotationCount, outputAnnotationCount,
methodAnnotatedInboundName, methodAnnotatedOutboundName,
isDeclarative, streamListener.condition());
if (isDeclarative) {
StreamListenerParameterAdapter[] toSlpaArray = new StreamListenerParameterAdapter[this.streamListenerParameterAdapters.size()];
Object[] adaptedInboundArguments = adaptAndRetrieveInboundArguments(method, methodAnnotatedInboundName,
this.applicationContext,
this.streamListenerParameterAdapters.toArray(toSlpaArray));
invokeStreamListenerResultAdapter(method, bean, methodAnnotatedOutboundName, adaptedInboundArguments);
} else {
registerHandlerMethodOnListenedChannel(method, streamListener, bean);
}
}
}
可以看到,這段代碼中完成了對@StreamListener註解的解析,並在最後調用了registerHandlerMethodOnListenedChannel()方法完成了註冊,看一下具體實現:
private void registerHandlerMethodOnListenedChannel(Method method, StreamListener streamListener, Object bean) {
Assert.hasText(streamListener.value(), "The binding name cannot be null");
if (!StringUtils.hasText(streamListener.value())) {
throw new BeanInitializationException("A bound component name must be specified");
}
final String defaultOutputChannel = StreamListenerMethodUtils.getOutboundBindingTargetName(method);
if (Void.TYPE.equals(method.getReturnType())) {
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");
}
StreamListenerMethodUtils.validateStreamListenerMessageHandler(method);
mappedListenerMethods.add(streamListener.value(),
new StreamListenerHandlerMethodMapping(bean, method, streamListener.condition(), defaultOutputChannel,
streamListener.copyHeaders()));
}
所以呢,最後的結果是,@StreamListener註解標註的方法會在系統啓動時由StreamListenerAnnotationBeanPostProcessor類發起解析,然後被StreamListenerAnnotationBeanPostProcessor類的內部類DefaultStreamListenerSetupMethodOrchestrator解析並註冊到mappedListenerMethods中,看一下mappedListenerMethods是個啥:
private final MultiValueMap<String, StreamListenerHandlerMethodMapping> mappedListenerMethods = new LinkedMultiValueMap<>();
額…一個map而已。name問題來了,註冊完了,它是怎麼工作的?在StreamListenerAnnotationBeanPostProcessor類全局搜mappedListenerMethods,可以看到它出現在了這段代碼裏:
@Override
public final void afterSingletonsInstantiated() {
this.injectAndPostProcessDependencies();
EvaluationContext evaluationContext = IntegrationContextUtils.getEvaluationContext(this.applicationContext.getBeanFactory());
for (Map.Entry<String, List<StreamListenerHandlerMethodMapping>> mappedBindingEntry : mappedListenerMethods
.entrySet()) {
ArrayList<DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper> handlers = new ArrayList<>();
for (StreamListenerHandlerMethodMapping mapping : mappedBindingEntry.getValue()) {
final InvocableHandlerMethod invocableHandlerMethod = this.messageHandlerMethodFactory
.createInvocableHandlerMethod(mapping.getTargetBean(),
checkProxy(mapping.getMethod(), mapping.getTargetBean()));
StreamListenerMessageHandler streamListenerMessageHandler = new StreamListenerMessageHandler(
invocableHandlerMethod, resolveExpressionAsBoolean(mapping.getCopyHeaders(), "copyHeaders"),
springIntegrationProperties.getMessageHandlerNotPropagatedHeaders());
streamListenerMessageHandler.setApplicationContext(this.applicationContext);
streamListenerMessageHandler.setBeanFactory(this.applicationContext.getBeanFactory());
if (StringUtils.hasText(mapping.getDefaultOutputChannel())) {
streamListenerMessageHandler.setOutputChannelName(mapping.getDefaultOutputChannel());
}
streamListenerMessageHandler.afterPropertiesSet();
if (StringUtils.hasText(mapping.getCondition())) {
String conditionAsString = resolveExpressionAsString(mapping.getCondition(), "condition");
Expression condition = SPEL_EXPRESSION_PARSER.parseExpression(conditionAsString);
handlers.add(
new DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper(
condition, streamListenerMessageHandler));
}
else {
handlers.add(
new DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper(
null, streamListenerMessageHandler));
}
}
if (handlers.size() > 1) {
for (DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper handler : handlers) {
Assert.isTrue(handler.isVoid(), StreamListenerErrorMessages.MULTIPLE_VALUE_RETURNING_METHODS);
}
}
AbstractReplyProducingMessageHandler handler;
if (handlers.size() > 1 || handlers.get(0).getCondition() != null) {
handler = new DispatchingStreamListenerMessageHandler(handlers, evaluationContext);
}
else {
handler = handlers.get(0).getStreamListenerMessageHandler();
}
handler.setApplicationContext(this.applicationContext);
handler.setChannelResolver(this.binderAwareChannelResolver);
handler.afterPropertiesSet();
this.applicationContext.getBeanFactory().registerSingleton(handler.getClass().getSimpleName() + handler.hashCode(), handler);
applicationContext.getBean(mappedBindingEntry.getKey(), SubscribableChannel.class).subscribe(handler);
}
this.mappedListenerMethods.clear();
}
真是好頭疼的一大段代碼啊,完全不想看幹嘛的。所以,偷個懶?簡化下它:
@Override
public final void afterSingletonsInstantiated() {
for (Map.Entry<String, List<StreamListenerHandlerMethodMapping>> mappedBindingEntry : mappedListenerMethods
.entrySet()) {
applicationContext.getBean(mappedBindingEntry.getKey(), SubscribableChannel.class).subscribe(handler);
}
this.mappedListenerMethods.clear();
}
是不是舒服多了?做了什麼一目瞭然啊!遍歷整個mappedListenerMethods,按個取出元素,一同猛如虎的操作,然後取出了bean名稱爲mappedBindingEntry.getKey(),類型爲SubscribableChannel的bean對象執行了下subscribe()方法。然後,就訂閱了MQ消息???
嗯,終於看完了。就這樣吧。