简述要点:
- 加载xml配置信息:
- 解析xml:my-batis.xml-->configuration-->mappers-->mapper.resource节点,创建XMLMapperBuilder
- XMLMapperBuilder:xml解析器。
- 调用config.configuration(resource),将其加载到loadedResources**(加的配置文件路径)**
- 调用bindMapperForNamespace():获取xml的namespace,通过classLoader.forName()加载对应的接口。调用configuration.mapperRegistry.getMapper(type)创建MapperProxyFactory
- 获取代理对象、执行对应sql:
- session.getMapper(Class<?>):直接调用DefaultSqlSessionz中的configuration.getMapper(type, this).
- configuration:存放了所有的环境、mapper、接口配置。
- MapperRegistry:存放了<K=接口、V=接口工厂>knownMappers。
- MapperProxyFactory:存放了接口(mapperInterface),接口方法及Mapper.xml文件的方法 (methodCache).
- MapperProxyFactory:先直接通过Proxy.newProxyInstance()来实现代理类。
- new 一个MapperProxy
- MapperProxy:存放了接口动态代理方法,等待接口方法调用。
- 当调用方法时,直接使用methodCache,找到对应的MapperMethod,执行:MapperMethod.execute()。
- MapperMethod:方法和sql的关联映射类。
- 当调用 MapperMethod.execute()时,对比方法menth来执行对应的sql。
核心配置:Configuration
protected Environment environment; //环境:事务管理、数据源(连接池)
protected boolean safeRowBoundsEnabled = false;
protected boolean safeResultHandlerEnabled = true;
protected boolean mapUnderscoreToCamelCase = false;
protected boolean aggressiveLazyLoading = true;
protected boolean multipleResultSetsEnabled = true;
protected boolean useGeneratedKeys = false;
protected boolean useColumnLabel = true;
protected boolean cacheEnabled = true;
protected boolean callSettersOnNulls = false;
protected String logPrefix;
protected Class <? extends Log> logImpl;
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
protected Integer defaultStatementTimeout;
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
protected Properties variables = new Properties(); //
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
protected MapperRegistry mapperRegistry = new MapperRegistry(this); '接口动态代理实例化工厂'
protected boolean lazyLoadingEnabled = false;
protected ProxyFactory proxyFactory;
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
*
* @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300</a> (google code)
*/
protected Class<?> configurationFactory;
'类型和别名的解析工厂'
protected final InterceptorChain interceptorChain = new InterceptorChain();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
'构造器:存放了默认的别名解析类'
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
- Environment:环境:事务管理、数据源(连接池)
- Properties:属性对象,用于解析column
- objectFactory:一个对象属性设置工厂,通过 method.invoke() 将查询的值存放到实例中
- DefaultObjectWrapperFactory:对象创建工厂,用来处理MetaObject,将column的值赋给对象示例
public class MetaObject {
private Object originalObject; //需要设置的对象示例
private ObjectWrapper objectWrapper; //mathod.invoke()调用属性的set方法将查询的值存放到实例中
private ObjectFactory objectFactory;
private ObjectWrapperFactory objectWrapperFactory; //挂载DefaultObjectWrapperFactory
}
- mapperRegistry:存放的mapper及对应的xml信息,以namespace为key
- 添加过程:
- 先通mybatis的核心配置,mybatis-config.xml 里面的<mapper/>加载实际的ORM配置
- 在ORM配置中,通过namespace查找到对应的 interface,并将interface上的注解也解析到配置信息中
- 当使用getMapper(Class<?> type) 的时候,会通过JDK的动态代理实现一个
- mapperRegistry 的属性 knownMappers
- key: namesepece的值
- MapperProxyFactory:以interface的类加载器加载实现的实例
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
- typeHandlerRegistry:存放了各种类型的解析方法,用来做ORM时,处理mysql返回的二进制结果
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
- typeAliasRegistry:存放了各种别名对应的对象
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
- mappedStatements:根据每个select|update|delete|insert 的id+namespect的名称作为key,存放每条sql的信息
- caches:存放cache对象
- resultMaps:用来存放解析后的信息resultMap信息
- parameterMaps:存放了param的信息,用于解析传入参数转换成sql信息
- keyGenerators:主键
MapperProxyFactory 动态代理:
IcStockMapper mapper = session.getMapper(IcStockMapper.class);
int count = mapper.selectByGoodsIdsCount(null);
- getMapper()调用:
- 调用session中的配置configuration
- 通过configuration中的配置mapperRegistry获取icstockMapper的动态代理
- 动态代理会通过 IcStockMapper 类加载器(classloader),绑定创建成mapperProxy
- 每次getMapper()都会创建一个新的动态代理
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
- selectByGoodsIdsCount()动态调用
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args); //真实调用的地方
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod / null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT / command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE / command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE / command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT / command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param); //mybatis的调用方法
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result / null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
}