Converter模式 及其在Spring中的应用


前言

在开发中,类型转换是我们天天都要做的事情.如何优雅的进行类型转换,而不是在逻辑代码中到处充斥着setter方法或者强制转换? 请使用Converter模式


提示:以下是本篇文章正文内容,下面案例可供参考

一、Converter关系图

在这里插入图片描述

二、代码

Converter抽象类

abstract class Converter<T, U> {

    private Function<T,U> convertFunction;

    public Converter(Function<T, U> convertFunction) {
        this.convertFunction = convertFunction;
    }

    public U convert(T t){
        return convertFunction.apply(t);
    }
}

Converter类 保存了一个Function类实例: convertFunction, 利用convertFunction可以将一个对象T 转化为对象U.

Converter子类

  • 我们这里假设要将User类,进行归档,保存为UserHistory类,看看User类是如何保存为UserHistory类的
public class User {
    private String name;
    private int mathScore;
    private int englishScore;

    public User(String name, int mathScore, int englishScore) {
        this.name = name;
        this.mathScore = mathScore;
        this.englishScore = englishScore;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMathScore() {
        return mathScore;
    }

    public void setMathScore(int mathScore) {
        this.mathScore = mathScore;
    }

    public int getEnglishScore() {
        return englishScore;
    }

    public void setEnglishScore(int englishScore) {
        this.englishScore = englishScore;
    }
}
public class UserHistory {
    private String name;
    private int totalScore;
    private Date recordDate;

    public UserHistory(String name, int totalScore, Date recordDate) {
        this.name = name;
        this.totalScore = totalScore;
        this.recordDate = recordDate;
    }

    @Override
    public String toString() {
        return "UserHistory{" +
                "name='" + name + '\'' +
                ", totalScore=" + totalScore +
                ", recordDate=" + recordDate +
                '}';
    }
}
public class UserToUserHistoryConverter extends Converter<User, UserHistory> {

    public UserToUserHistoryConverter() {
        super(UserToUserHistoryConverter::convertToHistory);
    }

    private static UserHistory convertToHistory(User user) {
        return new UserHistory(user.getName(), user.getMathScore() + user.getEnglishScore(), new Date());
    }
}
  • 我们把具体的转换行为抽象到了convertToHistory方法中

具体使用

public static void main(String[] args) {
        User user = new User("张三", 60, 70);
        UserToUserHistoryConverter converter = new UserToUserHistoryConverter();
        UserHistory history = converter.convert(user);
        System.out.println(history);
    }
输出:
UserHistory{name='张三', totalScore=130, recordDate=Wed Apr 21 16:07:49 CST 2021}

优点

  • 转换行为抽离到了具体的Converter中去执行,简化了主逻辑代码

三、Spring中是如何使用Converter模式的

在这里插入图片描述

  • 在spring-core包下的convert包下的类,大部分都是用来做转换用的,具体也是使用的Converter模式

spring 中的Converter类

/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.convert.converter;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * A converter converts a source object of type {@code S} to a target of type {@code T}.
 * 从资源S 转换到 资源T
 * <p>Implementations of this interface are thread-safe and can be shared.
 *
 * <p>Implementations may additionally implement {@link ConditionalConverter}.
 *
 * @author Keith Donald
 * @author Josh Cummings
 * @since 3.0
 * @param <S> the source type
 * @param <T> the target type
 */
@FunctionalInterface
public interface Converter<S, T> {

	/**
	 * Convert the source object of type {@code S} to target type {@code T}.
	 * @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
	 * @return the converted object, which must be an instance of {@code T} (potentially {@code null})
	 * @throws IllegalArgumentException if the source cannot be converted to the desired target type
	 */
	@Nullable
	T convert(S source);

	/**
	 * Construct a composed {@link Converter} that first applies this {@link Converter}
	 * to its input, and then applies the {@code after} {@link Converter} to the
	 * result.
	 * @param after the {@link Converter} to apply after this {@link Converter}
	 * is applied
	 * @param <U> the type of output of both the {@code after} {@link Converter}
	 * and the composed {@link Converter}
	 * @return a composed {@link Converter} that first applies this {@link Converter}
	 * and then applies the {@code after} {@link Converter}
	 * @since 5.3
	 */
	default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
		Assert.notNull(after, "After Converter must not be null");
		return (S s) -> {
			T initialResult = convert(s);
			return (initialResult != null ? after.convert(initialResult) : null);
		};
	}
}
  • 在spring的Converter类中,使用了更加简便的方法
  1. 直接使用@FunctionalInterface注解将Converter声明为了一个lambda表达式,将S类实例转换为T实例
  2. 以及andThen 这个default方法为Converter类增加了链式调用的功能.

具体的实现类

我们看一个比较常用的方便理解: String 转 Boolean

/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.convert.support;

import java.util.HashSet;
import java.util.Set;

import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;

/**
 * Converts String to a Boolean.
 *
 * @author Keith Donald
 * @author Juergen Hoeller
 * @since 3.0
 */
final class StringToBooleanConverter implements Converter<String, Boolean> {

	private static final Set<String> trueValues = new HashSet<>(8);

	private static final Set<String> falseValues = new HashSet<>(8);

	static {
		trueValues.add("true");
		trueValues.add("on");
		trueValues.add("yes");
		trueValues.add("1");

		falseValues.add("false");
		falseValues.add("off");
		falseValues.add("no");
		falseValues.add("0");
	}


	@Override
	@Nullable
	public Boolean convert(String source) {
		String value = source.trim();
		if (value.isEmpty()) {
			return null;
		}
		value = value.toLowerCase();
		if (trueValues.contains(value)) {
			return Boolean.TRUE;
		}
		else if (falseValues.contains(value)) {
			return Boolean.FALSE;
		}
		else {
			throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
		}
	}

}
  • 具体代码一看就明白了,与map中的字符串相等就返回true,反之则为false;

spring Converter的其他扩展

  • spring convert包下不单单只有简单的Converter转换,还有其他的扩展功能,包括ConditionalConverter:按条件转换,GenericConverter 通用的转换类,以及ConversionService类: spring的大部分常用的转换都可以用ConversionService来进行操作,因为它默认提供了多个Converter
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
		addScalarConverters(converterRegistry);
		addCollectionConverters(converterRegistry);

		converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new StringToTimeZoneConverter());
		converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
		converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

		converterRegistry.addConverter(new ObjectToObjectConverter());
		converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new FallbackObjectToStringConverter());
		converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
	}
  • 如果需要转换,只需要调用ConversionService的Convert方法即可
/**
	 * Convert the given {@code source} to the specified {@code targetType}.
	 * @param source the source object to convert (may be {@code null})
	 * @param targetType the target type to convert to (required)
	 * @return the converted object, an instance of targetType
	 * @throws ConversionException if a conversion exception occurred
	 * @throws IllegalArgumentException if targetType is {@code null}
	 */
	@Nullable
	<T> T convert(@Nullable Object source, Class<T> targetType);

总结

Converter模式在类型转换方面有着广泛的应用,学习了Converter模式之后,希望自己能够不再主代码中写一大堆的setter方法了,也可以借鉴spring中的convert包,将常用转换写的MySelfBeanConvertService中来简化代码.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值