自定义注解,mq消费
1.解析注解方式的mq消费者
2.注解的定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface JmqListener {
String id() default "";
String[] topics() default {};
}
3.注解的使用
@Component
public class MqConsumer {
@JmqListener(id = "alarmRecordsConsumer", topics = {"${alarmRecordsConsumer.receive.alarm.topic}"})
public void onMessage(List<Message> messages) {}
}
4.注解如何生效的
4.1 定义了一个实现了BeanPostProcessor接口的实现类JmqListenerRegister
package com.xx.jmq.client.springboot.annotation;
import com.xx.jmq.client.consumer.Consumer;
import com.xx.jmq.client.consumer.MessageListener;
import com.xx.jmq.client.springboot.configuration.JmqClient;
import com.xx.jmq.client.springboot.configuration.JmqConfigurationProperties;
import com.xx.jmq.common.message.Message;
import com.xx.jmq.client.springboot.adaptor.MessageListenerReflectAdapter;
import com.xx.jmq.client.springboot.configuration.policy.JmqConsumerPolicy;
import com.xx.jmq.client.springboot.support.ConsumerContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.StringValueResolver;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Objects;
public class JmqListenerRegister implements BeanPostProcessor, EmbeddedValueResolverAware, ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(JmqListenerRegister.class);
private final JmqConfigurationProperties jmqConfigurationProperties;
private GenericApplicationContext applicationContext;
private StringValueResolver stringValueResolver;
public JmqListenerRegister(JmqConfigurationProperties jmqConfigurationProperties) {
this.jmqConfigurationProperties = jmqConfigurationProperties;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = AopUtils.getTargetClass(bean);
for (Method method : beanClass.getDeclaredMethods()) {
JmqListener jmqListenerAnnotation = AnnotationUtils.findAnnotation(method, JmqListener.class);
if (jmqListenerAnnotation == null) {
continue;
}
MessageListener messageListener = getMethodMessageListener(bean, method);
JmqClient<JmqConsumerPolicy> jmqClient = jmqConfigurationProperties.getConsumers().get(jmqListenerAnnotation.id());
if (jmqClient == null || !jmqClient.getEnabled()) {
continue;
}
if (jmqClient.getPolicy() == null) {
if (jmqConfigurationProperties.getGlobalConsumerPolicy() != null) {
jmqClient.setPolicy(jmqConfigurationProperties.getGlobalConsumerPolicy());
} else {
jmqClient.setPolicy(new JmqConsumerPolicy());
}
}
registerListener(jmqListenerAnnotation, jmqClient, messageListener);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
return bean;
}
protected MessageListener getMethodMessageListener(Object bean, Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
boolean isListener = (parameterTypes.length == 1 &&
genericParameterTypes[0].getTypeName().equals(String.format("java.util.List<%s>", Message.class.getName())));
if (!isListener) {
throw new IllegalArgumentException("listener parameters error, need MessageListener.onMessage");
}
return new MessageListenerReflectAdapter(bean, method);
}
protected void registerListener(JmqListener jmqListener, JmqClient<JmqConsumerPolicy> jmqClient, MessageListener messageListener) {
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
if (!applicationContext.containsBean(jmqListener.id())) {
ConsumerContainer consumerContainer = new ConsumerContainer(jmqClient);
Object wrapperBean = beanFactory.initializeBean(consumerContainer, "consumerContainer." + jmqListener.id());
beanFactory.registerSingleton(jmqListener.id(), Objects.requireNonNull(wrapperBean));
logger.info("Register jmq consumer: {}", jmqClient.getId());
Consumer consumer = consumerContainer.getObject();
Assert.notNull(consumer, "consumer can not be null");
for (String topic : jmqListener.topics()) {
consumer.subscribe(stringValueResolver.resolveStringValue(topic), messageListener);
}
} else {
Consumer consumer = applicationContext.getBean(jmqListener.id(), Consumer.class);
for (String topic : jmqListener.topics()) {
consumer.subscribe(stringValueResolver.resolveStringValue(topic), messageListener);
}
}
}
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = (GenericApplicationContext) applicationContext;
}
@Override
public void setEmbeddedValueResolver(@NonNull StringValueResolver stringValueResolver) {
this.stringValueResolver = stringValueResolver;
}
}
4.2 BeanPostProcessor接口有什么作用,为什么要定义类来实现它
BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口。,他的作用主要是如果我们需要在Spring 容器完成 Bean 的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,我们就可以定义一个或者多个 BeanPostProcessor 接口的实现,然后注册到容器中。
这样MqConsumer类在被spring加载并实例化的时候,会自动执行了JmqListenerRegister中的postProcessBeforeInitialization方法
4.3 Sring加载bean的时候会自动找到实现了BeanPostProcessor接口的实现类,并执行接口里面定义的方法
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
4.4 JmqListenerRegister的postProcessBeforeInitialization方法,实例初始化之前执行
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = AopUtils.getTargetClass(bean); // 获取当时实例化的bean对象的类
for (Method method : beanClass.getDeclaredMethods()) {
.// 判断当前方法是否带JmqListener的注解
JmqListener jmqListenerAnnotation = AnnotationUtils.findAnnotation(method, JmqListener.class);
if (jmqListenerAnnotation == null) {
continue;
}
// 创建listener
MessageListener messageListener = getMethodMessageListener(bean, method);
// 创建jmqClient
JmqClient<JmqConsumerPolicy> jmqClient = jmqConfigurationProperties.getConsumers().get(jmqListenerAnnotation.id());
if (jmqClient == null || !jmqClient.getEnabled()) {
continue;
}
if (jmqClient.getPolicy() == null) {
if (jmqConfigurationProperties.getGlobalConsumerPolicy() != null) {
jmqClient.setPolicy(jmqConfigurationProperties.getGlobalConsumerPolicy());
} else {
jmqClient.setPolicy(new JmqConsumerPolicy());
}
}
registerListener(jmqListenerAnnotation, jmqClient, messageListener);
}
return bean;
}
// 注册listener
protected void registerListener(JmqListener jmqListener, JmqClient<JmqConsumerPolicy> jmqClient, MessageListener messageListener) {
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
if (!applicationContext.containsBean(jmqListener.id())) {
ConsumerContainer consumerContainer = new ConsumerContainer(jmqClient);
Object wrapperBean = beanFactory.initializeBean(consumerContainer, "consumerContainer." + jmqListener.id());
beanFactory.registerSingleton(jmqListener.id(), Objects.requireNonNull(wrapperBean));
logger.info("Register jmq consumer: {}", jmqClient.getId());
Consumer consumer = consumerContainer.getObject();
Assert.notNull(consumer, "consumer can not be null");
for (String topic : jmqListener.topics()) {
consumer.subscribe(stringValueResolver.resolveStringValue(topic), messageListener);
}
} else {
Consumer consumer = applicationContext.getBean(jmqListener.id(), Consumer.class);
for (String topic : jmqListener.topics()) {
// consumer消费,绑定listener,listener里又绑定了method,method反射invoke去执行
consumer.subscribe(stringValueResolver.resolveStringValue(topic), messageListener);
}
}
}
protected MessageListener getMethodMessageListener(Object bean, Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
boolean isListener = (parameterTypes.length == 1 &&
genericParameterTypes[0].getTypeName().equals(String.format("java.util.List<%s>", Message.class.getName())));
if (!isListener) {
throw new IllegalArgumentException("listener parameters error, need MessageListener.onMessage");
}
return new MessageListenerReflectAdapter(bean, method);
}
MessageListenerReflectAdapter的定义,还用到了适配器模式
package com.jd.jmq.client.springboot.adaptor;
import com.jd.jmq.client.consumer.MessageListener;
import com.jd.jmq.common.message.Message;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public class MessageListenerReflectAdapter implements MessageListener {
private final Method method;
private final Object instance;
public MessageListenerReflectAdapter(Object instance, Method method) {
this.instance = instance;
this.method = method;
this.method.setAccessible(true);
}
@Override
public void onMessage(List<Message> messages) {
try {
method.invoke(instance, messages);
} catch (InvocationTargetException e) {
if (e.getMessage() == null) {
throw new RuntimeException(e.getCause());
} else {
throw new RuntimeException(e);
}
} catch (Exception e) {
throw new RuntimeException("consume error", e);
}
}
}
4.5 JmqListenerRegister的postProcessAfterInitialization方法,实例初始化之前执行
@Override
public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
return bean;
}
原创:做时间的朋友