java笔记--springMessage处理自定义注解

前言

PS:前言基本都是废话,可以直接看正文.
先说一下,实现这个功能的原因,因为怕公司会更换OSS服务器,所以现有项目存放的图片都是不包含域名的,这是前提.
这样,我们可以无论是通过EL表达式在前台定义一个变量来记录域名还是从后台配置读取,都可以相对比较容易的在以后更换域名.
考虑到一些原因,最终决定还是从后台返回数据的时候,就将OSS域名追加.
于是,就有了下面这个功能.先说一下实现该功能的思路.
最初打算是将所有的自定义的注解处理类封装到一套工具里.
通过模板模式定义整体处理的流程,策略模式实现不同的自定义注解处理方法,通过工厂模式加载策略模式,通过单例来定义一些必须的参数,比如需要忽略掉那些包结合,只处理哪些包集合,忽略哪些类,只处理哪些类.
这是一个最基本的思路,这样可以最大化的降低耦合度.我也按照这个思路写了一下,代码量很多,而且在第一版完成之后,发现有可能不同的处理自定义注解的类针对的类是不同的,这是设计之初没考虑到的,于是,修改了单例对象,改为让每一个策略类都持有一个属于自己本身的参数内部类.
但是这么一来,初始化参数的地方就无法统一封装了,无异于又增加了复写策略类的成本.于是,思来想去,又换了一种比较简单的方式来实现,功能单一且代码量比较少.

正文

我目前使用的项目是基于springboot的,但是还有很多人是基于spring的,这里分别给出这两种实现的方式.

基于springboot自定义message处理的方法.

首先我们要写一个类来继承抽象类# WebMvcConfigurerAdapter#,通过这个类的名称就可以比较简单的发现,该类用来配置web容器.
然后我们复写该类的# configureMessageConverters# 方法
public void configureMessageConverters(List<HttpMessageConverter> converters) {
}
这个类用来配置消息转换的方法,我没有去看这个源码,但是这个应该是一个职责链模式.通过这个类我们可以添加一个我们自己的消息处理类
,或者只使用我们自己的消息处理类.
这里,我只是需要添加一个自己的消息处理类,所以,通过.
converters.add(new OSSFileURLMessageConverter());
 
     
来添加一个消息处理的流程.这里的
 
     
 
     
OSSFileURLMessageConverter
 
     
对象是我们自定义的一个消息处理类,下文会提到.
 
     
 
     
 
     
这样,我们就可以将我们在
 
     
 
     
OSSFileURLMessageConverter
 
     
 
     
 
     
 
     
里实现我们自己的处理逻辑了.

基于springmvc自定义message处理的方法.

如果是基于配置文件的话,我们可以配置一个消息转换类,下面是我之前项目中写的一段配置

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <ref bean="mappingJacksonHttpMessageConverter"/>
        list>
    property>
bean>
<bean id="mappingJacksonHttpMessageConverter"
      class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
    <property name="supportedMediaTypes">
        <list>
            <value>text/plain;charset=UTF-8value>
            <value>application/json;charset=UTF-8value>
        list>
    property>
bean>
我们可以写一个类继承
 
      
MappingJacksonHttpMessageConverter类,
然后重写
 
      
 
       
 
        
 
         
 
          
 
           
writeInternal方法,在这个方法里面实现自己的业务逻辑.
ok,上面都是如何将消息交给spring,下面是具体的内容.
第一步,是先写一个类,这个类用来处理自定义注解,类里面主要有三个方法.
第一个方法用来处理自定义注解.
第二个方法用来筛选类, 让第一个方法只处理真正可能会有自定义注解类.
第三个方法,是针对第二个方法,进行再次筛选,确保没有遗漏.
下面是具体的代码.

核心逻辑

首先我们需要筛选一下需要处理的类,确保类似于第三方jar之类的类不会被处理,以免造成资源浪费.
public void strategy(Object object){
    if (object instanceof Collection){
        //删选集合
        for (Object obj:(Collection)object){
            //递归筛选集合内的实体
            strategy(obj);

        }
    }else if (object instanceof Map){
        //筛选Map
        Set kyes=((Map) object).keySet();
        for (Object key:kyes){
            strategy(((Map) object).get(key));
        }
    }else if (object instanceof ExecuteResult){
        Object obj=((ExecuteResult) object).getResult();
        if (obj==null){
            return;
        }
        strategy(obj);

    }else if (object instanceof PageInfo){
        //筛选分页
        Object obje=((PageInfo) object).getList();
        if (obje==null){
            return;
        }
        strategy(obje);
    }else if (object.getClass().getPackage().getName().indexOf(BASE_PACKAGE)!=-1){
        //这一步,筛选出需要特殊处理的类
        //到这一步,需要进行特殊处理的均已处理完毕,接下来,我们处理普通对象
        //这一步,可以保证获取到的对象,没有特殊处理的对象,但是不保证对象的字段没有需要特殊处理的字段.
         dispose(object);
    }
    else {
       //忽略掉其余类
        return;
    }

}
PS:dispose(Object object)方法会在下面给出.
上面代码注释中已经给出了逻辑.里面的ExecuteResult类是自己封装的消息返回实体,所以需要特殊,而PageInfo是mybatis插件PageHelp的分页类,所以也需要特殊处理.
上面还用到了一个常量,BASE_PACKAGE,这个常量的作用是限制处理类所处包的范围,毕竟,我们需要使用自定义注解的地方,往往都是自己的实体类对象,所以,通过这一层可以进一步的限制处理类的范围.
到这一步的话,我们已经初步筛选了实体类,但是这时候还会有遗漏,因为很可能在其他类中可能含有我们要处理的类的对象,所以我们还需要进一步筛选.
private void dispose(Object object){
        //标准流程,获取所有字段,然后依次遍历
        Field[] fields=object.getClass().getDeclaredFields();
        for (Field field:fields){
            //先放开该字段的权限
            field.setAccessible(Boolean.TRUE);
            //处理该字段
            doHandler(field,object);
            //再次将字段的数据交给strategy方法,递归可以保证,没有遗漏的字段
            //因为strategy给出了跳出条件,所以,不会处理非指定类,也不会造成死循环
            try {
                //先判断字段内容是否有值,如果没有值,则跳过
                Object fieldValue=field.get(object);
                if (fieldValue==null){
                    continue;
                }
                strategy(field.get(object));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
    }
PS,doHandler(field,Object)方法是处理业务逻辑的地方,下文将会给出示例.
这样,我们就通过递归的方法完成了筛选,而且确保了没有遗漏的字段.上面这些部分都是一些通用的地方,主要目的就是限制处理的类和确保没有遗漏,这一部分可以抽取出来,作为模板方法来使用.
最后面其实相对来说是最不重要也是最重要的地方,实现业务逻辑,我这里逻辑比较简单,就是看看字段上面有没有@ImageUrl注解,有的话,就添加上OSS域名.
private  void doHandler(Field field,Object obj){
    //获取标注了ImageURL注解的字段
    if (field.isAnnotationPresent(ImageURL.class)){
        String suffix=  field.getAnnotation(ImageURL.class).param();
        //修改字段访问权限
        field.setAccessible(Boolean.TRUE);
        try {
            //填充内容
            field.set(obj, PropertiesConstans.IMAGE_PREFIX +field.get(obj)+ suffix);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
通过这种方式,就比较简单的实现了在返回json的时候,进行一些自定义处理的功能了.
这个方法还是能扩展的,比如,统一处理时间戳之类的.

修订记录

2017年8月1日,修改了方法中一个错误,这个错误会导致出现部分字段不处理.
 
    

你可能感兴趣的:(java)