介绍一个新写的小东西,叫FastConverter,叫这个名字是由于,它最初是被设计用来将服务器返回给前端的数据实体转换为json字符串的。前端
需求背景是:服务器通过一系列计算后,最终数据被存放在一个数据实体中,通过toJSON化,输出到前端。但输出时咱们对数据有一些格式化,或自定制化的需求,好比,数据实体中的Date,最终输出多是时间戳,也多是“yyyy-MM-dd”;数据实体中的用以表示金额的BigDecimal,在服务器端咱们用元作单位,带小数点,输出时咱们想让它变成以分为单位,不带小数点;用户敏感信息打码(用户名,身份证号,手机号等)等等。总之就是,直接将数据实体toJSON,不能知足咱们的需求,在toJSON的同时,咱们须要对数据作一些转换。git
不少初学者设计一个工具的时候,思路很散,作出来的东西不成体系。好比上面所述的功能,有的人确定是一个 "XxxTool"类 或者 "XxxUtils"类 就实现了。程序员
但这种散件很是丑陋,类的抽象和类的接口息息相关, "XxxTool"类 或者 "XxxUtils"类最大的问题是,它没法有效且内聚的描述本身的抽象,它的接口大多数状况下各司其职,这样的类必定违背开闭原则,依赖倒转原则,也违背内聚性。实在点说就是:1,当客户端程序员去使用这些工具类时,他们会发现,这个类有好多方法;2,每一个方法彷佛都是一个独立的功能点;3,当他发现缺乏他须要的功能时,他会不知所措,到底如何修改这个庞大的类;4,时间久了,这个类必定是没法维护的(这个复杂的私有方法是干什么的???为何只为那个公共方法提供服务???这个私有域又是干什么的???我能够修改它吗???还有哪些地方使用了它???算了,我本身加个新的域来实现个人功能吧);5,会有不少类依赖(紧耦合)这个工具类。github
若是你在开发系统底层的时候不在意这些小问题,等系统变得庞杂起来时,它会让你步履维艰,这是很明显的弊端,但我很惊讶如此多的人对它视而不见(也许是由于教科书上不教这些小细节吧)。spring
既然是转换数据,接口的设计挺天然的:json
public interface Converter<T, K> { K convert(T value, String tip) throws ConvertException; K convert(T value) throws ConvertException; boolean supports(T value); }
supports这个方法的设计学习了spring框架,后续开发也证实,它很是有用。值得一提的是,supports方法接收的是一个对象,而不是Class<T>。我在开发的时候曾经将它修改成Class<T>过,但后来发现,这样作局限性很大,并且被转换对象必定是事先存在了的,此处不须要使用Class<T>来作先于数据的判断;再者,若是是接收Class<T>,任何泛型T类型一致的转换器将没法共存(后续讲解)。服务器
convert方法中有一个String类型的tip参数,它是用来赋予转换器必定灵活性而引入的。好比要将Date转换为 “yyyy-MM-dd” 和 “yyyy.MM.dd” 你只须要一个转换器就能实现。框架
抛出的异常:ide
public class ConvertException extends Exception { public ConvertException(String message) { super(message); } }
有了转换器,咱们还须要一个转换器过滤器,由于在个人思路里,咱们能够将多个转换器注册到一个容器中,让容器自动根据supports过滤出适合某种数据的转换器(后续你将看到,它很是有用)。工具
public interface ConverterFilter { <T> Converter getConverter(T value); }
因为泛型擦除机制的存在,该接口就算限定返回值是Converter<T, K> ,你也没法获取到正确的能将T -> K 的Converter<T, K>转换器,你获取到的仅仅是Converter。因此此处定义的返回值是Converter,而不是Converter<T, K>。
我在需求中提到了,要将数据实体中的数据作一些格式化或自定制化的转换。实现这一步我采用的是默认转换加自定义转换并存的策略。自定义转换如何工做?使用注解!经过在域(类中声明的字段)上打上注解来告知系统,此域如何进行转换。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public [@interface](https://my.oschina.net/u/996807) FieldConverter { String tip() default ""; Class<? extends Converter> converter(); }
这个注解很简单,就不赘述了。
public class BeanToMapConverterHandler extends AbstractFilterBaseConverterHandler<Object, Map<String,Object>> { public BeanToMapConverterHandler(ConverterFilter converterFilter) { super(converterFilter); } [@Override](https://my.oschina.net/u/1162528) protected Map<String, Object> converting(Object value, String tip) throws ConvertException { Map<String, Object> map = new HashMap<>(); for (Field field : value.getClass().getDeclaredFields()) { // 获取域值 Object fieldValue; try { PropertyDescriptor pd = new PropertyDescriptor(field.getName(), value.getClass()); Method reader = pd.getReadMethod(); if (reader != null) { fieldValue = reader.invoke(value); } else { continue; } } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); throw new ConvertException("BeanToMapConverterHandler对数据转换过程当中发生异常:" + e.getMessage()); } catch (IntrospectionException e) { continue; } // 转换域值 Object mapValue = fieldValue; FieldConverter annotation = field.getAnnotation(FieldConverter.class); if (annotation == null) { Converter converter = this.getConverter(fieldValue); if (converter != null) { mapValue = converter.convert(fieldValue, tip); } } else { try { mapValue = annotation.converter().newInstance().convert(fieldValue, annotation.tip()); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); throw new ConvertException(e.getMessage()); } } map.put(field.getName(), mapValue); } return map; } [@Override](https://my.oschina.net/u/1162528) public boolean supports(Object value) { return value != null; } }
这个类继承自AbstractFilterBaseConverterHandler,下面贴出它的代码:
public abstract class AbstractFilterBaseConverterHandler<T, K> extends AbstractConverterHandler<T, K> { private ConverterFilter converterFilter; public AbstractFilterBaseConverterHandler(ConverterFilter converterFilter) { this.converterFilter = converterFilter; } protected Converter getConverter(Object value) { return converterFilter.getConverter(value); } protected ConverterFilter getConverterFilter() { return converterFilter; } }
AbstractFilterBaseConverterHandler继承自AbstractConverterHandler,下面贴出AbstractConverterHandler的代码:
public abstract class AbstractConverterHandler<T, K> implements Converter<T, K> { private String tip; public AbstractConverterHandler() { this.tip = ""; } public AbstractConverterHandler(String tip) { this.tip = tip; } protected abstract K converting(T value, String tip) throws ConvertException; @Override public K convert(T value) throws ConvertException { return this.convert(value, this.tip); } @Override public K convert(T value, String tip) throws ConvertException { if (!this.supports(value)) { throw new ConvertException(this.getClass().getName() + " 没法转换数据 " + value); } return this.converting(value, tip); } }
抽象类AbstractConverterHandler实现了Converter接口的两个convert方法,所以,实现你本身的Converter只须要继承这个抽象类并实现converting和supports两个方法就能够。
这个抽象类主要是为了填充默认的tip,以及实现两个convert方法的调用逻辑。能够看出来,最终客户端程序员使用一个Converter的时候,对convert的调用最终都会落在
public K convert(T value, String tip) throws ConvertException {}
这个方法上,而它会自动调用一次supports,并抛出异常。这给Converter的编写带来了不少便捷性和一致性。个人全部Converter都是经过继承AbstractConverterHandler实现的。
AbstractFilterBaseConverterHandler这个抽象类是用来定义一个依赖ConverterFilter的Converter的。后面我会介绍到,有一类Converter是须要依赖ConverterFilter的,例如BeanToMapConverterHandler。
BeanToMapConverterHandler这个Converter是用来将bean转换为Map的,由于Map到JSON对象的转换结果,等同于Object到JSON对象的转换结果,因此我先将数据实体转换为Map。从BeanToMapConverterHandler中能够看出来,每次获取一个域值,我会判断,它是否使用FieldConverter注解标记了转换器,若是有,用标记的转换器转换域值,不然我会将它扔到ConverterFilter中去过滤出一个转换器来,用过滤出来的转换器转换域值。
你可能已经看出来了,ConverterFilter中注册的,就是默认转换器。
因为向ConverterFilter注册多少转换器,什么转换器是由你决定的,因此BeanToMapConverterHandler这个转换器的具体行为,就是可变的,由ConverterFilter决定的。
转换器过滤器:
public class ResponseConverterFilter extends AbstractConverterFilter { @Override protected void initConverters(List<Converter<?, ?>> converters) { converters.add(new StringConverterHandler()); converters.add(new NumberToStringConverterHandler()); converters.add(new BooleanToNumberStringConverterHandler()); converters.add(new DateToTimeStampStringConverterHandler()); converters.add(new EnumValueConverterHandler()); converters.add(new NullToEmptyStringConverterHandler()); converters.add(new ArrayToListConverterHandler(this)); converters.add(new CollectionToListConverterHandler(this)); converters.add(new MapToMapConverterHandler(this)); converters.add(new BeanToMapConverterHandler(this)); } }
抽象转换器过滤器:
public abstract class AbstractConverterFilter implements ConverterFilter { private List<Converter<?, ?>> converters; public AbstractConverterFilter() { converters = new ArrayList<>(); this.initConverters(converters); } protected abstract void initConverters(List<Converter<?, ?>> converters); @Override public <T> Converter getConverter(T value) { for (Converter converter : converters) { try { if (converter.supports(value)) { return converter; } } catch (ClassCastException ignored) { } } return null; } }
老套路,先是一个抽象类(AbstractConverterFilter)完成基本操做,而后是一个具体的实现类(ResponseConverterFilter)。
在ResponseConverterFilter中,你能够看到我所注册的默认转换器。前六个是Converter,后四个是基于ConverterFilter的Converter。在此我不一一介绍每一个Converter是干什么的,只着重介绍一下CollectionToListConverterHandler这个基于ConverterFilter的Converter。
首先问一个问题:若是数据实体中的某个字段是容器(List,Set,Map...)应该怎么办?
必定是须要对容器中的数据作转换处理的,不然输出到前端的数据就不符合需求。那么怎么对容器中的数据作转换呢?对容器中的数据的转换也要按照统一的规则走(使用默认转换器转换,若是是Bean,被FieldConverter注解的域要走指定的转换器)。
这一步就由三个特殊的转换器实现:ArrayToListConverterHandler,CollectionToListConverterHandler,MapToMapConverterHandler。这里我只介绍CollectionToListConverterHandler,其余两个功能和它相似。先看代码:
public class CollectionToListConverterHandler<T, K> extends AbstractFilterBaseConverterHandler<Collection<T>, List<K>> { public CollectionToListConverterHandler(ConverterFilter converterFilter) { super(converterFilter); } @Override protected List<K> converting(Collection<T> value, String tip) throws ConvertException { ArrayList<K> list = new ArrayList<>(); for (T obj : value) { Converter converter = this.getConverter(obj); if (converter == null) { throw new ConvertException("没有转换器能够处理" + obj); } else{ list.add((K) converter.convert(obj, tip)); } } return list; } @Override public boolean supports(Collection<T> value) { return value != null; } }
这个基于ConverterFilter的Converter从容器中取出元素,将每一个元素放到ConverterFilter中去筛选出合适的Converter,而后用它转换数据。
注意观察ResponseConverterFilter中的:
converters.add(new CollectionToListConverterHandler(this));
这个this很关键,它使得整个系统能够处理容器嵌套,且行为是一致的。
ObjectToJsonStringConverterHandler:
public class ObjectToJsonStringConverterHandler extends AbstractFilterBaseConverterHandler<Object, String> { public ObjectToJsonStringConverterHandler(ConverterFilter converterFilter) { super(converterFilter); } @Override protected String converting(Object value, String tip) { try { Converter converter = new CommonFilterBaseConverterHandler(this.getConverterFilter()); return JSON.toJSONString(converter.convert(value)); } catch (ConvertException e) { e.printStackTrace(); return ""; } } @Override public boolean supports(Object value) { return true; } }
CommonFilterBaseConverterHandler:
它只是简单的从ConverterFilter中取出合适的转换器转换数据。因为咱们定义ResponseConverterFilter时注册了BeanToMapConverterHandler,使得它能够将Bean转换为Map,因此这个通用转换器最终将获得一个Map。配合前面的ObjectToJsonStringConverterHandler转换器,就能够获得最终的JSON字符串。
public class CommonFilterBaseConverterHandler<T, K> extends AbstractFilterBaseConverterHandler<T, K> { public CommonFilterBaseConverterHandler(ConverterFilter converterFilter) { super(converterFilter); } @Override public boolean supports(T value) { if (this.getConverter(value) == null) { return false; } return true; } @Override protected K converting(T value, String tip) throws ConvertException { Converter converter = this.getConverter(value); if (converter != null) { return (K) converter.convert(value); } else { throw new ConvertException("没有转换器能够处理" + value); } } }
public static void main(String[] a) { class TheEntity { @FieldConverter(converter = BigDecimalToAmountConverterHandler.class) private BigDecimal interest = new BigDecimal(100); @FieldConverter(converter = DateToFormatStringConverterHandler.class, tip = "yyyy-MM-dd") private Date now = new Date(); private Integer id = 1; private Boolean bool = true; private Date now2 = new Date(); ... 省略getter,setter方法 } try { System.out.println(new ObjectToJsonStringConverterHandler(new ResponseConverterFilter()).convert(new TheEntity())); } catch (ConvertException e) { e.printStackTrace(); } } // 输出结果 :{"bool":"1","interest":"10000","now":"2018-11-13","id":"1","now2":"1542102030250"}
这里只简单的展现了它的工做结果,实际系统中它的功能会比这个强大的多。每个Converter都处理一种特定的数据转换,职责专注,多个Converter能够经过ConverterFilter组合在一块儿,完成一些复杂的数据转换。且你只需经过在ConverterFilter中注册Converter就能够,是一种可插拔机制。
这个框架能够有不少变体,看你怎么玩,好比下面这样:
/** * 无限转换转换器 * * 该转换器将使用ConverterFilter中注册的转换器进行无限次数的转换,直到没有适配的 * 转换器可用为止。因为此转换器会进行无限次数的转换,因此你要确保你的ConverterFilter * 链路中,必定会转换出一种没有任何转换器能够对它继续进行转换的数据类型,且要保证 * 不出现某个转换器的结果是另外一个转换器supports的状况。 * * @param <T> * @param <K> */ public class CommonInfiniteFilterBaseConverterHandler<T, K> extends AbstractFilterBaseConverterHandler<T, K> { public CommonInfiniteFilterBaseConverterHandler(ConverterFilter converterFilter) { super(converterFilter); } @Override protected K converting(T value, String tip) throws ConvertException { Object obj = value; Converter converter = null; while ((converter = this.getConverter(obj)) != null) { obj = converter.convert(obj); } return (K) obj; } @Override public boolean supports(T value) { return value != null; } }
配合这几个转换器:
public class RequestConverterFilter extends AbstractConverterFilter { @Override protected void initConverters(List<Converter<?, ?>> converters) { converters.add(new HttpInputMessageToFormStringConverterHandler()); converters.add(new FormStringToJsonStringConverterHandler()); converters.add(new HttpServletRequestToJsonStringConverterHandler()); converters.add(new JsonStringToRequestCheckEntityConverterHandler()); converters.add(new RequestCheckEntityToRequestEntityConverterHandler()); } }
不知道光看这几行代码你能不能发现这组转换器能实现什么(它能将多种数据最终转换为RequestEntity实体)。它优雅的地方在于,每种转换都是独立的,转换器写好后,你能够把它用在任何地方,不用局限于和这个框架配合。