Mybatis源码解析
2021/6/6 14:23:04
本文主要是介绍Mybatis源码解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Mybatis源码解析
首先是Mybatis的基本使用步骤
public class StudentMapperTest { private static SqlSessionFactory sqlSessionFactory; @BeforeClass public static void init() { try { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); //内部实际创建了一个Configuration对象,并以此创建出DefaultSqlSessionFactory对象 sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (IOException e) { e.printStackTrace(); } } @Test public void testSelectList() { SqlSession sqlSession = null; try { sqlSession = sqlSessionFactory.openSession(); //这里实际上是通过executor对象调用方法 List<Student> students = sqlSession.selectList("com.demo.mapper.UserMapper.getUserByName"); //这里实际上是通过this.configuration.getMapper(type, this);来获取代理对象的。这个代理对象内部是通过sqlSession执行sql。 //而sqlSession内部实际上都是通过executor执行的。而一级缓存又在executor中所以,所有通过同一个sqlSession执行的方法共享一级缓存。 //sqlSessionFactory.openSession()如果再创建一个sqlSession,实际上内部的executor也是新创建的所以就不会与先前那个sqlSession共享一级缓存了。 Mapper mapper = sqlSession.getMapper(mapper.class); for (int i = 0; i < students.size(); i++) { System.out.println(students.get(i)); } } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } }
这里是具体的配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- 通过这个配置文件完成mybatis与数据库的连接 --> <configuration> <!-- 引入数据源配置 database.properties 文件 --> <properties resource="database.properties"></properties> <!--配置mybatis 运行中的一些行为 --> <settings> <!-- 设置Mybatis的log实现为LOG4J --> <setting name="logImpl" value="LOG4J"/> </settings> <typeAliases> <!-- <typeAlias alias="User" type="com.zy.entity.User"/> --> <package name="cn.zy.entity"/> </typeAliases> <!-- 配置mybatis运行环境 --> <environments default="dev"> <environment id="dev"> <!-- 采用jdbc事务管理 --> <transactionManager type="JDBC"></transactionManager> <!-- 采用Mybatis自带的数据源 POOLED --> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!-- 将mapper文件加入到配置文件中 --> <mappers> <mapper resource="cn/zy/dao/UserMapper.xml"/> <package name="com.hytc.mapper" /> <package url="....." /> <package class="接口路径"/> </mappers> </configuration>
先放上通过sqlSession执行sql语句的过程这里是引用Coder648的博客
//此方法在SimpleExecutor的父类BaseExecutor中实现 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示 BoundSql boundSql = ms.getBoundSql(parameter); //为本次查询创建缓存的Key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } //进入query的重载方法中 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { // 如果缓存中没有本次查找的值,那么从数据库中查询 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; } //从数据库查询 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { // 查询的方法 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } // 将查询结果放入缓存 localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; } // SimpleExecutor中实现父类的doQuery抽象方法 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 传入参数创建StatementHanlder对象来执行查询 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 创建jdbc中的statement对象 stmt = prepareStatement(handler, ms.getStatementLog()); // StatementHandler进行处理 return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } // 创建Statement的方法 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; //条代码中的getConnection方法经过重重调用最后会调用openConnection方法,从连接池中获得连接。 Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; } //从连接池获得连接的方法 protected void openConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("Opening JDBC Connection"); } //从连接池获得连接 connection = dataSource.getConnection(); if (level != null) { connection.setTransactionIsolation(level.getLevel()); } setDesiredAutoCommit(autoCommit); } //进入StatementHandler进行处理的query,StatementHandler中默认的是PreparedStatementHandler public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; //原生jdbc的执行 ps.execute(); //处理结果返回。 return resultSetHandler.handleResultSets(ps); }
我们来根据基本使用来看一下源码
第一步
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
获取mybatis配置文件的字节流对象
第二步
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
获取sqlSessionFactory对象,实际上这是一个接口,我们获取到的对象实际上是该接口的实现类DefaultSqlSessionFactory。
SqlSessionFactoryBuilder有关源码如下
build方法的参数
1、properties 实际上这是一个hashTable,存放了外部数据库连接池的位置信息。可以当参数传入,也可以在配置文件配置,因为最终将二者存放在一个default properties 中。作用是给数据库连接池提供配置数据(url username password)
2、reader读取mybatis配置文件的文件流对象 这是主要的
3、environment,这个参数的作用是当mybatis配置了多个数据源时,指定使用哪一个。
public class SqlSessionFactoryBuilder { public SqlSessionFactoryBuilder() { } //从这可以看出build大体分为两类,主要是字符流,以及字符流。 public SqlSessionFactory build(Reader reader) { return this.build((Reader)reader, (String)null, (Properties)null); } public SqlSessionFactory build(Reader reader, String environment) { return this.build((Reader)reader, environment, (Properties)null); } public SqlSessionFactory build(Reader reader, Properties properties) { return this.build((Reader)reader, (String)null, properties); } public SqlSessionFactory build(Reader reader, String environment, Properties properties) { SqlSessionFactory var5; try { //parser创建出Configuration,并根据配置文件对其初始化 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); //var5就是调用build最终返回的对象(看最下面的方法),也就是DefaultSqlSessionFactory var5 = this.build(parser.parse()传入一个Configuration); } ....省略若干 return var5; } public SqlSessionFactory build(InputStream inputStream) { return this.build((InputStream)inputStream, (String)null, (Properties)null); } public SqlSessionFactory build(InputStream inputStream, String environment) { return this.build((InputStream)inputStream, environment, (Properties)null); } public SqlSessionFactory build(InputStream inputStream, Properties properties) { return this.build((InputStream)inputStream, (String)null, properties); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build(parser.parse()); } ....省略若干 return var5; } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
接下来是XMLConfigBuilder的源码,省略若干
public class XMLConfigBuilder extends BaseBuilder { private boolean parsed; private final XPathParser parser; private String environment; private final ReflectorFactory localReflectorFactory; public XMLConfigBuilder(Reader reader) { this((Reader)reader, (String)null, (Properties)null); } public XMLConfigBuilder(Reader reader, String environment) { this((Reader)reader, environment, (Properties)null); } public XMLConfigBuilder(Reader reader, String environment, Properties props) { //XPathParser是用来解析xml配置文件的 this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } public XMLConfigBuilder(InputStream inputStream) { this((InputStream)inputStream, (String)null, (Properties)null); } public XMLConfigBuilder(InputStream inputStream, String environment) { this((InputStream)inputStream, environment, (Properties)null); } public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { //首先创建了Configuration对象。并传给了它的父类,同时也是parse返回的对象 super(new Configuration()); this.localReflectorFactory = new DefaultReflectorFactory(); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; } public Configuration parse() { //这里的parsed是全局变量,不允许对configuration进行多次初始化 if (this.parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } else { this.parsed = true; //这个方法实际上是xml配置文件每个标签对解析成以一个节点对象,并对configuration进行初始化 this.parseConfiguration(this.parser.evalNode("/configuration")); //最终返回从父类继承过来的configuration对象 return this.configuration; } } //这里是具体的方法,下列的方法中大部分通过这种形式this.configuration.setVfsImpl(vfsImpl)给configuration初始化。 private void parseConfiguration(XNode root) { try { this.propertiesElement(root.evalNode("properties")); Properties settings = this.settingsAsProperties(root.evalNode("settings")); this.loadCustomVfs(settings); this.loadCustomLogImpl(settings); this.typeAliasesElement(root.evalNode("typeAliases")); this.pluginElement(root.evalNode("plugins")); this.objectFactoryElement(root.evalNode("objectFactory")); this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.reflectorFactoryElement(root.evalNode("reflectorFactory")); this.settingsElement(settings); this.environmentsElement(root.evalNode("environments")); this.databaseIdProviderElement(root.evalNode("databaseIdProvider")); this.typeHandlerElement(root.evalNode("typeHandlers")); this.mapperElement(root.evalNode("mappers")); } catch (Exception var3) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); } } private void loadCustomVfs(Properties props) { this.configuration.setVfsImpl(vfsImpl); } private void loadCustomLogImpl(Properties props) { this.configuration.setLogImpl(logImpl); } private void typeAliasesElement(XNode parent) { this.configuration.getTypeAliasRegistry().registerAliases(alias); } private void pluginElement(XNode parent) throws Exception { this.configuration.addInterceptor(interceptorInstance); } private void objectFactoryElement(XNode context) throws Exception { this.configuration.setObjectFactory(factory) } private void objectWrapperFactoryElement(XNode context) throws Exception { this.configuration.setObjectWrapperFactory(factory); } private void reflectorFactoryElement(XNode context) throws Exception { this.configuration.setReflectorFactory(factory); } private void propertiesElement(XNode context) throws Exception { if (context != null) { Properties defaults = context.getChildrenAsProperties(); String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { //这一步是解析资源,返回一个propertise对象,并放进defaults defaults.putAll(Resources.getUrlAsProperties(url)); } //这里是获取build传入的Properties,因为前面设置了this.configuration.setVariables(props); Properties vars = this.configuration.getVariables(); //如果不为空就合并。 if (vars != null) { defaults.putAll(vars); } //设置的作用是提供将${user}替换成真实值的数据。 this.parser.setVariables(defaults); //将最终合并后Properties给configuration赋值。注意Properties实际上是一个hashTable存放的连接数据库的配置 this.configuration.setVariables(defaults); } } private void settingsElement(Properties props) { this.configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); this.configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); //开起二级缓存的配置 this.configuration.setCacheEnabled(this.booleanValueOf(props.getProperty("cacheEnabled"), true)); this.configuration.setProxyFactory((ProxyFactory)this.createInstance(props.getProperty("proxyFactory"))); this.configuration.setLazyLoadingEnabled(this.booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); this.configuration.setAggressiveLazyLoading(this.booleanValueOf(props.getProperty("aggressiveLazyLoading"), false)); this.configuration.setMultipleResultSetsEnabled(this.booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); this.configuration.setUseColumnLabel(this.booleanValueOf(props.getProperty("useColumnLabel"), true)); this.configuration.setUseGeneratedKeys(this.booleanValueOf(props.getProperty("useGeneratedKeys"), false)); this.configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); this.configuration.setDefaultStatementTimeout(this.integerValueOf(props.getProperty("defaultStatementTimeout"), (Integer)null)); this.configuration.setDefaultFetchSize(this.integerValueOf(props.getProperty("defaultFetchSize"), (Integer)null)); this.configuration.setDefaultResultSetType(this.resolveResultSetType(props.getProperty("defaultResultSetType"))); this.configuration.setMapUnderscoreToCamelCase(this.booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); this.configuration.setSafeRowBoundsEnabled(this.booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); this.configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); this.configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); this.configuration.setLazyLoadTriggerMethods(this.stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); this.configuration.setSafeResultHandlerEnabled(this.booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); this.configuration.setDefaultScriptingLanguage(this.resolveClass(props.getProperty("defaultScriptingLanguage"))); this.configuration.setDefaultEnumTypeHandler(this.resolveClass(props.getProperty("defaultEnumTypeHandler"))); this.configuration.setCallSettersOnNulls(this.booleanValueOf(props.getProperty("callSettersOnNulls"), false)); this.configuration.setUseActualParamName(this.booleanValueOf(props.getProperty("useActualParamName"), true)); this.configuration.setReturnInstanceForEmptyRow(this.booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); this.configuration.setLogPrefix(props.getProperty("logPrefix")); this.configuration.setConfigurationFactory(this.resolveClass(props.getProperty("configurationFactory"))); } private void environmentsElement(XNode context) throws Exception { if (context != null) { //这里就是判断builed传过来的String environment参数了,如果不传使用默认数据源。当然这个默认的也是配置文件中指定的 if (this.environment == null) { this.environment = context.getStringAttribute("default"); } Iterator var2 = context.getChildren().iterator(); //这里是遍历所有的数据源,找到与environment相等名字的数据源 while(var2.hasNext()) { XNode child = (XNode)var2.next(); String id = child.getStringAttribute("id"); if (this.isSpecifiedEnvironment(id)) { TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager")); //创建数据库连接池工厂对象。 DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); //级联调用,实际上是返回键id,dataSource,txFactory封装在一起的一个对象 Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource); //environmentBuilder.build()方法将上述三个参数作为environment的有参构造参数创建environment对象,environment其实也是对这三个参数的一个封装 this.configuration.setEnvironment(environmentBuilder.build()); } } } } //创建连接池工厂 private DataSourceFactory dataSourceElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); //这一步获取连接数据库的信息等等 Properties props = context.getChildrenAsProperties(); DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).newInstance(); factory.setProperties(props); return factory; } else { throw new BuilderException("Environment declaration requires a DataSourceFactory."); } } private void databaseIdProviderElement(XNode context) { this.configuration.setDatabaseId(databaseId); } private void mapperElement(XNode parent) throws Exception { if (parent != null) { Iterator var2 = parent.getChildren().iterator(); while(true) { while(var2.hasNext()) { XNode child = (XNode)var2.next(); String resource; //在这mappers分为两种,package分一种,其他url class resource分一种 if ("package".equals(child.getName())) { resource = child.getStringAttribute("name"); this.configuration.addMappers(resource); } else { resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); XMLMapperBuilder mapperParser; InputStream inputStream; //从这可以看出对于每一个 mapper url class resource只能选择一种 if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); inputStream = Resources.getResourceAsStream(resource); //这里是对mapper.xml进行解析同解析mybatis配置文件类似 mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); inputStream = Resources.getUrlAsStream(url); mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments()); mapperParser.parse(); } else { if (resource != null || url != null || mapperClass == null) { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } //无论是什么方式最终都会将mapperInterface通过addMapper创建实现类对象,上面的也会调用this.configuration.addMapper()方法,存进configuration的属性mapperRegistry的一个map集合中Map<Class<?>, MapperProxyFactory<?>>集合中。而这个集合中的value是mapper的代理工厂,用来创建mapper的代理对象。在这里只是创建工厂,当调用getMapper是代理对象才被创建出来 Class<?> mapperInterface = Resources.classForName(mapperClass); this.configuration.addMapper(mapperInterface); } } } return; } } } }
这里是对mapper操作的方法
public <T> void addMapper(Class<T> type) { this.mapperRegistry.addMapper(type); } //这个MapperRegistry也是Configuration的一个成员变量 public class MapperRegistry { private final Configuration config; //this.mapperRegistry.addMapper(type)就是存放进knownMappers这个map中 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap(); public MapperRegistry(Configuration config) { this.config = config; } //这个getMapper是返回一个Mapper的代理对象 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //从knownMappers中获取,它的值是调用addMapper方法赋予的。而这个方法的调用是Configuration初始化时执行的。 MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } else { try { //详细查看这个方法,这个方法创建了mapper代理对象 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception var5) { throw new BindingException("Error getting mapper instance. Cause: " + var5, var5); } } } public <T> boolean hasMapper(Class<T> type) { return this.knownMappers.containsKey(type); } public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (this.hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { this.knownMappers.put(type, new MapperProxyFactory(type)); MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { this.knownMappers.remove(type); } } } } public Collection<Class<?>> getMappers() { return Collections.unmodifiableCollection(this.knownMappers.keySet()); } //扫描包中的class加入knownMappers map集合中 public void addMappers(String packageName, Class<?> superType) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil(); //这个函数是扫描包中所有的class并且将类型为new IsA(superType)加进mapperSet中,就是下面返回的set集合 resolverUtil.find(new IsA(superType), packageName); Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses(); Iterator var5 = mapperSet.iterator(); while(var5.hasNext()) { Class<?> mapperClass = (Class)var5.next(); this.addMapper(mapperClass); } } public void addMappers(String packageName) { this.addMappers(packageName, Object.class); } }
mapperProxyFactory
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return this.mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return this.methodCache; } protected T newInstance(MapperProxy<T> mapperProxy) { return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy); } //为什么要传入sqlsession呢?实际上通过mapper代理对象执行的sql语句实际上是通过sqlsession来执行的, public T newInstance(SqlSession sqlSession) { MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache); return this.newInstance(mapperProxy); } }
MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //直接放行Object定义的方法 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } //直接放行默认方法。默认方法是接口中定义一个已实现方法,且该接口的实现类不需要实现该方法。为什么要有默认方法。因为如果没有默认方法,加入给JDK中的某个接口添加一个新的抽象方法,那么所有实现了该接口的类都得修改,影响将非常大。 if (method.isDefault()) { return this.invokeDefaultMethod(proxy, method, args); } } catch (Throwable var5) { throw ExceptionUtil.unwrapThrowable(var5); } //创建出mapperMethod对象,这个对象内部通过调用sqlSession执行sql语句,同时MapperMethod内内有一个属性command,这个command对象封装了一个内部通过从configuration对象中的mappedStatements.get(mapperInterface.getName() + "." + methodName)获取mappedStatement。 //然后获取mappedStatement的id="com.demo.mapper.UserMapper.getUserByName"赋值给command。用来执行sqlSession.方法("com.demo.mapper.UserMapper.getUserByName",params) MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> { return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()); }); } private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE); if (!constructor.isAccessible()) { constructor.setAccessible(true); } Class<?> declaringClass = method.getDeclaringClass(); return ((Lookup)constructor.newInstance(declaringClass, 15)).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); } }
以上都是通过读取mybatis配置文件创建Configuration对象,并为Configuration对象初始化的一个过程,主要发生在XMLConfigBuilder方法中,重点是通过读取配置信息创建了environment对象,这个对象封装了我们指定的数据库连接池对象,事务工厂对象,id。将mapper代理工厂对象存储在 MapperRegistry的knownMappers集合中
创建出Configuration对象后通过这个对象创建出new DefaultSqlSessionFactory(config)对象,再获取SqlSession对象,注意SqlSession中是封装了Configuration对象的
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) { DefaultSqlSession var8; try { boolean autoCommit; try { autoCommit = connection.getAutoCommit(); } catch (SQLException var13) { autoCommit = true; } Environment environment = this.configuration.getEnvironment(); TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment); Transaction tx = transactionFactory.newTransaction(connection); //configuration.newExecutor源码如下,配合下面一张图 Executor executor = this.configuration.newExecutor(tx, execType); var8 = new DefaultSqlSession(this.configuration, executor, autoCommit); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error opening session. Cause: " + var14, var14); } finally { ErrorContext.instance().reset(); } return var8; } private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) { return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory()); }
这里是Executor的继承体系
最底层的接口是Executor,有两个实现类:BaseExecutor和CachingExecutor,CachingExecutor用于二级缓存,而BaseExecutor则用于一级缓存及基础的操作。
1、SimpleExecutor是最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;
2、BatchExecutor执行器,顾名思义,通过批量操作来优化性能。通常需要注意的是批量更新操作,由于内部有缓存的实现,使用完成后记得调用flushStatements来清除缓存。
3、ReuseExecutor 可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该sqlsession中有效,所以使用完成后记得调用flushStatements来清除Map。
public class Configuration { //成员变量,通过读取配置文件进行初始化,是否开启二级缓存 protected boolean cacheEnabled; //mapper中的每一条sql语句都会被封装成一个MappedStatement对象key = 全限定类名 + 方法名,value = 对应的MappedStatement对象 protected final Map<String, MappedStatement> mappedStatements; //内部存放了mapper代理工厂对象,以及通过getMapper方法创建出代理对象并返回 protected final MapperRegistry mapperRegistry; //这个对象主要封装了数据库连接池对象,事务对象,id protected Environment environment; //这个主要是连接数据库的信息,如user=root password=1234 等实际上是一个hashTable protected Properties variables; public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? this.defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Object executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (this.cacheEnabled) { executor = new CachingExecutor((Executor)executor); } Executor executor = (Executor)this.interceptorChain.pluginAll(executor); return executor; } }
先来看看BaseExecutor的源码
public abstract class BaseExecutor implements Executor { //这个就是一级缓存,内部封装了一个hashMap private Map<Object, Object> cache = new HashMap();而BaseExecutor又是sqlsession的一个属性所以说一级缓存的范围是sqlsession protected PerpetualCache localCache; //这个query的调用是通过sqlsession对象来调用的,当通过sqlsession调用方法时内部就调用了BaseExecutor的方法来执行sql语句 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (this.closed) { throw new ExecutorException("Executor was closed."); } else { if (this.queryStack == 0 && ms.isFlushCacheRequired()) { this.clearLocalCache(); } List list; try { ++this.queryStack; //先判断缓存中有没有 list = resultHandler == null ? (List)this.localCache.getObject(key) : null; if (list != null) { //注意这个key值是由CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);这几个参数共同影响的 this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { //这里是数据库中真正的查询。 list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { --this.queryStack; } if (this.queryStack == 0) { Iterator var8 = this.deferredLoads.iterator(); while(var8.hasNext()) { BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next(); deferredLoad.load(); } this.deferredLoads.clear(); if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { this.clearLocalCache(); } } return list; } } }
下面来看看DefaultSqlSession的源码
public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; private final boolean autoCommit; //从这看出通过Sqlsession执行sql语句实际上是通过executor.query来执行的。 public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = this.configuration.getMappedStatement(statement); this.executor.query(ms, this.wrapCollection(parameter), rowBounds, handler); } catch (Exception var9) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); } finally { ErrorContext.instance().reset(); } } }
关于二级缓存,注意二级缓存并不是只有一个,对于每一个MappedStatement有一个cache属性就是该sql语句对应的二级缓冲区。而每个MappedStatement在初始化Configuration时创建出来的。然后把cache作为key值通过获取value=TransactionalCache,TransactionalCache内部有个临时二级缓存,把cache传进去内部有方法flushPendingEntries将临时与cache之间进行数据赋值。
public class CachingExecutor implements Executor { private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager(); //这里传入的Executor是上文创建的三种Executor之一 public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } public Transaction getTransaction() { return this.delegate.getTransaction(); } public void close(boolean forceRollback) { try { if (forceRollback) { this.tcm.rollback(); } else { this.tcm.commit(); } } finally { this.delegate.close(forceRollback); } } public boolean isClosed() { return this.delegate.isClosed(); } public int update(MappedStatement ms, Object parameterObject) throws SQLException { //注意这一点,如果再mybatis配置中配置了flushCache属性,那么再update时会清空临时缓冲区。否则只清理真正的二级缓冲区 this.flushCacheIfRequired(ms); return this.delegate.update(ms, parameterObject); } public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql); return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException { this.flushCacheIfRequired(ms); return this.delegate.queryCursor(ms, parameter, rowBounds); } public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //重点是这个真正存放二级缓存的地方,这个是每一个MappedStatement共用一个。所以二级缓存才能在不同sqlSession共享数据 Cache cache = ms.getCache(); if (cache != null) { this.flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { this.ensureNoOutParams(ms, boundSql); //先从二级缓存中取,如果map中不存在cache为key的value 那么就将cache作为参数创建TransactionalCache对象,并通过key从cache中获取数据。 List<E> list = (List)this.tcm.getObject(cache, key); if (list == null) { //这里是走一级缓存读取数据库的路子 list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //这里是将读取的数据放进临时二级缓存只有执行commit之后才会将临时二级缓存中的数据放进MappedStatement中的cache中 this.tcm.putObject(cache, key, list); } return list; } } return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } public void commit(boolean required) throws SQLException { this.delegate.commit(required); //实际上调用了TransactionalCache,中的commit方法 //这里的commit会将临时二级缓冲区中存进真正的二级缓冲区,并清空临时二级缓冲区,如果在调用commit之前调用了clear那么会先清理掉二级缓冲区再将临时缓冲区存进二级缓冲区 this.tcm.commit(); } }
TransactionalCache类中的方法
public void clear() { this.clearOnCommit = true; this.entriesToAddOnCommit.clear(); } public void commit() { if (this.clearOnCommit) { this.delegate.clear(); } this.flushPendingEntries(); this.reset(); } public void rollback() { this.unlockMissedEntries(); this.reset(); } private void reset() { this.clearOnCommit = false; this.entriesToAddOnCommit.clear(); this.entriesMissedInCache.clear(); } private void flushPendingEntries() { Iterator var1 = this.entriesToAddOnCommit.entrySet().iterator(); while(var1.hasNext()) { Entry<Object, Object> entry = (Entry)var1.next(); this.delegate.putObject(entry.getKey(), entry.getValue()); } var1 = this.entriesMissedInCache.iterator(); while(var1.hasNext()) { Object entry = var1.next(); if (!this.entriesToAddOnCommit.containsKey(entry)) { this.delegate.putObject(entry, (Object)null); } } }
主要的过程就是这样了。我自己觉得思路写的不是很清晰,如果有错误请大家指出我会改正。写这个文章的目的主要是为了记笔记,同时也希望能够帮助别人。
这篇关于Mybatis源码解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27MQ底层原理资料详解:新手入门教程
- 2024-11-27MQ项目开发资料入门教程
- 2024-11-27RocketMQ源码资料详解:新手入门教程
- 2024-11-27本地多文件上传简易教程
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器
- 2024-11-26Java云原生资料:新手入门教程与实战指南
- 2024-11-26JAVA云原生资料入门教程