参加过开发的人肯定都知道每个领域的对象都有自己的命名规则,每个公司都有自己的规范,这样就可以让维护人员减少很多时间去琢磨别人写的代码。
概念:
VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。
模型:
下面以一个时序图建立简单模型来描述上述对象在三层架构应用中的位置
l 用户发出请求(可能是填写表单),表单的数据在展示层被匹配为VO。
l 展示层把VO转换为服务层对应方法所要求的DTO,传送给服务层。
l 服务层首先根据DTO的数据构造(或重建)一个DO,调用DO的业务方法完成具体业务。
l 服务层把DO转换为持久层对应的PO(可以使用ORM工具,也可以不用),调用持久层的持久化方法,把PO传递给它,完成持久化操作。
l 对于一个逆向操作,如读取数据,也是用类似的方式转换和传递,略。
所以,这种情况下,比如Service层的对象都是DTO对象,但是往往Dao层返回的是一个DO对象,这时候就需要将DO对象的属性封装到DTO,这里提供一个工具类,仅供参考,将要封装的对象继承该类,调用clone方法就可以完成同属姓名的clone。
package com.lmcxcloud.common.utils.copy; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; /** * 基础POJO类 * @author zhonghuashishan * */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class AbstractObject { /** * 浅度克隆 * @param clazz * @param <T> * @return * @throws Exception */ public <T> T clone(Class<T> clazz) throws Exception { T target = clazz.newInstance(); BeanCopierUtils.copyProperties(this, target); return target; } /** * 浅度克隆 * @param target * @param <T> * @return * @throws Exception */ public <T> T clone(T target) throws Exception { BeanCopierUtils.copyProperties(this, target); return target; } /** * 深度克隆 * @param clazz * @param cloneDirection * @param <T> * @return * @throws Exception */ public <T> T clone(Class<T> clazz, Integer cloneDirection) throws Exception { // 先完成基本字段的浅克隆 T target = clazz.newInstance(); BeanCopierUtils.copyProperties(this, target); // 完成所有List类型的深度克隆 // CategoryDTO Class<?> thisClazz = this.getClass(); Field[] fields = thisClazz.getDeclaredFields(); for(Field field : fields) { field.setAccessible(true); // 如果判断某个字段是List类型的 // field = private List<Relation> relations; if(field.getType() != List.class) { continue; } // field.getType() List 不是 List<Relation> // List<RelationDTO>集合 List<?> list = (List<?>) field.get(this); if(list == null || list.size() == 0) { continue; } // 获取List集合中的泛型类型 // RelationDTO Class<?> listGenericClazz = getListGenericType(field); // 获取要克隆的目标类型 // 假设CloneDirection是反向,此时获取到的就是RelationVO Class<?> cloneTargetClazz = getCloneTargetClazz(listGenericClazz, cloneDirection); // 将list集合克隆到目标list集合中去 List clonedList = new ArrayList(); cloneList(list, clonedList, cloneTargetClazz, cloneDirection); // 获取设置克隆好的list的方法名称 // setRelations Method setFieldMethod = getSetCloneListFieldMethodName(field, clazz); setFieldMethod.invoke(target, clonedList); // target是CategoryVO对象,此时就是调用CategoryVO的setRelations方法, // 将克隆好的List<CategoryVO>给设置进去 } return target; } /** * 将一个list克隆到另外一个list * @param sourceList * @param targetList * @param cloneTargetClazz * @param cloneDirection * @throws Exception */ private void cloneList(List sourceList, List targetList, Class cloneTargetClazz, Integer cloneDirection) throws Exception { for(Object object : sourceList) { AbstractObject targetObject = (AbstractObject) object; // 将集合中的RelationDTO,调用其clone()方法,将其往RelationVO去克隆 AbstractObject clonedObject = (AbstractObject) targetObject.clone( cloneTargetClazz, cloneDirection); // RelationVO的集合 targetList.add(clonedObject); } } /** * 获取list集合的泛型类型 * @param field * @return * @throws Exception */ private Class<?> getListGenericType(Field field) throws Exception { // genericType = List<RelationDTO>,不是List Type genericType = field.getGenericType(); if(genericType instanceof ParameterizedType){ ParameterizedType parameterizedType = (ParameterizedType) genericType; return (Class<?>)parameterizedType.getActualTypeArguments()[0]; } return null; } /** * 获取目标类名 * @param clazz * @param cloneDirection * @return * @throws Exception */ private Class<?> getCloneTargetClazz(Class<?> clazz, Integer cloneDirection) throws Exception { String cloneTargetClassName = null; // ReflectionDTO String className = clazz.getName(); if(cloneDirection.equals(CloneDirection.FORWARD)) { if(className.endsWith(DomainType.VO)) { cloneTargetClassName = className.substring(0, className.length() - 2) + "DTO"; } else if(className.endsWith(DomainType.DTO)) { cloneTargetClassName = className.substring(0, className.length() - 3) + "DO"; } } if(cloneDirection.equals(CloneDirection.OPPOSITE)) { if(className.endsWith(DomainType.DO)) { cloneTargetClassName = className.substring(0, className.length() - 2) + "DTO"; } else if(className.endsWith(DomainType.DTO)) { cloneTargetClassName = className.substring(0, className.length() - 3) + "VO"; } } return Class.forName(cloneTargetClassName); } /** * 获取设置克隆好的list的方法名称 * @param field * @param clazz * @return * @throws Exception */ private Method getSetCloneListFieldMethodName(Field field, Class<?> clazz) throws Exception { String name = field.getName(); String setMethodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1); Method setFieldMethod = null; for(Method method : clazz.getDeclaredMethods()) { if(method.getName().equals(setMethodName)) { setFieldMethod = method; break; } } return setFieldMethod; } }
工具类导入:
package com.lmcxcloud.common.utils.copy; import net.sf.cglib.beans.BeanCopier; import java.util.HashMap; import java.util.Map; /** * BeanCopier工具类 */ public class BeanCopierUtils { /** * BeanCopier缓存 */ public static Map<String, BeanCopier> beanCopierCacheMap = new HashMap<String, BeanCopier>(); /** * 将source对象的属性拷贝到target对象中去 * @param source source对象 * @param target target对象 */ public static void copyProperties(Object source, Object target){ String cacheKey = source.getClass().toString() + target.getClass().toString(); BeanCopier beanCopier = null; // 线程1和线程2,同时过来了 if (!beanCopierCacheMap.containsKey(cacheKey)) { // 两个线程都卡这儿了 // 但是此时线程1先获取到了锁,线程2就等着 synchronized(BeanCopierUtils.class) { // 线程1进来之后,发现这里还是没有那个BeanCopier实例 // 此时线程2,会发现缓存map中已经有了那个BeanCopier实例了,此时就不会进入if判断内的代码 if(!beanCopierCacheMap.containsKey(cacheKey)) { // 进入到这里会创建一个BeanCopier实例并且放在缓存map中 beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false); beanCopierCacheMap.put(cacheKey, beanCopier); } else { beanCopier = beanCopierCacheMap.get(cacheKey); } } } else { beanCopier = beanCopierCacheMap.get(cacheKey); } beanCopier.copy(source, target, null); } }