利用dozer与拦截器做字段翻译映射回显页面
通常任何系统做设计的时候,都会用到数据字典,复杂的系统可能还会有N多业务字段的标识,甚至树结构,而这些相关联的业务表中,存储的字段,都是只存一个id或者一个标识,但我们在页面给客户展示的时候,却是给客户展示这个标识所表达的完整意思。 比如一张个人信息表,可能会涉及,居住区域,性别,状态等等,在个人信息表,我们存储居住区域1,性别1,状态1,展示在页面的时候,却想要展示居住区域:北京,性别:男,状态:良好。
1.通常我们要实现上述功能,一种在查询的时候,用sql解决,但是这样,如果业务逻辑比较复杂,涉及的表多,关联查询的性能是很大问题。
2.另一种,会在程序端进行控制,遍历集合,然后处理,甚至更有在页面进行处理,这样整个的代码的通用性低,维护成本高,耦合度高。
这里我提供一种解决方案,如果页面返回的vo需要转换,只需一个在controller方法中增加一个注解,就可以解决,废话不多说,开始:
1.所属jar包,(默认是使用spring的了,spring的相关jar包都已经引好。)
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.5.1</version>
</dependency>
2.思路:dozer是一个对象转换的利器,只需要简单的配置,就可以进行对象间的映射,所以我们利用dozer进行对象映射,但是这样,每次需要需要转换都需要去调用dozer,所以在利用aop的方法拦截,做统一处理。
spring与dozer配置
<bean class="com.utils.dozer.service.DozerBeanMapperFactory">
<property name="mappingFiles">
<list>
<value>classpath:dozer/**/dozer*.xml</value><!--这里指的是相关实体类转换的配置文件 -->
</list>
</property>
<property name="customConvertersWithId">
<map>
<entry key="dict" value-ref="dictConverter" />
<entry key="genericIntegerString" value-ref="genericIntegerStringConverter" />
<entry key="genericStringString" value-ref="genericStringStringConverter" />
<entry key="user" value-ref="userConverter" />
<entry key="genericArrayString" value-ref="genericArrayStringConverter" />
<entry key="yearsBetString" value-ref="yearsBetStringConverter" />
<!-- 这里这些是指自定义的转换器,比如dictConverter就是数据字典的转换器
-->
</map>
</property>
</bean>
<!-- <bean id="genericIntegerStringConverter" class="com.utils.dozer.converter.GenericIntegerStringConverter"/>
<bean id="dictConverter" class="com.utils.dozer.converter.DictConverter"/> -->
<context:component-scan base-package="com.utils.dozer" />
entry指的是自定义的转换器,key指和id对应,value-ref指的是对应转换器的class
dozer的
<mapping type="one-way">
<class-a>com.facede.user.model.User</class-a>
<class-b>com.web.portal.process.input.UserCondition</class-b>
<field custom-converter-id="dict" custom-converter-param="1000001">
<a>lDstate</a>
<b>lDstateName</b>
</field>
<field custom-converter-id="genericStringString"
custom-converter-param="[A:我是A][B:我是B]">
<a>cType</a>
<b>cTypeName</b>
</field>
</mapping>
这里主要举例数据字典翻译器和一个通用的string转string的翻译器.这里user和userCondtion是映射的实体类,你可以,详情可以去dozer的官网看一下。
需要讲一下的是,custom-converter-id 和custom-converter-param的配合使用,官方文档里可能也没有这样使用。
custom-converter-id指的就是上面spring配置中entry的key,custom-converter-param指的是,在转换器中需要使用的参数
getParameter()获取custom-converter-param传递参数
我这里是在项目启动的时候 ,就把数据字典全都加载到了redis缓存中了,然后数据字典直接缓存取,当然可能出现新增的情况,缓存当中没有,如果缓存中没有,再去数据库取就是了
package com.utils.dozer.converter;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dozer.DozerConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import com.utils.redis.RedisCommonDao;
/**
* 数据字典项目翻译
* genius
*/
@Component
public class DictConverter extends DozerConverter<Integer, String> {
private static final Log logger = LogFactory.getLog(DictConverter.class);
@Autowired
RedisCommonDao redisCommonDao;
public DictConverter() {
super(Integer.class, String.class);
}
@Override
public String convertTo(Integer dictId , String destination) {
if (null == dictId || null == getParameter()) {
return null;
}
String result = null;
try {
Integer dictModeId = NumberUtils.toInt(getParameter(), 0);//
JSONObject value = (JSONObject) redisCommonDao.getValue("FY-"+dictModeId+"-"+dictId);
result = value.getString("vcDataName");
} catch (RuntimeException e) {
logger.error("根据字典编号获取字典名称错误:" + e.getMessage());
} catch (Exception e) {
logger.error("根据字典编号获取字典名称错误:" + e.getMessage());
}
return result;
}
@Override
public Integer convertFrom(String arg0, Integer arg1) {
return null;
}
}
然后自定义注解,这个这里就不贴代码了,如果有不明白的,可以百度一下spring自定义注解
拦截器中
这个地方配置的MethodInterceptor拦截器与原理,也不做讲解了,提供一个思路,需要用到拦截器
<!-- 翻译拦截器 -->
<bean id="dozerCustomInterceptor" class="com.common.web.interceptor.DozerCustomInterceptor"></bean>
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Controller</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>dozerCustomInterceptor</value>
</list>
</property>
</bean>
public class DozerCustomInterceptor implements MethodInterceptor{
@Autowired
DozerMapperService dozerMapperService;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
Method method = invocation.getMethod();
Object[] arguments = invocation.getArguments();
DozerCustomConverter dozerCustomConverter = AnnotationUtils.findAnnotation(method, DozerCustomConverter.class);
if(dozerCustomConverter!=null && result!=null) {
//进行dozer转换处理,如果有自定义注解,那么就需要做翻译处理,然后根据注解的类型,选择不同的转换处理,因为不同的类型返回的结果值存放的位置可能不同>
Class<?> entryClass = (Class<?>)AnnotationUtils.getAnnotationAttributes(dozerCustomConverter).get("entryClass");
DozerCustomConverterTYPE type = (DozerCustomConverterTYPE) AnnotationUtils.getAnnotationAttributes(dozerCustomConverter).get("value");
if(type==DozerCustomConverterTYPE.SIMPLE) {
result = this.resolveSimple(result,entryClass);
} else if(type==DozerCustomConverterTYPE.JSONRESULT) {
result = this.resolveJsonResult(result,entryClass);
} else if(type==DozerCustomConverterTYPE.MODELANDVIEW) {
result = this.resolveModelAndView(result,entryClass);
} else if(type==DozerCustomConverterTYPE.VIEW && (result instanceof String || result instanceof View)) {
this.resolveView(arguments,entryClass);
} else if(type==DozerCustomConverterTYPE.JSONOFJSONRESULT) {
result = this.resolveJsonofjsonResult(result,entryClass);
}
}
return result;
}
dozerService
package com.utils.dozer.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.dozer.BeanFactory;
import org.dozer.CustomConverter;
import org.dozer.DozerBeanMapper;
import org.dozer.DozerEventListener;
import org.dozer.Mapper;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
/**
*
* @author genius
*
*/
public class DozerBeanMapperFactory implements FactoryBean<Mapper>, InitializingBean, DisposableBean {
private DozerBeanMapper beanMapper;
private Resource[] mappingFiles;
private List<CustomConverter> customConverters;
private List<DozerEventListener> eventListeners;
private Map<String, BeanFactory> factories;
private Map<String, CustomConverter> customConvertersWithId;
/**
* @param mappingFiles
* Spring resource definition
*/
public final void setMappingFiles(final Resource[] mappingFiles) {
this.mappingFiles = mappingFiles;
}
public final void setCustomConverters(final List<CustomConverter> customConverters) {
this.customConverters = customConverters;
}
public final void setEventListeners(final List<DozerEventListener> eventListeners) {
this.eventListeners = eventListeners;
}
public final void setFactories(final Map<String, BeanFactory> factories) {
this.factories = factories;
}
public final void setCustomConvertersWithId(Map<String, CustomConverter> customConvertersWithId) {
this.customConvertersWithId = customConvertersWithId;
}
// ==================================================================================================================================
// interface 'FactoryBean'
// ==================================================================================================================================
public final Mapper getObject() throws Exception {
return this.beanMapper;
}
public final Class<Mapper> getObjectType() {
return Mapper.class;
}
public final boolean isSingleton() {
return true;
}
// ==================================================================================================================================
// interface 'InitializingBean'
// ==================================================================================================================================
public final void afterPropertiesSet() throws Exception {
this.beanMapper = new DozerBeanMapper();
if (this.mappingFiles != null) {
final List<String> mappings = new ArrayList<String>(this.mappingFiles.length);
for (Resource mappingFile : this.mappingFiles) {
mappings.add(mappingFile.getURL().toString());
}
this.beanMapper.setMappingFiles(mappings);
}
if (this.customConverters != null) {
this.beanMapper.setCustomConverters(this.customConverters);
}
if (this.eventListeners != null) {
this.beanMapper.setEventListeners(this.eventListeners);
}
if (this.factories != null) {
this.beanMapper.setFactories(this.factories);
}
if (this.customConvertersWithId != null) {
this.beanMapper.setCustomConvertersWithId(this.customConvertersWithId);
}
}
/**
*
* @throws Exception
*/
public void destroy() throws Exception {
if (this.beanMapper != null) {
this.beanMapper.destroy();
}
}
}
package com.utils.dozer.service;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.dozer.Mapper;
/**
* 翻译实现类
* @author genius
*
*/
@Singleton
@Named
public class DozerMapperService implements MapperService {
@Inject
private Mapper mapper;
public <T> T map(Object source, Class<T> destinationClass) {
return mapper.map(source, destinationClass);
}
public void map(Object source, Object destination) {
mapper.map(source, destination);
}
public <T> T map(Object source, Class<T> destinationClass, String mapId) {
return mapper.map(source, destinationClass, mapId);
}
public void map(Object source, Object destination, String mapId) {
mapper.map(source, destination, mapId);
}
public <T> List<T> mapList(List<?> source, Class<T> destinationClass) {
List<T> list = new ArrayList<T>();
for (Object obj : source) {
list.add(mapper.map(obj, destinationClass));
}
return list;
}
public <T> List<T> mapList(List<?> source, Class<T> destinationClass, String mapId) {
List<T> list = new ArrayList<T>();
for (Object obj : source) {
list.add(mapper.map(obj, destinationClass, mapId));
}
return list;
}
}