mybatis 源码解析之初始化流程
一、本文介绍
本文只专注于mybaits初始化流程,旨在帮助各位弄清楚mybatis初始化过程中都进行那些工作,这些初始化工作在那些类中。并没有对每项初始化工作进行详细的介绍,如果有这方面的需求请移步别处,避免耽误您的时间。只要各位学会了怎么去看mybatis源码,那我相信如果以后想要深入的学习细节的实现会变得非常简单。好了,话不多说,下面进入正题。
二、简单介绍一下mybatis基础配置
MybatisConfig 类是mybatis 的基础配置类
[code]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 类是访问数据库的工具类
[code]@Repository public class Dao extends SqlSessionDaoSupport { /** * 设置工厂类 * * @param sqlSessionFactory sql工厂类 */ @Autowired public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { super.setSqlSessionFactory(sqlSessionFactory); } public <T> 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 <T> List<T> 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<String,Object> params, Page page) { params.put("page",page); page.setList(this.getSqlSession().selectList(prefix + key, params)); return page; }
MyBatisPageInterceptopr 是自定义的分页拦截器,如果想要学习这块的知识,请自行百度
[code]@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<String,Object> params = (Map<String,Object>)parameObject; for(Map.Entry<String,Object> 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 配置信息
[code]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 请求实例
[code]@Service public class LevelService { private static final String CLASSNAME = LevelService.class.getName()+"."; @Autowired private Dao dao; public Object listLevel(Map<String,Object> params,Page page){ return dao.page("","listLevels",params,page); } public LevelBean getLevelById(Long id){ return dao.get(CLASSNAME,"getLevelById",id); } public List<LevelBean> getLevelList(Map<String,Object> params){ return dao.getList(CLASSNAME,"listLevels",params); } }
三、mybatis的初始化流程介绍
首先先看mybatisConfig 中对于 SqlSessionFactory的配置
[code] /** * 配置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()方法,下面我们看一下这个方法
[code] public SqlSessionFactory getObject() throws Exception { //如果sqlSessionFactory为空,则进行初始化 if (this.sqlSessionFactory == null) { this.afterPropertiesSet(); } return this.sqlSessionFactory; }
然后看 afterPropertiesSet()方法
[code]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()方法
[code] 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()方法
[code] 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文件的流程
[code]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 标签的方法,如果有兴趣,其他的方法可自己研究一下
[code] private void buildStatementFromContext(List<XNode> list) { if (this.configuration.getDatabaseId() != null) { this.buildStatementFromContext(list, this.configuration.getDatabaseId()); } this.buildStatementFromContext(list, (String)null); }
继续看里面的buildStatementFromContext()方法
[code] private void buildStatementFromContext(List<XNode> 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()
[code]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标签,如<if>等,则这里返回的是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)
[code]public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
最终就是构建了一个DefaultSqlSessionFactory。
到这里myabtis大体的初始化流程就结束了,其实整体而言myabtis初始化并不复杂,主要是构建Configuration。Configuration里面保存了myabtis所有的配置信息,其中包括 类型解析器,类型别名,resultMap ,拦截器,mapperstatement,还有一些默认的处理器,所以各位有时间需要看一下这个类里面大体的属性。
- Mybatis源码解析之查询流程
- Mybatis源码解析之写流程
- MyBatis源码分析-MyBatis初始化流程
- Mybatis3源码分析(15)-Sql解析执行-Statement初始化和参数设置
- Mybatis的源码与流程解析
- minetest源码解析二:GUIEngine初始化、菜单刷新流程以及核心函数介绍
- Mybatis源码解析-Mybatis初始化过程
- MyBatis源码解析(一)——MyBatis初始化过程解析
- Mybatis源码解析之初始化配置文件封装为Configuration源码详解
- Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段
- SpringMVC源码解析-DispatcherServlet启动流程和初始化
- mybatis源码解析 - 通过一个简单查询例子分析流程
- mybatis源码解析 - 通过一个简单查询例子分析流程
- Beego源码解析(一)——配置项初始化流程
- Mybatis工作机制源码分析—初始化—mapper配置文件解析
- mybatis+spring源码解析(动态代理 spring初始化)
- 【mybatis源码分析】原理分析之三:初始化(配置文件读取和解析)
- mybatis源码解析-----执行流程1
- MyBatis源码分析-MyBatis初始化流程
- MyBatis源码解析(一)——MyBatis初始化过程解析