公司有MQTT平台服务端相关开发,涉及通过不同topic调用不同service任务,网上查询相关资料,发现没有提供MessageServiceProvider相关代码,今天写了个简易demo,通过注解反射获取service,代码如下。
新建抽象类BaseMessageService,泛型根据你的需求,方法process为要执行的任务方法
public abstract class BaseMessageService<T extends String> {
/*
* 执行任务
*
* */
public abstract Object process(int i,T msg);
}
创建自定义注解
@Retention(RetentionPolicy.RUNTIME) // 指定注解的保留策略
@Target({ElementType.TYPE, ElementType.METHOD}) // 指定注解的使用范围
public @interface MessageValue {
String topic();
String desc();
}
新建2个类继承BaseMessageService,并定义注解内容
@MessageValue(topic = "1子类",desc = "11111子类")
public class MessageService1 extends BaseMessageService<String> {
@Override
public Object process(int i, String msg) {
System.out.println("1子类接收到的内容:"+msg);
return null;
}
}
@MessageValue(topic = "2子类",desc = "222子类")
public class MessageService2 extends BaseMessageService<String> {
@Override
public Object process(int i, String msg) {
System.out.println("2子类接收到的内容:"+ msg);
return null;
}
}
编写MessageServiceProvider,获取返回的service
通过反射获取抽象类的所有子类,再获取其注解topic的值,与传入topic的值相等返回对应service。
public class MessageServiceProvider {
public static BaseMessageService getMessageService(String topic) throws IllegalAccessException, InstantiationException {
//BaseMessageService所在的包路径
Set<Class<?>> clazzs2 = listAllSubclasses("com.XXXX.XXXX", BaseMessageService .class);
for(Class c : clazzs2){
MessageValue mat = (MessageValue)c.getAnnotation(MessageValue.class);
if(topic.equals(mat.topic())){
return (BaseMessageService)c.newInstance();
}
}
return null;
}
/**
* 默认过滤器(无实现)
*/
private final static Predicate<Class<?>> EMPTY_FILTER = clazz -> true;
/**
* 扫描目录下的所有class文件
*
* @param scanPackage 搜索的包根路径
* @return
*/
public static Set<Class<?>> getClasses(String scanPackage) {
return getClasses(scanPackage, EMPTY_FILTER);
}
/**
* 返回所有的子类(不包括抽象类)
*
* @param scanPackage 搜索的包根路径
* @param parent
* @return
*/
public static Set<Class<?>> listAllSubclasses(String scanPackage, Class<?> parent) {
return getClasses(scanPackage, (clazz) -> {
return parent.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers());
});
}
/**
* 返回所有带制定注解的class列表
*
* @param scanPackage 搜索的包根路径
* @param annotation
* @return
*/
public static <A extends Annotation> Set<Class<?>> listClassesWithAnnotation(String scanPackage,
Class<A> annotation) {
return getClasses(scanPackage, (clazz) -> {
return clazz.getAnnotation(annotation) != null;
});
}
/**
* 扫描目录下的所有class文件
*
* @param pack 包路径
* @param filter 自定义类过滤器
* @return
*/
public static Set<Class<?>> getClasses(String pack, Predicate<Class<?>> filter) {
ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metaFactory = new SimpleMetadataReaderFactory(patternResolver);
String path = ClassUtils.convertClassNameToResourcePath(pack);
String location = ResourceUtils.CLASSPATH_URL_PREFIX + path + "/**/*.class";
Resource[] resources;
Set<Class<?>> result = new HashSet<>();
try {
resources = patternResolver.getResources(location);
for (Resource resource : resources) {
MetadataReader metaReader = metaFactory.getMetadataReader(resource);
if (resource.isReadable()) {
String clazzName = metaReader.getClassMetadata().getClassName();
if (clazzName.contains("$")) {
// 忽略内部类
continue;
}
// Class<?> clazz = Class.forName(clazzName);
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(clazzName);
if (filter.test(clazz)) {
result.add(clazz);
}
}
}
} catch (Exception e) {
}
return result;
}
}
通过反射获取所有子类我是参考这篇文章,需要spring
https://blog.csdn.net/littleschemer/article/details/47378455
写一个测试类
public class TestDemo {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
String topic ="1子类";
BaseMessageService messageService = MessageServiceProvider.getMessageService(topic);
messageService.process(1,"test");
}
}
这里可以优化一下,在项目启动的时候就把service注解中的value和class放进map里,这样获取的时候直接通过topic去取实例就行了。