Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句)
Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句)
1.冗余的if/else或switch
有没有朋友写过以下的代码结构,大量的if/esle判断,来选择不同的执行方式
if(type==1001){
return decodeMsg1001(msg);
}else if(type==1002){
return decodeMsg1002(msg);
}
.....
或者上面的代码也是可以转换成相应的switch语句来执行,结构如下所示:
switch(type){
case 1001:{
return decodeMsg1001(msg)
}
case 1002:{
return decodeMsg1002(msg);
}
....
}
总之,如果type的值很多的话,就会导致这段代码特别的长。如果想在处理消息的函数中添加一些其他的信息,则会导致处理消息的函数体也会比较长。
Java语言是面向对象的,有没有一种方法用面向对象的方式来解决这个问题。将大量的if/else语句转换成使用接口的方式来解决。答案是有的
2.思路想法
上述的问题大多数产生在,多个不同类型的消息对应着不同的处理方法(不同消息所对应的数据不一致)。
可以将不同的类型与类型所对应的处理方法,做成一个映射表。即一种type对应于一个typeProcessor,在java中这种数据结构是Map。所以在系统初始化时,先生成这样一种Map的对应关系。当一种类型type的消息到来时,从这个map中查找出所对应的处理类,然后动态产生一个接口对象,然后执行接口中的方法。那这样就会有相应的问题:
- 问题一:每一种类型对应一个处理类,就会有好多实体类,怎么做好一个类型对应一个处理类
- 问题二:如果一个一个类实体对象的手动添加,也会产生好多冗余的代码。
3.消息对应实现
3.1 利用annotation实现类型与处理类对应
在java中可以利用Annotation这一特性,来实现消息类型与实体类的对应,先创建一个annotation。
@Target(ElementType.TYPE) //TYPE(类型)是指可以用在Class,Interface,Enum和Annotation类型上.
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ProtocolCommand {
int msgCommand() default 0 ;
}
创建好相应的Annotation后,可以添加在每一个消息处理类上,在Annotation中有一个属性为msgCommand,是int类型的,这样就可以做到对应。
3.2消息处理接口
为方便操作,可以创建一个消息处理接口,对应每一种类型的消息处理类都需要实现这一接口,这样的好处在动态产生对象很好的做到统一处理。
public interface IUbloxMsg {
public int decodeUbxMsg(RawData rawData);
}
这里的接口很简单,如果想处理复杂的业务,可以自行在接口中添加相应的方法。
这里也可以不用接口这一特性来实现功能,也可以用抽象类 来实现。每一种特性都可以实现功能。
3.3具体消息实现
@ProtocolCommand(msgCommand=UbloxConstant.ID_NAVSOL)
public class NavSolUbloxMsgImpl implements IUbloxMsg {
public int decodeUbxMsg(RawData rawData) {
return 0;
}
}
可以看到在具体的消息类型处理类上,加上我们自定义的注解,即可实现具体消息类型与具体消息处理类对应。
4.生成Map消息类型映射
4.1消息处理类扫描,得到Class集合
通常情况下,为了方便管理消息处理类应该放在一个包目录下。所以我们需要自动扫描出这个包目录下的所有的class,产生Class集合。然后在这个集合中查找到有我们自定义注解的类,同时得到注解对象,获取消息类型,并将此消息类型添加至Map消息映射中。
类扫描代码核心部分代码如下:
public static ClassFilter cFilter = new ClassFilter(true);
//传入的参数为包名
public static Set<Class<?>> scannClassesByPackage(String pack) throws ClassNotFoundException, IOException {
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
boolean recursive = true;
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
Enumeration<URL> dirs;
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
while (dirs.hasMoreElements()) {
URL url = dirs.nextElement();
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
JarFile jar;
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
jar = jarURLConnection.getJarFile();
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.charAt(0) == '/') {
name = name.substring(1);
}
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
if (idx != -1) {
packageName = name.substring(0, idx).replace('/', '.');
}
if ((idx != -1) || recursive) {
if (name.endsWith(".class") && !entry.isDirectory()) {
String className = name.substring(packageName.length() + 1, name.length() - 6);
classes.add(Class.forName(packageName + '.' + className));
}
}
}
}
}
}
return classes;
}
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath,
final boolean recursive, Set<Class<?>> classes) throws ClassNotFoundException {
File dir = new File(packagePath);
if (!dir.exists() || !dir.isDirectory()) {
return;
}
File[] dirfiles = dir.listFiles(cFilter);
for (File file : dirfiles) {
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
String className = file.getName().substring(0, file.getName().length() - 6);
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
}
}
}
4.2生成Map映射
上述已经通过扫描得到所有的消息处理类的集合,下面通过集合遍历查找,得到有自定义注解的类。并且得到注解对象获取消息类型,生成Map映射对象。相应的核心代码如下所示:
private Map<Integer, Class<?>> msgImplClass = new HashMap<Integer, Class<?>>();
public void initMsgClazz(String pkg ) {
Set<Class<?>> classes = null;
try {
classes = ClassScannerUtils.scannClassesByPackage(pkg);
} catch (ClassNotFoundException e) {
throw new ProtocolMsgException("invalid package [ " +pkg+" ]",e);
} catch (IOException e) {
throw new ProtocolMsgException("IO error for read class ",e);
}
if(classes == null ){
throw new ProtocolMsgException("scanner class is null ");
}
for (Class<?> c : classes) {
if (c.isAnnotationPresent( ProtocolCommand.class)) {
ProtocolCommand protocolCommand=c.getAnnotation(ProtocolCommand.class);
msgImplClass.put(protocolCommand.msgCommand(),c);
}
}
}
public Class<?> getClassByMsgCommand(Integer msgCommand){
if(msgImplClass == null || msgImplClass.size() <=0){
return null;
}
return msgImplClass.get(msgCommand);
}
至此整个思路就完成拉
- 用法一:TCP/IP通信应用中,可以参考此思路进行数据消息处理。(应该是最多的一种)
- 用法二:其他地方,有多种消息类型需要处理
代码获取下载,可点击下方链接进行代码参考与下载:
Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句)的更多相关文章
- 【转】Java 枚举7常见种用法
原文网址:http://softbeta.iteye.com/blog/1185573 Java 枚举7常见种用法 博客分类: java java枚举enmu 原创地址:http://blog.li ...
- 使用Java8中的Optional类来消除代码中的null检查
简介 Optional类是Java 8新增的一个类,Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException). —— 每个 Java 程序员都非常了解的异常 ...
- 子查询。ANY三种用法。ALL两种用法。HAVING中使用子查询。SELECT中使用子查询。
子查询存在的意义是解决多表查询带来的性能问题. 子查询返回单行多列: ANY三种用法: ALL两种用法: HAVING中的子查询返回单行单列: SELECT中使用子查询:(了解就好,避免使用这种方法! ...
- Java 枚举7常见种用法
DK1.5引入了新的类型--枚举.在 Java 中它虽然算个"小"功能,却给我的开发带来了"大"方便. 用法一:常量 在JDK1.5 之前,我们定义常量都是: ...
- Java枚举常见7种用法
DK1.5引入了新的类型——枚举.在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便.用法一:常量在JDK1.5 之前,我们定义常量都是: publicstaticfianl…… .现 ...
- Java枚举7常见种用法
DK1.5引入了新的类型——枚举.在Java中它虽然算个“小”功能,却给我的开发带来了“大”方便. 方法/步骤 用法一:常量 在JDK1.5之前,我们定义常量都是:publicstaticfianl. ...
- Java 枚举7常见种用法(转)
JDK1.5引入了新的类型——枚举.在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便. 用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl ...
- Java 枚举常见7种用法
用法一:常量 在JDK1.5 之前,我们定义常量都是: publicstaticfianl.....现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法. publ ...
- (转) java定时器的几种用法
package com.lid; import java.util.Calendar; import java.util.Date; import java.util.Timer; import ja ...
随机推荐
- spring boot:用rocketmq发送延时消息用来取消订单(spring boot 2.3.3)
一,为什么要用延时消息来取消订单? 1,为什么要取消订单 在电商的下单过程中,需要在生成订单时扣减库存, 但有可能发生这种情况:用户下了单,临时改变主意不再支付, 则订单不能无限期的保留,因为还要把占 ...
- go创建http服务
Go语言这种从零开始使用到解决问题的速度,在其他语言中是完全不可想象的.学过 C++ 的朋友都知道,一到两年大强度的理论学习和实战操练也只能学到这门语言的皮毛,以及知道一些基本的避免错误的方法. 那么 ...
- svn:E170001:Authorization failed解决
eclipse添加svn资源库:打开eclipse→Window→SVN资源库→空白处右键新建资源库位置→填写正确的URL→finish 错误信息: 原因一:用户名或密码错误 因svn登录验证的账号信 ...
- Python之集合详解
定义: 1.不同元素组成 2.无序 3.集合中的元素必须是不可变类型 创建集合 s = {1,2,3,4,5,6,7,8} 1.定义可变集合 >>> set_test = set(' ...
- Ubuntu20.4安装
官网下载镜像 https://releases.ubuntu.com/20.04/ubuntu-20.04-live-server-amd64.iso 挂载开装 选语言 选键盘 网络设置DHCP到地址 ...
- influxdb集群部署
环境准备 influxdb enterprise运行条件最低需要三个meta nodes节点以及两个data nodes Meta nodes之间使用TCP和Raft一致性协议通信,默认端口为8089 ...
- JavaWeb中的域对象
域对象:可以在不同Servlet之间传递数据的对象,就是域对象,它们内置了map集合,都有setAttribute和getAttribute方法: Java Web有ServletContext.Se ...
- conda回滚
1. 查看历史版本: conda list --revision 2. 安装上次版本: conda install revision 13 13是历史序号.从上面看出,最近的历史序号是14,因此上一个 ...
- C2. Pokémon Army (hard version) 解析(思維)
Codeforce 1420 C2. Pokémon Army (hard version) 解析(思維) 今天我們來看看CF1420C2 題目連結 題目 略,請直接看原題. 前言 根本想不到這個等價 ...
- C语言之 Switch和?:运算符的反汇编
Switch条件语句 通过上面一篇了解了条件语句的使用,接下来就直接进行反汇编学习 #include <stdio.h> void print() { int b = 1; switch ...