一、本文介绍
本文只专注于mybaits初始化流程,旨在帮助各位弄清楚mybatis初始化过程中都进行那些工作,这些初始化工作在那些类中。并没有对每项初始化工作进行详细的介绍,如果有这方面的需求请移步别处,避免耽误您的时间。只要各位学会了怎么去看mybatis源码,那我相信如果以后想要深入的学习细节的实现会变得非常简单。好了,话不多说,下面进入正题。
二、简单介绍一下mybatis基础配置
MybatisConfig 类是mybatis 的基础配置类
public class MybatisConfig implements ApplicationContextAware {
private static PropertiesUtil propertiesUtil;
private ApplicationContext applicationContext;
static {
propertiesUtil = new PropertiesUtil("jdbc.properties");
}
/**
* 配置连接池
* @return
*/
@Bean
public DruidDataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(propertiesUtil.getProperty("db.url"));
dataSource.setUsername(propertiesUtil.getProperty("db.username"));
dataSource.setPassword(propertiesUtil.getProperty("db.password"));
dataSource.setDriverClassName(propertiesUtil.getProperty("db.driverName"));
dataSource.setMaxActive(Integer.parseInt(propertiesUtil.getProperty("db.maxActive")));
return dataSource;
}
/**
* 配置sqlfactory
* @return
* @throws Exception
*/
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
Resource[] resources = applicationContext.getResources("classpath:mappers/**/*.xml");
factoryBean.setMapperLocations(resources);
//设置分页的插件
Interceptor[] interceptors = new Interceptor[]{new MyBatisPageInterceptopr()};
factoryBean.setPlugins(interceptors);
return factoryBean.getObject();
}
/**
* 配置全局事务
* @return
*/
@Bean
public DataSourceTransactionManager transactionManager(){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
Dao 类是访问数据库的工具类
@Repository
public class Dao extends SqlSessionDaoSupport {
/**
* 设置工厂类
*
* @param sqlSessionFactory sql工厂类
*/
@Autowired
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
super.setSqlSessionFactory(sqlSessionFactory);
}
public T get(String prefix,String key,Object params){
return getSqlSession().selectOne(prefix+key,params);
}
public void insert(String prefix,String key,Object params){
getSqlSession().insert(prefix+key,params);
}
public void update(String prefix,String key,Object params){
getSqlSession().update(prefix+key,params);
}
public void delete(String prefix,String key,Object params){
getSqlSession().delete(prefix+key,params);
}
public List getList(String prefix, String key, Object params) {
return this.getSqlSession().selectList(prefix + key, params);
}
/**
* 获取分页的数据
* @param prefix
* @param key
* @param params
* @param page
* @return
*/
public Page page(String prefix, String key, Map params, Page page) {
params.put("page",page);
page.setList(this.getSqlSession().selectList(prefix + key, params));
return page;
}
MyBatisPageInterceptopr 是自定义的分页拦截器,如果想要学习这块的知识,请自行百度
@Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}))
public class MyBatisPageInterceptopr implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler =getActuralHandlerObject(invocation);
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
String sql = statementHandler.getBoundSql().getSql();
//检查是否是select语句,如果不是则直接放行
if(checkIsSelectFalg(sql)){
return invocation.proceed();
}
BoundSql boundSql = statementHandler.getBoundSql();
Object paramObject = boundSql.getParameterObject();
Page page = getPageParam(paramObject);
//如果参数是空则直接放行
if(page==null){
return invocation.proceed();
}
//获取页码
Integer pageNum = page.getPageNum();
//获取每页条数
Integer pageSize = page.getPageSize();
int total = getTotal(invocation,metaStatementHandler,boundSql);
//将动态获取到的分页参数会天道pageParam中
setTotleToParam(page,total,pageSize);
return updateSql2Limit(invocation,metaStatementHandler,pageNum,pageSize);
}
/**
* 生成代理对象
* @param o
* @return
*/
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
private StatementHandler getActuralHandlerObject(Invocation invocation){
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
//获取statementHandler的代理类
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
Object object = null;
//分离代理链,目标可能被多个拦截器拦截,分离出最原始的目标类
while (metaStatementHandler.hasGetter("h")){
object = metaStatementHandler.getValue("h");
metaStatementHandler = SystemMetaObject.forObject(object);
}
if(object==null){
return statementHandler;
}
return (StatementHandler)object;
}
/**
* 判断是否是select语句
* @param sql
* @return
*/
private boolean checkIsSelectFalg(String sql){
return !sql.trim().toLowerCase().contains("select");
}
/**
* 获取分页参数
* @param parameObject
* @return
*/
private Page getPageParam(Object parameObject){
if(parameObject==null){
return null;
}
Page page =null;
if(parameObject instanceof Map){
Map params = (Map)parameObject;
for(Map.Entry entry:params.entrySet()){
if(entry.getValue() instanceof Page){
return (Page)entry.getValue();
}
}
}else if(parameObject instanceof Page){
page = (Page)parameObject;
}
return page;
}
/**
* 获取总条数
* @param invocation
* @param metaStatementHandler
* @param boundSql
* @return
*/
private int getTotal(Invocation invocation, MetaObject metaStatementHandler, BoundSql boundSql){
//获取mapper文件中当前语句的配置信息
MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
//获取所有的配置
Configuration configuration = mappedStatement.getConfiguration();
//获取当前查询的sql
String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
String countSql = "select count(*) as total from ("+sql+")$_paging";
//获取connection 连接对象,用于和执行countsql
Connection conn = (Connection) invocation.getArgs()[0];
PreparedStatement ps = null;
int total = 0;
try{
//预编译统计总记录数的sql
ps = conn.prepareStatement(countSql);
//构建统计的BoundSql
BoundSql countBoundSql = new BoundSql(configuration,countSql,boundSql.getParameterMappings(),boundSql.getParameterObject());
//构建paramterHandler ,用于设置统计的sql参数
ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement,boundSql.getParameterObject(),countBoundSql);
//设置总数的sql参数
parameterHandler.setParameters(ps);
//执行查询语句
ResultSet rs = ps.executeQuery();
while (rs.next()){
total = rs.getInt("total");
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return total;
}
/**
* 设置条数参数到Page对象中
* @param page
* @param total
* @param pageSize
*/
private void setTotleToParam(Page page,int total,int pageSize){
page.setTotalCount(total);
page.setTotalPage(total%pageSize==0?total/pageSize:(total/pageSize+1));
}
/**
* 修改原始的sql语句为分页sql语句
* @param invocation
* @param metaStatementHandler
* @param pageNum
* @param pageSize
* @return
*/
private Object updateSql2Limit(Invocation invocation,MetaObject metaStatementHandler,int pageNum,int pageSize) throws InvocationTargetException, IllegalAccessException, SQLException {
String sql = (String)metaStatementHandler.getValue("delegate.boundSql.sql");
//构建分页的sql语句
String limitSql = "select * from ("+sql+")$_paging_table limit ?,?";
//修改当前要执行的sql语句
metaStatementHandler.setValue("delegate.boundSql.sql",limitSql);
//相当于调用prepare方法,预编译的sql并且加入参数,但是少了分页的两个参数,他返回一个ps
PreparedStatement ps = (PreparedStatement) invocation.proceed();
//设置分页的两个参数
int count = ps.getParameterMetaData().getParameterCount();
ps.setInt(count-1,(pageNum-1)*pageSize);
ps.setInt(count,pageSize);
return ps;
}
jdbc.properties 配置信息
db.url=jdbc:mysql://localhost:3306/pms?prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true
db.username=root
db.password=huc123456
db.driverName = com.mysql.jdbc.Driver
db.maxActive=20
service 请求实例
@Service
public class LevelService {
private static final String CLASSNAME = LevelService.class.getName()+".";
@Autowired
private Dao dao;
public Object listLevel(Map params,Page page){
return dao.page("","listLevels",params,page);
}
public LevelBean getLevelById(Long id){
return dao.get(CLASSNAME,"getLevelById",id);
}
public List getLevelList(Map params){
return dao.getList(CLASSNAME,"listLevels",params);
}
}
三、mybatis的初始化流程介绍
首先先看mybatisConfig 中对于 SqlSessionFactory的配置
/**
* 配置sqlfactory
* @return
* @throws Exception
*/
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
//设置连接池信息
factoryBean.setDataSource(dataSource());
//设置mapper文件路劲信息
Resource[] resources = applicationContext.getResources("classpath:mappers/**/*.xml");
factoryBean.setMapperLocations(resources);
//设置分页的插件
Interceptor[] interceptors = new Interceptor[]{new MyBatisPageInterceptopr()};
factoryBean.setPlugins(interceptors);
//生成SqlSessionFactory实例
return factoryBean.getObject();
}
在此处我们能看到生成SqlSessionFactory的入口,那就是SqlSessionFactoryBean.getObject()方法,下面我们看一下这个方法
public SqlSessionFactory getObject() throws Exception {
//如果sqlSessionFactory为空,则进行初始化
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}
return this.sqlSessionFactory;
}
然后看 afterPropertiesSet()方法
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
这个方法前面是断言,继续后续的 buildSqlSessionFactory()方法
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
XMLConfigBuilder xmlConfigBuilder = null;
Configuration configuration;
//如果configuration 不为空,则直接获取configuration
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (logger.isDebugEnabled()) {
logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
//定义configuration,它构造方法里面初始化了默认的参数,可以点进去看一下
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
//设置相应的myabtis自己的工厂类
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
//设置typeAliases
String[] typeHandlersPackageArray;
String[] arr$;
int len$;
int i$;
String packageToScan;
if (StringUtils.hasLength(this.typeAliasesPackage)) {
typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeAliasesPackage, ",; \t\n");
arr$ = typeHandlersPackageArray;
len$ = typeHandlersPackageArray.length;
for(i$ = 0; i$ < len$; ++i$) {
packageToScan = arr$[i$];
configuration.getTypeAliasRegistry().registerAliases(packageToScan, this.typeAliasesSuperType == null ? Object.class : this.typeAliasesSuperType);
if (logger.isDebugEnabled()) {
logger.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
int len$;
if (!ObjectUtils.isEmpty(this.typeAliases)) {
Class[] arr$ = this.typeAliases;
len$ = arr$.length;
for(len$ = 0; len$ < len$; ++len$) {
Class> typeAlias = arr$[len$];
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (logger.isDebugEnabled()) {
logger.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
//设置自定义的插件
if (!ObjectUtils.isEmpty(this.plugins)) {
Interceptor[] arr$ = this.plugins;
len$ = arr$.length;
for(len$ = 0; len$ < len$; ++len$) {
Interceptor plugin = arr$[len$];
configuration.addInterceptor(plugin);
if (logger.isDebugEnabled()) {
logger.debug("Registered plugin: '" + plugin + "'");
}
}
}
//设置typeHandlers 类型处理器
if (StringUtils.hasLength(this.typeHandlersPackage)) {
typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage, ",; \t\n");
arr$ = typeHandlersPackageArray;
len$ = typeHandlersPackageArray.length;
for(i$ = 0; i$ < len$; ++i$) {
packageToScan = arr$[i$];
configuration.getTypeHandlerRegistry().register(packageToScan);
if (logger.isDebugEnabled()) {
logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
TypeHandler[] arr$ = this.typeHandlers;
len$ = arr$.length;
for(len$ = 0; len$ < len$; ++len$) {
TypeHandler> typeHandler = arr$[len$];
configuration.getTypeHandlerRegistry().register(typeHandler);
if (logger.isDebugEnabled()) {
logger.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (logger.isDebugEnabled()) {
logger.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception var23) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var23);
} finally {
ErrorContext.instance().reset();
}
}
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
//设置依赖的环境
Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment);
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException var22) {
throw new NestedIOException("Failed getting a databaseId", var22);
}
}
//根据配置的mapper路径 逐个解析mapper文件
if (!ObjectUtils.isEmpty(this.mapperLocations)) {
Resource[] arr$ = this.mapperLocations;
len$ = arr$.length;
for(i$ = 0; i$ < len$; ++i$) {
Resource mapperLocation = arr$[i$];
if (mapperLocation != null) {
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments());
// 尽心mapper文件的解析
xmlMapperBuilder.parse();
} catch (Exception var20) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var20);
} finally {
ErrorContext.instance().reset();
}
if (logger.isDebugEnabled()) {
logger.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
}
} else if (logger.isDebugEnabled()) {
logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
上面的这个方法先构建了Configuration,然后创建了sqlSessionFactory,我们先看一下mapper文件的解析,也就是xmlMapperBuilder.parse()方法
public void parse() {
//如果该mapper没有加载则加载mapper文件
if (!this.configuration.isResourceLoaded(this.resource)) {
//加载mapper文件
this.configurationElement(this.parser.evalNode("/mapper"));
this.configuration.addLoadedResource(this.resource);
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingChacheRefs();
this.parsePendingStatements();
}
主要看configurationElement()方法,里面定义了解析mapper文件的流程
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
} else {
//读取命名空间
this.builderAssistant.setCurrentNamespace(namespace);
//读取缓存的引用
this.cacheRefElement(context.evalNode("cache-ref"));
//读取缓存配置
this.cacheElement(context.evalNode("cache"));
//解析parameterMap配置
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//解析resultMap配置
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析定义的sql标签
this.sqlElement(context.evalNodes("/mapper/sql"));
//解析定义select|insert|update|delete 标签信息
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);
}
}
这里我们主要看一下解析select|insert|update|delete 标签的方法,如果有兴趣,其他的方法可自己研究一下
private void buildStatementFromContext(List list) {
if (this.configuration.getDatabaseId() != null) {
this.buildStatementFromContext(list, this.configuration.getDatabaseId());
}
this.buildStatementFromContext(list, (String)null);
}
继续看里面的buildStatementFromContext()方法
private void buildStatementFromContext(List list, String requiredDatabaseId) {
//遍历mapper的select,update,delete 标签进行解析
Iterator i$ = list.iterator();
while(i$.hasNext()) {
XNode context = (XNode)i$.next();
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser);
}
}
}
具体解析方法是statementParser.parseStatementNode()
public void parseStatementNode() {
//这里解析标签上的一些属性信息
String id = this.context.getStringAttribute("id");
String databaseId = this.context.getStringAttribute("databaseId");
if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
Integer fetchSize = this.context.getIntAttribute("fetchSize");
Integer timeout = this.context.getIntAttribute("timeout");
String parameterMap = this.context.getStringAttribute("parameterMap");
String parameterType = this.context.getStringAttribute("parameterType");
Class> parameterTypeClass = this.resolveClass(parameterType);
String resultMap = this.context.getStringAttribute("resultMap");
String resultType = this.context.getStringAttribute("resultType");
String lang = this.context.getStringAttribute("lang");
LanguageDriver langDriver = this.getLanguageDriver(lang);
Class> resultTypeClass = this.resolveClass(resultType);
String resultSetType = this.context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
String nodeName = this.context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
includeParser.applyIncludes(this.context.getNode());
this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
//这里着重介绍一下,sqlSource里面封装的标签定义的sql的基本sql语句以及参数的配置
//如果没有myabtis的动态sql标签,如等,则这里返回的是RawSqlSource,如果
//是动态sql语句,则返回的是DynamicSqlSource。sqlsource主要用来生成BoundSql
//后期执行sql语句会用到这个类
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
String resultSets = this.context.getStringAttribute("resultSets");
String keyProperty = this.context.getStringAttribute("keyProperty");
String keyColumn = this.context.getStringAttribute("keyColumn");
String keyStatementId = id + "!selectKey";
keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
Object keyGenerator;
if (this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
}
//这个方法里面就是构造了MappedStatement,并将其添加到configuration中
//MappedStatement,这里面封装了mapper里面的所有配置,是myabtis
//核心类之一。
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
好了,到这里 configuration大体的初始化流程就清除了,那我们返回之前的SqlSessionFactoryBean.buildSqlSessionFactory()方法里面的 this.sqlSessionFactoryBuilder.build(configuration)
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
最终就是构建了一个DefaultSqlSessionFactory。
到这里myabtis大体的初始化流程就结束了,其实整体而言myabtis初始化并不复杂,主要是构建Configuration。Configuration里面保存了myabtis所有的配置信息,其中包括 类型解析器,类型别名,resultMap ,拦截器,mapperstatement,还有一些默认的处理器,所以各位有时间需要看一下这个类里面大体的属性。