说说类型转换

最近工作中总是发现类型不匹配的现象。比如说在mybatis里面,如果表里面的数据类型是timestamp ,如果你的dto对应字段是Date类型那么就会发生出来的数据不是你要的样子。这个时候你需要自己写一个自定义的转换器,并将其配置到mybatis的配置文件中。 第一步:配置


    <typeHandlers>
        <typeHandler  handler="com.framework.demo.utils.MyDateTypeHandler" javaType="String" jdbcType="TIMESTAMP"/>
    </typeHandlers>

java代码主要是实现接口TypeHandler<T>接口。比如讲temestamp类型的数据转换成自定义格式的string 第二步:编写代码

public class MyDateTypeHandler implements TypeHandler<String> {

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void setParameter(java.sql.PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, String.valueOf(parameter));
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
         String sqlTimestamp = rs.getString(columnName);
        if (sqlTimestamp != null) {
            try {
                return sqlTimestamp.substring(0,sqlTimestamp.lastIndexOf("."));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
        Date sqlTimestamp = rs.getDate(columnIndex);

        if (sqlTimestamp != null) {
            try {
                return dateFormat.format(sqlTimestamp);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    public String getResult(java.sql.CallableStatement cs, int columnIndex) throws SQLException {
        String sqlTimestamp = cs.getString(columnIndex);
        if (sqlTimestamp != null) {
            try {
                return dateFormat.format(sqlTimestamp);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

这样就完成了,我发现如果不写这个自定义也能够转换,只是出来的字符串 多了一个(.0)。不知道什么原因。

除了mybatis经常会遇到类型转换之外,spring中的类型也是随处可见。 #案例 在我的项目中有这样一种配置

 @RequestMapping(value = "{id}/delete", method = RequestMethod.GET)
    public String showDeleteForm(@PathVariable("id") M m, Model model) 

这句话不只是将id映射到M中对应字段中,是将id对应的表行信息映射到M中去。 第一步,自定义类型转换器。

public class DomainClassConverter<T extends ConversionService & ConverterRegistry> implements
        ConditionalGenericConverter, ApplicationContextAware {

    private  ApplicationContext beanFactory;

    public DomainClassConverter(){
        this.beanFactory = null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.beanFactory =applicationContext;
    }

    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        if(FieldAccessVo.class.isAssignableFrom(targetType.getObjectType())&&targetType.hasAnnotation(PathVariable.class)){
            return  true;
        }
        return false;
    }

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(Object.class, Object.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null || !StringUtils.hasText(source.toString())) {
            return null;
        }
        Class<?> domainType = targetType.getType();
        String daoImpl = domainType.getSimpleName().substring(0,1).toLowerCase()+domainType.getSimpleName().substring(1, domainType.getSimpleName().length())+"Dao";
        try{
            ConfigurableBaseSqlMapDao bean =(ConfigurableBaseSqlMapDao)beanFactory.getBean(daoImpl);
            return bean.findById(Long.valueOf(source.toString()));
        }catch (Exception e1){

            return null;
        }
  }

这里的原意是找到service的实现类,然后带着事务去查数据。可是我发现,即使我找到了service的实现类,这个代理类要想真正被调用,需要method方法,而我底层是泛型,method方法怎么也映射到不。于是,我找到dao对应的方法去调用去获取数据。 第二步,将类型转换器配置到xml中 spring为我们提供了工厂类,我们只要将我们要注册的converter实例放置到工厂类相应参数当中,最后会将转换器放置到上下文中

  <!-- 类型转换及数据格式化 -->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
        <set>
            <bean id="domainClassConverter" class="com.framework.demo.web.controller.converter.DomainClassConverter"/>
            <bean id="stringToDateConverter" class="com.framework.demo.utils.converter.StringToDateConverter"/>
        </set>
        </property>
    </bean>

这样转换器就根据matcher匹配之后调用转换方法去做转换动作。 #看看底层 FormattingConversionServiceFactoryBean这个工厂类很重要。他里面有转换器converter和格式转换器formater。最重要的里面有一个FormattingConversionService。这个类图解如下

输入图片说明 图1 放置位置

可以看出这个类其实是一个注册器的派生,既能够注册converter又能够注册formater。它的实例化如下:

this.conversionService = new DefaultFormattingConversionService(this.embeddedValueResolver, this.registerDefaultFormatters);

这样什么converter、formater都在conversionService里面了,那么spring怎么用的呢? 首先:来看看类AnnotationDrivenBeanDefinitionParser 其中的方法getConversionService 将它包装成beanDefinition

private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) {
		RuntimeBeanReference conversionServiceRef;
		if (element.hasAttribute("conversion-service")) {
			conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service"));
		}
		else {
			RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
			conversionDef.setSource(source);
			conversionDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			String conversionName = parserContext.getReaderContext().registerWithGeneratedName(conversionDef);
			parserContext.registerComponent(new BeanComponentDefinition(conversionDef, conversionName));
			conversionServiceRef = new RuntimeBeanReference(conversionName);
		}
		return conversionServiceRef;
	}

这样它就在上下文中了。在需要转换的类将conversionService注入到类中做类型转换。

看图一发现转换这个类的关系非常紧密,也是为什么spring代码可扩展和可维护性特别强。我们不妨从头到尾看看每一个接口,每一个实现类是组合组织的。

首先是converter的注册中心:接口ConverterRegistry。该接口对外提供注册和注销converter的功能。

然后是formatter的注册中心:接口FormatterRegistry。该接口继承自ConverterRegistry,所以说实现了该接口的实现了肯定也实现了ConverterRegistry。图中可以看出FormattingConversionService类实现了该接口。

第三是接口ConversionService:有了上面的两个接口,那么converter和formatter就能够注册进来了。第三个接口ConversionService就是定义了如何使用这些注册进来的converter和formatter。 第四个就是ConfigurableConversionService接口:该接口扩展了两个接口ConversionService, ConverterRegistry 。将注册converter、formatter的功能和使用converter、formatter的功能结合在了一起。

第四个是实现类GenericConversionService。该实现类实现了converter、formatter注册操作和如何使用converter、formatter的操作。

注意这里面的代码组织。

首先是内部类Converters。这个类对所有注册过来的converter进行统一管理。

第二个就是缓存组织:converterCache。每一次都那么从converters里面查效率低下。配置一个HashMap类型的converterCache可以有效的缓存源类型和目标类型与转换器之间的关系。其中ConverterCacheKey的组织还是很重要的。一定要记住定义equals 和compareTo方法。确保这个key唯一性。

最后,我们发现它还给工厂留了一个是怕。将factory和converter做了一个适配。这样能够将factory“伪装”成converter注册到converters中。

最后一个实现类DefaultFormattingConversionService。该类在GenericConversionService的基础上组合了三个jsr354Present、jsr310Present、jodaTimePresent。将相应类型的converter注册进来。

至此一个简单的类组织接口就介绍完毕。我们平时写业务代码的时候总是觉得可扩展性不足,可维护性捉襟见肘。往往问题出现在代码的组织结构上。我们很少用内部类,将功能细分。往往忘了按照功能 划分各个类的关系。接口是提供服务在载体。只有接口的服务定义的清晰,代码才能够做到可扩展。在本案例能够让我们感觉到代码结构十分清晰,就是因为接口见关系清晰的原因吧。

欢迎加入qq群: 122567875 欢迎关注微信公众号:hyssop的后花园

转载于:https://my.oschina.net/zjItLife/blog/741427

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值