Mybatis中的设计模式
Mybatis中使用了大量的设计模式。
以下列举一些看源码时,觉得还不错的用法:
创建型模式
工厂方法模式
DataSourceFactory
通过不同的子类工厂,实例化不同的DataSource
TransactionFactory
通过不同的工厂,生产不同的Transaction
单例模式
ErrorContext
是单例模式,但只是线程级别的:
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>();private ErrorContext() {}public static ErrorContext instance() {ErrorContext context = LOCAL.get();if (context == null) {context = new ErrorContext();LOCAL.set(context);}return context;}
Configuration
类虽然正常情况下只有一个实例,但是它的设计并不符合单例模式
public Configuration(Environment environment) {this();this.environment = environment;}
可以看到,它的构造器并没有私有化,我们可以new多个实例
LogFactory
也不是单例模式,每次都会通过构造器实例化对象
private LogFactory() {// disable construction}public static Log getLog(Class<?> aClass) {return getLog(aClass.getName());}public static Log getLog(String logger) {try {return logConstructor.newInstance(logger);} catch (Throwable t) {throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t);}}
结构型模式
代理模式
在初始化配置时,会调用MapperRegistry
的addMapper方法将Mapper接口存储在knownMappers
中,key为Mapper接口,value为MapperProxyFactory实例
public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {knownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
当我们通过UserMapper mapper = sqlSession.getMapper(UserMapper.class);
获取Mapper接口时,则会调用MapperRegistry#getMapper
,返回Mapper接口的代理类
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}
代理类的生成
public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}
protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}
可以看到,使用的是JDK动态代理,MapperProxy
实现了InvocationHandler
接口,我们关注MapperProxy#invoke
即可:
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (method.isDefault()) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}
调用mapper接口,最终执行的就是我们编写的SQL
行为型模式
模板方法模式
BaseExecutor
BaseExecutor
实现了Executor
接口,定义了模板方法,并将钩子方法留给子类实现:
模板方法 | 钩子方法 |
---|---|
query | doQuery |
update | doUpdate |
flushStatements | doFlushStatements |
queryCursor | doQueryCursor |
BaseTypeHandler
BaseTypeHandler
实现了TypeHandler
接口,定义模板方法,将钩子方法留给子类实现:
-
setNonNullParameter
-
getNullableResult
模板方法 | 钩子方法 |
---|---|
setParameter | setNonNullParameter |
getResult(ResultSet rs, String columnName) | getNullableResult(ResultSet rs, String columnName) |
getResult(ResultSet rs, int columnIndex) | getNullableResult(ResultSet rs, int columnIndex) |
getResult(CallableStatement cs, int columnIndex) | getNullableResult(CallableStatement cs, int columnIndex) |
策略模式
在DefaultParameterHandler#setParameters
中,
@Overridepublic void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional paramsvalue = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException | SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}}
重点关注:
TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException | SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}
根据参数映射,拿到类型处理器,就是策略模式的应用,不同的类型处理器setParameter
方法实现并不一样,这就是不同的参数类型,使用不同的类型处理器处理。
在解析结果集时,也是一样的,不同的类型需要使用不同的类型处理器获取结果:
DefaultResultSetHandler#getPropertyMappingValue
通过ResultMap的属性映射:
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)throws SQLException {if (propertyMapping.getNestedQueryId() != null) {return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);} else if (propertyMapping.getResultSet() != null) {addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?return DEFERRED;} else {final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);return typeHandler.getResult(rs, column);}}
DefaultResultSetHandler#applyAutomaticMappings
自动映射也是一样的:
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);boolean foundValues = false;if (!autoMapping.isEmpty()) {for (UnMappedColumnAutoMapping mapping : autoMapping) {final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);if (value != null) {foundValues = true;}if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {// gcode issue #377, call setter on nulls (value is not 'found')metaObject.setValue(mapping.property, value);}}}return foundValues;}