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处理的方法.

如果是基于配置文件的话,我们可以配置一个消息转换类,下面是我之前项目中写的一段配置
<!-- 支持返回json(避免IEajax请求时,返回json出现下载 ) --> <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-8</value>
            <value>application/json;charset=UTF-8</value>
        </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日,修改了方法中一个错误,这个错误会导致出现部分字段不处理.

转载于:https://my.oschina.net/u/3101282/blog/1502879

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值