一直很羡慕kafka的@KafkaListener注解,在方法上加上这个注解就可以自动监听消息,非常方便且灵活。但有些工具缺没有封装此类注解,例如netty、redis队列等,于是就自己整了个。
思路非常简单,获取spring ioc容器所有bean,然后进行匹配自定义注解,最后利用反射机制代理方法即可。
废话少说,直接上码。
首先,增加spring bean工具类。
package per.zdb.td.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
/**
* 通过方法注解代理执行方法
* @param annotation
* @param args
* @return 返回成功执行的数量
*/
public static int invokeMethodByAnnotation(Class<? extends Annotation> annotation,
Object... args) {
int successCount = 0;
List<Object> beans = getBeansByMethodAnnotation(annotation);
for (Object bean : beans) {
List<Method> methods = getMethodsByAnnotation(bean, annotation);
for (Method method : methods) {
try {
method.invoke(bean,args);
successCount++;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
return successCount;
}
/**
* 通过注解获取bean
* @param annotation
* @return
*/
public static List<Object> getBeansByMethodAnnotation(Class<? extends Annotation> annotation) {
List<Object> list = new ArrayList();
String[] beanNames = getApplicationContext().getBeanDefinitionNames();
for (String beanName : beanNames) {
Object bean = getBean(beanName);
List<Method> methodsByAnnotation = getMethodsByAnnotation(bean, annotation);
if (methodsByAnnotation.isEmpty()){
//方法未匹配则跳过
continue;
}
list.add(bean);
}
return list;
}
/**
* 通过注解获取方法
* @param bean
* @param annotation
* @return
*/
private static List<Method> getMethodsByAnnotation(Object bean, Class<? extends Annotation> annotation) {
List<Method> methodList = new ArrayList<>();
Method[] methods = bean.getClass().getMethods();
if (methods==null){
return methodList;
}
for (Method item : methods) {
Annotation annotation1 = item.getAnnotation(annotation);
if (annotation1!=null) {
methodList.add(item);
}
}
return methodList;
}
}
添加自定义注解类
package per.zdb.td.netty.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 接受socket消息
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SocketListener {
}
在入口处代理回调含有自定义注解的方法
/**
* 接收消息
* @param ctx
* @param msg
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
String text = msg.text();
System.out.println("接收到消息:"+text);
//执行所有@SocketListener方法
SpringContextUtil.invokeMethodByAnnotation(SocketListener.class,ctx,msg);
}
在监听器类添加监听方法
package per.zdb.td.netty.listener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.springframework.stereotype.Component;
import per.zdb.td.netty.annotation.SocketListener;
@Component
public class WebSocketListener {
@SocketListener
public void listener(ChannelHandlerContext ctx, TextWebSocketFrame msg){
System.out.println("1号 @SocketListener接收到消息:"+msg.text());
}
@SocketListener
public void listener2(ChannelHandlerContext ctx, TextWebSocketFrame msg){
System.out.println("2号 @SocketListener接收到消息:"+msg.text());
}
}