目录
反射工具包
类型转换
Reflector类主要实现了对JavaBean的元数据属性的封装,比如可读属性列表,可写属性列表,反射操作的封装,如属性对应的setter方法,getter方法的反射调用。
public class Reflector {/** JavaBean 的 Class类型,在调用 Reflector 的构造方法时初始化该值 */private final Class> type;/** 可读的属性列表 */private final String[] readablePropertyNames;private final String[] writablePropertyNames;/** key 属性名,value 该属性名对应的 setter方法调用器 */private final Map setMethods = new HashMap<>();private final Map getMethods = new HashMap<>();/** key 属性名称,value 该属性 setter方法的返回值类型 */private final Map> setTypes = new HashMap<>();private final Map> getTypes = new HashMap<>();/** type 的默认构造方法 */private Constructor> defaultConstructor;/** 所有属性名称的集合 */private Map caseInsensitivePropertyMap = new HashMap<>();/*** 里面的大部分方法都是通过简单的 JDK反射操作 实现的* @param clazz*/public Reflector(Class> clazz) {type = clazz;addDefaultConstructor(clazz);// 处理 clazz 中的 所有getter方法,填充 getMethods集合 和 getTypes集合addGetMethods(clazz);addSetMethods(clazz);// 处理没有 getter、setter方法 的字段addFields(clazz);// 根据 getMethods、setMethods集合 初始化可读、可写的属性readablePropertyNames = getMethods.keySet().toArray(new String[0]);writablePropertyNames = setMethods.keySet().toArray(new String[0]);// 初始化 caseInsensitivePropertyMap集合,key 属性名的大写,value 属性名for (String propName : readablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}for (String propName : writablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}}
}
ReflectorFactory
public interface ReflectorFactory {boolean isClassCacheEnabled();void setClassCacheEnabled(boolean classCacheEnabled);/*** 主要看一下这个方法,通过 JavaBean 的 clazz 获取该 JavaBean 对应的 Reflector*/Reflector findForClass(Class> type);
}public class DefaultReflectorFactory implements ReflectorFactory {private boolean classCacheEnabled = true;/** 大部分容器及工厂设计模式的管用伎俩,key:JavaBean的clazz,value:JavaBean对应的Reflector实例 */private final ConcurrentMap, Reflector> reflectorMap = new ConcurrentHashMap<>();/*** 实例化一个 ConcurrentMap全局变量,然后暴露一个方法从 map 中获取目标对象,这种设计是很多框架都会用的*/@Overridepublic Reflector findForClass(Class> type) {if (classCacheEnabled) {// synchronized (type) removed see issue #461return reflectorMap.computeIfAbsent(type, Reflector::new);} else {return new Reflector(type);}}public DefaultReflectorFactory() {}@Overridepublic boolean isClassCacheEnabled() {return classCacheEnabled;}@Overridepublic void setClassCacheEnabled(boolean classCacheEnabled) {this.classCacheEnabled = classCacheEnabled;}
}/*** 支持定制化 ReflectorFactory*/
public class CustomReflectorFactory extends DefaultReflectorFactory {}
ObjectFactory
该类也是接口加一个默认实现类,并且支持自定义扩展,Mybatis中有很多这样的设计方式。
/*** MyBatis uses an ObjectFactory to create all needed new Objects.*/
public interface ObjectFactory {/*** Sets configuration properties.*/default void setProperties(Properties properties) {// NOP}/*** Creates a new object with default constructor.*/ T create(Class type);/*** Creates a new object with the specified constructor and params.*/ T create(Class type, List> constructorArgTypes, List
类型转换是实现ORM的重要一环,由于数据库中的数据类型与Java语言的数据类型并不对等,所以在PrepareStatement为sql语句绑定参数时,需要从Java类型转换成JDBC类型,从结果集获取数据时又要将JDBC类型转换成Java类型,Mybatis使用TypeHandler完成了上述的双向转换。
JdbcType
Mybatis通过JdbcType这个枚举类型代表了JDBC中的数据类型。
/*** 该枚举类描述了 JDBC 中的数据类型*/
public enum JdbcType {/** This is added to enable basic support for the* ARRAY data type - but a custom type handler is still required*/ARRAY(Types.ARRAY),BIT(Types.BIT),TINYINT(Types.TINYINT),SMALLINT(Types.SMALLINT),INTEGER(Types.INTEGER),BIGINT(Types.BIGINT),FLOAT(Types.FLOAT),REAL(Types.REAL),DOUBLE(Types.DOUBLE),NUMERIC(Types.NUMERIC),DECIMAL(Types.DECIMAL),CHAR(Types.CHAR),VARCHAR(Types.VARCHAR),LONGVARCHAR(Types.LONGVARCHAR),DATE(Types.DATE),TIME(Types.TIME),TIMESTAMP(Types.TIMESTAMP),BINARY(Types.BINARY),VARBINARY(Types.VARBINARY),LONGVARBINARY(Types.LONGVARBINARY),NULL(Types.NULL),OTHER(Types.OTHER),BLOB(Types.BLOB),CLOB(Types.CLOB),BOOLEAN(Types.BOOLEAN),CURSOR(-10), // OracleUNDEFINED(Integer.MIN_VALUE + 1000),NVARCHAR(Types.NVARCHAR), // JDK6NCHAR(Types.NCHAR), // JDK6NCLOB(Types.NCLOB), // JDK6STRUCT(Types.STRUCT),JAVA_OBJECT(Types.JAVA_OBJECT),DISTINCT(Types.DISTINCT),REF(Types.REF),DATALINK(Types.DATALINK),ROWID(Types.ROWID), // JDK6LONGNVARCHAR(Types.LONGNVARCHAR), // JDK6SQLXML(Types.SQLXML), // JDK6DATETIMEOFFSET(-155), // SQL Server 2008TIME_WITH_TIMEZONE(Types.TIME_WITH_TIMEZONE), // JDBC 4.2 JDK8TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE); // JDBC 4.2 JDK8public final int TYPE_CODE;/** 该静态集合维护了 常量编码 与 JdbcType 之间的关系 */private static Map codeLookup = new HashMap<>();static {for (JdbcType type : JdbcType.values()) {codeLookup.put(type.TYPE_CODE, type);}}JdbcType(int code) {this.TYPE_CODE = code;}public static JdbcType forCode(int code) {return codeLookup.get(code);}}
TypeHandler
TypeHandler是Mybatis中所有类型转换器的顶层接口,主要用于实现数据从Java类型到JdbcType 类型的相互转换。
public interface TypeHandler {/** 通过 PreparedStatement 为 SQL语句 绑定参数时,将数据从 Java类型 转换为 JDBC类型 */void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;/** 从结果集获取数据时,将数据由 JDBC类型 转换成 Java类型 */T getResult(ResultSet rs, String columnName) throws SQLException;T getResult(ResultSet rs, int columnIndex) throws SQLException;T getResult(CallableStatement cs, int columnIndex) throws SQLException;}/*** 可用于实现自定义的 TypeHandler*/
public abstract class BaseTypeHandler extends TypeReference implements TypeHandler {/*** 只是处理了一些数据为空的特殊情况,非空数据的处理都交给子类去处理*/@Overridepublic void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {if (parameter == null) {if (jdbcType == null) {throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");}try {ps.setNull(i, jdbcType.TYPE_CODE);} catch (SQLException e) {throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "+ "Cause: " + e, e);}} else {try {setNonNullParameter(ps, i, parameter, jdbcType);} catch (Exception e) {throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "+ "Try setting a different JdbcType for this parameter or a different configuration property. "+ "Cause: " + e, e);}}}@Overridepublic T getResult(ResultSet rs, String columnName) throws SQLException {try {return getNullableResult(rs, columnName);} catch (Exception e) {throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);}}
}public class IntegerTypeHandler extends BaseTypeHandler {/*** NonNull 就是 NoneNull,非空的意思*/@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)throws SQLException {// IntegerTypeHandler 就调用 PreparedStatement 的 setInt()方法// BooleanTypeHandler 就调用 PreparedStatement 的 setBoolean()方法// 其它的基本数据类型,以此类推ps.setInt(i, parameter);}@Overridepublic Integer getNullableResult(ResultSet rs, String columnName)throws SQLException {int result = rs.getInt(columnName);return result == 0 && rs.wasNull() ? null : result;}@Overridepublic Integer getNullableResult(ResultSet rs, int columnIndex)throws SQLException {int result = rs.getInt(columnIndex);return result == 0 && rs.wasNull() ? null : result;}@Overridepublic Integer getNullableResult(CallableStatement cs, int columnIndex)throws SQLException {int result = cs.getInt(columnIndex);return result == 0 && cs.wasNull() ? null : result;}
}
TypeHandler主要用于单个参数的类型转换,如果要将多个列的值转换成一个Java对象,可以在映射文件中定义合适的映射规则
TypeHandlerRegistry
TypeHandlerRegistry主要负责管理所有已知的TypeHandler,Mybatis在初始化过程中会为所有已知的TypeHandler创建对象,并注册到TypeHandlerRegistry
// TypeHandlerRegistry 中的核心字段如下/** 该集合主要用于从结果集读取数据时,将数据从 JDBC类型 转换成 Java类型 */private final Map> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);/*** 记录了 Java类型 向指定 JdbcType 转换时,需要使用的 TypeHandler对象。* 如:String 可能转换成数据库的 char、varchar 等多种类型,所以存在一对多的关系*/private final Map>> typeHandlerMap = new ConcurrentHashMap<>();/** key:TypeHandler 的类型;value:该 TypeHandler类型 对应的 TypeHandler对象 */private final Map, TypeHandler>> allTypeHandlersMap = new HashMap<>();
注册TypeHandler对象
TypeHandlerRegistry中的register()方法实现了注册TypeHandler对象的功能,该方法存在多种重载,但大多数register()方法最终都会走register(Type javaType, JdbcType jdbcType, TypeHandler> handler) 的处理逻辑,该重载方法中分别指定了TypeHandler能够处理的Java类型、JDBC类型、TypeHandler对象。
/*** TypeHandlerRegistry 中对 register()方法 实现了多种重载,本 register()方法* 被很多重载方法调用,用来完成注册功能。*/private void register(Type javaType, JdbcType jdbcType, TypeHandler> handler) {if (javaType != null) {Map> map = typeHandlerMap.get(javaType);if (map == null || map == NULL_TYPE_HANDLER_MAP) {map = new HashMap<>();typeHandlerMap.put(javaType, map);}map.put(jdbcType, handler);}allTypeHandlersMap.put(handler.getClass(), handler);}
TypeHandlerRegistry还提供了扫描并注册指定包目录下TypeHandler实现类的register()方法重载。
/*** 从指定 包名packageName 中获取自定义的 TypeHandler实现类*/public void register(String packageName) {ResolverUtil> resolverUtil = new ResolverUtil<>();// 查找指定包下的 TypeHandler接口实现类resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);Set>> handlerSet = resolverUtil.getClasses();for (Class> type : handlerSet) {// 忽略掉 内部类、接口 及 抽象类if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {register(type);}}}
TypeHandlerRegistry的构造方法,其通过多种register()方法重载,完成了所有已知的 TypeHandler的重载。
/*** 进行 Java 及 JDBC基本数据类型 的 TypeHandler 注册* 除了注册 Mybatis 提供的 基本TypeHandler 外,我们也可以添加自定义的 TypeHandler* 接口实现,在 mybatis-config.xml配置文件 中 节点 下添加相应的* 节点配置,并指定自定义的 TypeHandler实现类。Mybatis 在初始化时* 会解析该节点,并将 TypeHandler类型 的对象注册到 TypeHandlerRegistry 中供 Mybatis 后续使用*/public TypeHandlerRegistry() {register(Boolean.class, new BooleanTypeHandler());register(boolean.class, new BooleanTypeHandler());register(JdbcType.BOOLEAN, new BooleanTypeHandler());register(JdbcType.BIT, new BooleanTypeHandler());register(Byte.class, new ByteTypeHandler());register(byte.class, new ByteTypeHandler());register(JdbcType.TINYINT, new ByteTypeHandler());register(Short.class, new ShortTypeHandler());register(short.class, new ShortTypeHandler());register(JdbcType.SMALLINT, new ShortTypeHandler());register(Integer.class, new IntegerTypeHandler());register(int.class, new IntegerTypeHandler());register(JdbcType.INTEGER, new IntegerTypeHandler());register(Long.class, new LongTypeHandler());register(long.class, new LongTypeHandler());register(Float.class, new FloatTypeHandler());register(float.class, new FloatTypeHandler());register(JdbcType.FLOAT, new FloatTypeHandler());register(Double.class, new DoubleTypeHandler());register(double.class, new DoubleTypeHandler());register(JdbcType.DOUBLE, new DoubleTypeHandler());register(Reader.class, new ClobReaderTypeHandler());register(String.class, new StringTypeHandler());register(String.class, JdbcType.CHAR, new StringTypeHandler());register(String.class, JdbcType.CLOB, new ClobTypeHandler());register(String.class, JdbcType.VARCHAR, new StringTypeHandler());register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());register(String.class, JdbcType.NCHAR, new NStringTypeHandler());register(String.class, JdbcType.NCLOB, new NClobTypeHandler());register(JdbcType.CHAR, new StringTypeHandler());register(JdbcType.VARCHAR, new StringTypeHandler());register(JdbcType.CLOB, new ClobTypeHandler());register(JdbcType.LONGVARCHAR, new StringTypeHandler());register(JdbcType.NVARCHAR, new NStringTypeHandler());register(JdbcType.NCHAR, new NStringTypeHandler());register(JdbcType.NCLOB, new NClobTypeHandler());register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());register(JdbcType.ARRAY, new ArrayTypeHandler());register(BigInteger.class, new BigIntegerTypeHandler());register(JdbcType.BIGINT, new LongTypeHandler());register(BigDecimal.class, new BigDecimalTypeHandler());register(JdbcType.REAL, new BigDecimalTypeHandler());register(JdbcType.DECIMAL, new BigDecimalTypeHandler());register(JdbcType.NUMERIC, new BigDecimalTypeHandler());register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());register(Instant.class, new InstantTypeHandler());register(LocalDateTime.class, new LocalDateTimeTypeHandler());register(LocalDate.class, new LocalDateTypeHandler());register(LocalTime.class, new LocalTimeTypeHandler());register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());register(OffsetTime.class, new OffsetTimeTypeHandler());register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());register(Month.class, new MonthTypeHandler());register(Year.class, new YearTypeHandler());register(YearMonth.class, new YearMonthTypeHandler());register(JapaneseDate.class, new JapaneseDateTypeHandler());}
TypeHandler
TypeHandlerRegistry其实就是一个容器,前面注册了一堆东西,也就是为了方便获取,其对应的方法为getTypeHandler(),该方法也存在多种重载,其中最重要的一个重载为 getTypeHandler(Type type, JdbcType jdbcType),它会根据指定的Java类型和JdbcType类型查找相应的TypeHandler对象。
/*** 获取 TypeHandler对象* getTypeHandler()方法 亦存在多种重载,而本重载方法被其它多个重载方法调用*/private TypeHandler getTypeHandler(Type type, JdbcType jdbcType) {if (ParamMap.class.equals(type)) {return null;}// Java数据类型 与 JDBC数据类型 的关系往往是一对多,// 所以一般会先根据 Java数据类型 获取 Map>对象// 再根据 JDBC数据类型 获取对应的 TypeHandler对象Map> jdbcHandlerMap = getJdbcHandlerMap(type);TypeHandler> handler = null;if (jdbcHandlerMap != null) {handler = jdbcHandlerMap.get(jdbcType);if (handler == null) {handler = jdbcHandlerMap.get(null);}if (handler == null) {// #591handler = pickSoleHandler(jdbcHandlerMap);}}// type drives generics herereturn (TypeHandler) handler;}
上一篇:c++运算符重载的几个例子记录
下一篇:华为机试 - 面试