日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Mybatis源码分析之(三)mapper接口底层原理(为什么不用写方法体就能访问到数据库)

發(fā)布時(shí)間:2025/3/12 数据库 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mybatis源码分析之(三)mapper接口底层原理(为什么不用写方法体就能访问到数据库) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

mybatis是怎么拿sqlSession

在 上一篇的時(shí)候,我們的SqlSessionFactoryBuilder已經(jīng)從xml文件中解析出了Configuration并且返回了sessionFactory。

然后我們要從session;中拿到sqlSession

public class DefaultSqlSessionFactory implements SqlSessionFactory {private final Configuration configuration;@Overridepublic SqlSession openSession() {//默認(rèn)情況下ExecutorType是ExecutorType.SIMPLE類型的return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {//獲取配置的環(huán)境信息final Environment environment = configuration.getEnvironment();//獲取environment中的TransactionFactoryfinal TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);//生成Transactiontx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//生成Executor(重要,之后的查詢都得靠它)final Executor executor = configuration.newExecutor(tx, execType);//返回DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}} }//DefaultSqlSession類的組成,其實(shí)新建的時(shí)候就只是把他的字段賦值而已 public class DefaultSqlSession implements SqlSession {private Configuration configuration;private Executor executor;private boolean autoCommit;private boolean dirty;private List<Cursor<?>> cursorList; }public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {this.configuration = configuration;this.executor = executor;this.dirty = false;this.autoCommit = autoCommit;}

上面分析的是到拿到sqlSession為止,重點(diǎn)其實(shí)不是在上面這里,因?yàn)榈缴厦鏋橹?#xff0c;其實(shí)主要的功能只是將配置的信息解析成我們要的類,然后進(jìn)行初始化賦值。

Mapper的實(shí)現(xiàn)原理

下面我們從SqlSession中拿到mapper,并執(zhí)行方法其實(shí)才是,你感覺到mybatis框架開始幫我們做事的開始。

public static void main(String[] args) {//拿到SqlSessionSqlSession sqlsession = MybatisUtil.getSqlsession();//拿mapperTDemoMapper mapper = sqlsession.getMapper(TDemoMapper.class);//調(diào)用mapper的方法List<TDemo> all = mapper.getAll();for (TDemo item : all)System.out.println(item);}

因?yàn)槲覀冊(cè)陧?xiàng)目中的TDemoMapper只是一個(gè)接口,并沒有實(shí)現(xiàn)這個(gè)接口方法,但是為什么我們?cè)谡{(diào)用這個(gè)接口方法的時(shí)候就可以得到返回結(jié)果呢?mybatis究竟做了什么?

首先我們回到之前解析Mapper的語句

mapperElement(root.evalNode("mappers"));private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {String mapperPackage = child.getStringAttribute("name");configuration.addMappers(mapperPackage);} else {String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if (resource != null && url == null && mapperClass == null) { //根據(jù)resource解析ErrorContext.instance().resource(resource);InputStream inputStream = Resources.getResourceAsStream(resource);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());mapperParser.parse();} else if (resource == null && url != null && mapperClass == null) {//根據(jù)url 解析ErrorContext.instance().resource(url);InputStream inputStream = Resources.getUrlAsStream(url);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());mapperParser.parse();} else if (resource == null && url == null && mapperClass != null) {//根據(jù)mapperClass解析//首先通過mapperClass的路徑,生成mapperClass的接口類Class<?> mapperInterface = Resources.classForName(mapperClass);//降mapperClass加入到configuration中去configuration.addMapper(mapperInterface);} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}}//Configuration類下public <T> void addMapper(Class<T> type) {mapperRegistry.addMapper(type);}//MapperRegistry類下 public class MapperRegistry {private final Configuration config;private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();//最終調(diào)用這個(gè)方法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 {//將接口包裝成MapperProxyFactory類放入knownMappers中(knownMappers就是存放我們的mapper接口的)knownMappers.put(type, new MapperProxyFactory<T>(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.//通過這個(gè)builder來解析mapper的statement。(把mapper和mapper.xml文件相關(guān)聯(lián),方法名與xml中的id相關(guān)聯(lián),為了之后調(diào)用的時(shí)候能找到的語句)MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);//開始解析parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}} }//MapperAnnotationBuilder類中public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {//通過xml文件解析loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();//獲得接口的方法(為了獲取方法上的注解,通過注解的方式來讓方法于sql語句相關(guān)聯(lián))Method[] methods = type.getMethods();for (Method method : methods) {try {// issue #237if (!method.isBridge()) {//具體的解析過程parseStatement(method);}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}

具體的調(diào)用過程就不細(xì)跟了,無非就是獲取節(jié)點(diǎn),獲取屬性值,(或者是獲取方法,然后獲取注解信息),巴拉巴拉……然后設(shè)置到configuration中。
上面要注意的點(diǎn)是,若既配置xml又配置注解的情況下,注解會(huì)覆蓋xml,原因非常簡(jiǎn)單,源碼中注解解析在xml解析后面,然后覆蓋的情況是,他們有相同的namespace+id。
然后我們繼續(xù)我們的主線任務(wù),就是mapper的設(shè)計(jì)架構(gòu)。從上面我們可以知道,configuration中有一個(gè)MapperRegistry類型的字段mapperRegistry,其中有一個(gè)字段叫knownMappers,knownMappers里面存著key為接口類型,值為MapperProxyFactory的。

//我們?cè)谡{(diào)用TDemoMapper mapper = sqlsession.getMapper(TDemoMapper.class);的時(shí)候 //先調(diào)用DefaultSqlSession類下的@Overridepublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);} //然后調(diào)用Configuration類下的public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}//最后調(diào)用MapperRegistry類下的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 {//實(shí)際生成是這段代碼,通過mapperProxyFactory來生成實(shí)例對(duì)象return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}//實(shí)際調(diào)用類 public class MapperProxyFactory<T> {private final Class<T> mapperInterface;private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();public T newInstance(SqlSession sqlSession) {//實(shí)例化一個(gè)代理類final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);//通過這個(gè)函數(shù)實(shí)例化return newInstance(mapperProxy);}protected T newInstance(MapperProxy<T> mapperProxy) {//動(dòng)態(tài)代理的基本操作(說明最終實(shí)現(xiàn)方式是動(dòng)態(tài)代理)return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, 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;}//動(dòng)態(tài)代理中最重要的方法invoke@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {//如果是Object中的方法就不走下面的代理了,直接執(zhí)行(比如toString,hashCode)return method.invoke(this, args);} else if (isDefaultMethod(method)) {//如果不是靜態(tài)方法而且不是抽象方法,則不增強(qiáng)方法return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}//實(shí)際我們的mapper接口的方法走的邏輯就是下面這2條final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);} }

總結(jié)

我們通過sqlSession獲得mapper方法,而sqlSession從configuration中的mapperRegistry中獲取MapperProxyFactory對(duì)象,在通過MapperProxyFactory對(duì)象的newInstance方法得到MapperProxy的動(dòng)態(tài)代理實(shí)例對(duì)象。

我們使用的mapper其實(shí)是通過MapperProxy動(dòng)態(tài)代理,在運(yùn)行時(shí)候生成的一個(gè)新的對(duì)象進(jìn)行方法增強(qiáng)的,里面的接口方法都會(huì)通過下面2個(gè)語句進(jìn)行數(shù)據(jù)庫的操作,以及后續(xù)對(duì)數(shù)據(jù)的處理

final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);12

這兩條語句其實(shí)包含對(duì)訪問數(shù)據(jù)庫對(duì)象的創(chuàng)建,訪問數(shù)據(jù)庫到得到數(shù)據(jù)庫返回?cái)?shù)據(jù)后的處理等內(nèi)容,非常復(fù)雜,本篇就到此為止。

總結(jié)

以上是生活随笔為你收集整理的Mybatis源码分析之(三)mapper接口底层原理(为什么不用写方法体就能访问到数据库)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。