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通信应用中,可以参考此思路进行数据消息处理。(应该是最多的一种)
  • 用法二:其他地方,有多种消息类型需要处理

代码获取下载,可点击下方链接进行代码参考与下载:

下载代码

11-21 20:23