easy-mapper 源码分析
来源:互联网 发布:windows时间服务未运行 编辑:程序博客网 时间:2024/06/05 17:56
问题:
在编写程序的过程中,我们会将bean按照业务的不同分作po,ao,pojo,dto等等。一个业务联下来经常会遇到不同bean之间的转换工作,像下面这样:
Student t = new Student();
t.setName(studentDto.getName());
t.setId(studentDto.getId());
t.setEmail(studentDto.getEmail());
……
这样的代码在bean里的属性异常丰富的时候会变的非常麻烦,我们不得不去写一些工厂类来完成这些功能。这样我们可能会写出N多个assemb方法出来。到这里我们就会感觉很不爽了,于是我们又写了一个利用反射自动设置的通用方法。但是之后又会发现其他的一些特例的问题(比如有的属性我不想要之类的......)修改原来方法的成本比较高,导致我们又回到上面的麻烦的代码中。easy-mapper就是帮我们做这个事情的,这个是百度的一个叫张旭的人物写的,下面贴出github地址
github地址:> https://github.com/neoremind/easy-mapper
使用异常简单maven项目直接引用
<dependency> <groupId>com.baidu.unbiz</groupId> <artifactId>easy-mapper</artifactId> <version>1.0.2</version></dependency>
Person p = new Person();p.setFirstName("NEO");p.setLastName("jason");p.setJobTitles(Lists.newArrayList("abc", "dfegg", "iii"));p.setSalary(1000L);PersonDto dto = MapperFactory.getCopyByRefMapper() .mapClass(Person.class, PersonDto.class) .registerAndMap(p, PersonDto.class);System.out.println(dto);
上面是用法,下面我开始分析源码的一些重要逻辑(欢迎指正)
在codegen包下存放的是相关字节码增强功能实现的辅助类,框架实现的类转换功能依赖的是字节码增强生成的代码,这种技术效率很高,而且作者在生成一次之后又将生成的类加入到缓存中,下次再调用就直接从缓存拿了,更加提升了转换效率。在github上作者给出了和同类插件的性能对比图,虽然算不上顶尖,但是好在使用方便,学习成本很低,已经很优秀了
有关字节码增强技术的文章请看这里
http://blog.chinaunix.net/uid-21718047-id-3337821.html
上面的create Object number是转换对象的个数
exception包下是默认的异常处理
mapping包下是针对两个类之中的匹配属性不同类型之间的转换策略
metadata包下是转换过程中使用的数据源bean,之中包含了一些本类使用的工具方法
util包下是一些公共的公用类
根目录下的几个类是用来暴露给开发人员的入口类
下面我们跟踪一条主要的业务线来梳理逻辑,从使用的类入手
PersonDto dto = MapperFactory.getCopyByRefMapper() .mapClass(Person.class, PersonDto.class) .registerAndMap(p, PersonDto.class);
MapperFactory
MapperFactory.getCopyByRefMapper方法会返回一个单例的Mapper类,Mapper是一个接口,实现类只有 CopyByRefMapper 这一个,下面看看这个类里搞了什么
Mapper
Mapper是一个接口,里面提供了源类转换成目标类map方法的不同参数的实现,以及注册控件(或者初始化),清理缓存的方法
CopyByRefMapper
这个类的构造方法里初始化了三个属性
/** * 构造方法 */ public CopyByRefMapper() { this.classMapCache = new Memoizer<MapperKey, ClassMap<Object, Object>>(); this.mapperCache = new Memoizer<MapperKey, AtoBMapping<Object, Object>>(); codeGenerator = new MappingCodeGenerator(); }
classMapCache 和 mapperCache 都是用来做代码缓存的
codeGenerator是代码生成器 这两个我们先不关注,先关注主干
MapperFactory.getCopyByRefMapper()方法发挥的Mapper类后又调用了生成的这个Mapper(其实就是CopyByRefMapper)类的mapClass方法,看看这个方法里面搞了什么事情
@Override public <A, B> ClassMapBuilder<A, B> mapClass(Class<A> aType, Class<B> bType) { return new ClassMapBuilder<A, B>(TypeFactory.valueOf(aType), TypeFactory.valueOf(bType), this); }
这个方法里返回了一个ClassMap的构造器ClassMapBuilder,看看这个类里面是什么东西
ClassMapBuilder
这个类的构造方法长这个样子
/** * 构造方法 * * @param aType 源类型 * @param bType 目标类型 * @param mapper 客户端调用的入口mapper */ public ClassMapBuilder(Type<A> aType, Type<B> bType, Mapper mapper) { this.aType = aType; this.bType = bType; this.mapper = mapper; propertyResolver = new IntrospectorPropertyResolver(); }
这三个入参我们来看看,他是这样构造的
return new ClassMapBuilder<A, B>(TypeFactory.valueOf(aType), TypeFactory.valueOf(bType), this);
TypeFactory
TypeFactory.valueOf(aType) 做了什么:
public static <E> Type<E> valueOf(final Class<E> rawType) { if (rawType == null) { return null; } else if (rawType.isAnonymousClass() && rawType.getGenericSuperclass() instanceof ParameterizedType) { ParameterizedType genericSuper = (ParameterizedType) rawType.getGenericSuperclass(); return valueOf(genericSuper); } else { return intern(rawType, new java.lang.reflect.Type[0], new HashSet<java.lang.reflect.Type>()); } }
入参是java.lang包下的class类,出参是本项目metadata包下的Type类,这个是将java原生的类转换成自定义类的一个工具方法。这个方法声明为static 不用考虑并发带来的问题。整个项目的思路就是先将java原生class或者Property属性装换为自定义的类型后,然后再进行匹配转换操作
我们来看看他是怎么转换的
前面第一个if我们不管,第二个是判定内部类的不管,先看最简单的一条逻辑最后的else里
return intern(rawType, new java.lang.reflect.Type[0], new HashSet<java.lang.reflect.Type>());
看看这个方法里干了什么
private static <T> Type<T> intern(final Class<T> rawType, final java.lang.reflect.Type[] typeArguments, final Set<java.lang.reflect.Type> recursiveBounds) { //(1) Type<?>[] convertedArguments = TypeUtil.convertTypeArguments(rawType, typeArguments, recursiveBounds); TypeKey key = TypeKey.valueOf(rawType, convertedArguments); WeakReference<Type<?>> mapped = typeCache.get(key); Type<T> typeResult = null; if (mapped != null) { typeResult = (Type<T>) mapped.get(); } if (typeResult == null) { synchronized(rawType) { mapped = typeCache.get(key); if (mapped != null) { typeResult = (Type<T>) mapped.get(); } if (typeResult == null) { typeResult = createType(key, rawType, convertedArguments); mapped = new WeakReference<Type<?>>(typeResult); WeakReference<Type<?>> existing = typeCache.putIfAbsent(key, mapped); if (existing != null) { if (existing.get() == null) { typeCache.put(key, mapped); } else { mapped = existing; typeResult = (Type<T>) mapped.get(); } } } } } return typeResult; }
第一步就调用了一个转换方法,将入参为java原生class中的属性转换成了自定义的Type数组类型,跟进去,看到一个static方法,我把理解写到代码里
/***rawType 是原生classactualTypeArguments 是java原生class类的接口类,这个弄了个数组还只有一个长度actualTypeArguments 是一个Type类型的Set**/static Type<?>[] convertTypeArguments(final Class<?> rawType, final java.lang.reflect.Type[] actualTypeArguments, final Set<java.lang.reflect.Type> recursiveBounds) { //首先获取原生class类的所有属性信息,是一个原生的TypeVariable数组 TypeVariable<?>[] typeVariables = rawType.getTypeParameters(); //申明一个自定义Type类型数组备用 Type<?>[] resultTypeArguments = new Type<?>[typeVariables.length]; //判断set里边包含当前这个Type吗?第一次来肯定是不包含的,跳过 if (recursiveBounds.contains(rawType)) { return new Type<?>[0]; } //actualTypeArguments.length == 0 && typeVariables.length > 0 这个判断成立,进去 else if (actualTypeArguments.length == 0 && typeVariables.length > 0) { //将当前的class对象加到set中 recursiveBounds.add(rawType); //下面这个将原生类转换成自定义的Type类型,看解释(2) resultTypeArguments = convertTypeArgumentsFromAncestry(rawType, recursiveBounds); recursiveBounds.remove(rawType); } else if (actualTypeArguments.length < typeVariables.length) { throw new IllegalArgumentException("Must provide all type-arguments or none"); } else { for (int i = 0, len = actualTypeArguments.length; i < len; ++i) { java.lang.reflect.Type t = actualTypeArguments[i]; recursiveBounds.add(rawType); resultTypeArguments[i] = TypeFactory.limitedValueOf(t, recursiveBounds); recursiveBounds.remove(rawType); } } return resultTypeArguments; }
(2)
resultTypeArguments = convertTypeArgumentsFromAncestry(rawType, recursiveBounds);
/** 这个第一个参数是原生class对象 第二个参数是将上面的class对象用Set包装了下*/ static Type<?>[] convertTypeArgumentsFromAncestry(final Class<?> rawType, final Set<java.lang.reflect.Type> bounds) {//声明了一个LinkedHashMap备用 Map<TypeVariable<?>, Type<?>> typesByVariable = new LinkedHashMap<TypeVariable<?>, Type<?>>(); //从原生的class对象中获取属性列表,并将属性列表添加到上面的LinkedHashMap for (TypeVariable<?> var : rawType.getTypeParameters()) { typesByVariable.put(var, TypeFactory.limitedValueOf(var, bounds)); } //新建一个原生Type类型的set Set<java.lang.reflect.Type> genericAncestors = new LinkedHashSet<java.lang.reflect.Type>(); genericAncestors.add(rawType.getGenericSuperclass()); genericAncestors.add(rawType.getSuperclass()); genericAncestors.addAll(Arrays.asList(rawType.getGenericInterfaces())); genericAncestors.addAll(Arrays.asList(rawType.getInterfaces())); Iterator<java.lang.reflect.Type> iter = genericAncestors.iterator(); while (iter.hasNext()) { java.lang.reflect.Type ancestor = iter.next(); iter.remove(); if (ancestor instanceof ParameterizedType) { ParameterizedType superType = (ParameterizedType) ancestor; TypeVariable<?>[] variables = ((Class<?>) superType.getRawType()).getTypeParameters(); java.lang.reflect.Type[] actuals = superType.getActualTypeArguments(); for (int i = 0; i < variables.length; ++i) { Type<?> resolvedActual = TypeFactory.limitedValueOf(actuals[i], bounds); TypeVariable<?> var = (TypeVariable<?>) ((actuals[i] instanceof TypeVariable) ? actuals[i] : variables[i]); Type<?> currentActual = typesByVariable.get(var); if (currentActual != null) { typesByVariable.put(var, getMostSpecificType(currentActual, resolvedActual)); } } } else if (ancestor instanceof Class) { Class<?> superType = (Class<?>) ancestor; TypeVariable<?>[] variables = superType.getTypeParameters(); for (int i = 0; i < variables.length; ++i) { Type<?> resolvedActual = TypeFactory.limitedValueOf(variables[i], bounds); Type<?> currentActual = typesByVariable.get(variables[i]); if (currentActual != null) { typesByVariable.put(variables[i], getMostSpecificType(currentActual, resolvedActual)); } } } } return typesByVariable.values().toArray(new Type<?>[0]); }
未完待续。。。。
- easy-mapper 源码分析
- (5-3)Mapper源码分析
- Mybatis源码分析获取Mapper
- MapReudce源码分析之Mapper
- Tomcat源码阅读之Mapper分析
- Hadoop中Mapper过程的源码分析
- Tomcat源码分析--资源映射器Mapper
- mybatis源码分析之Mapper代理实现分析
- Mybatis3源码分析(三):解析mapper的xml配置文件
- MapReduce框架Mapper和Reducer类源码分析
- Mybatis3源码分析(三):解析mapper的xml配置文件
- Mybatis3源码分析(19)-Mapper生成过程-示例
- Mybatis3源码分析(20)-Mapper实现-配置加载
- Mybatis3源码分析(21)-Mapper实现-动态代理
- Mybatis3源码分析(21)-Mapper实现-动态代理
- 【Tomcat9源码分析】Mapper路由映射器的设计
- Mybatis源码分析(一)--Mapper的动态代理
- Mybatis3源码分析(21)-Mapper实现-动态代理
- C# params关键字
- PHP 安全性漫谈
- LayoutAnimation实现ListView的子View淡入淡出效果效果图
- Android应用间跳转
- 无限极分类
- easy-mapper 源码分析
- 两个恶心的崩溃
- SQL性能优化
- MySQL官方文档中NULL和空值的区别
- 发现了一个非常好的介绍Codeblocks使用的网站
- Android TextView两端完美对齐解决方案
- java导出Excel通用方法
- 【小工具】简单的小钟表
- 研究性学习的MOOR平台--关键是对接了综评以及资料共享