Mybaits 源码解析 (十一)----- 设计模式精妙使用:静态代理和动态代理结合使用:@MapperScan将Mapper接口生成代理注入到Spring
上一篇文章我们讲了sqlsessionfactorybean,通过这个factorybean创建sqlsessionfactory并注册进spring容器,这篇文章我们就讲剩下的部分,通过mapperscannerconfigurer将mapper接口生成代理注入到spring
扫描mapper接口
我们上一篇文章介绍了扫描mapper接口有两种方式,一种是通过bean.xml注册mapperscannerconfigurer对象,一种是通过@mapperscan("com.chenhao.mapper")注解的方式,如下
方式一:
<bean class="org.mybatis.spring.mapper.mapperscannerconfigurer"> <property name="basepackage" value="com.chenhao.mapper" /> </bean>
方式二:
@configuration @mapperscan("com.chenhao.mapper") public class appconfig {
@mapperscan
我们来看看@mapperscan这个注解
@import(mapperscannerregistrar.class) public @interface mapperscan {
mapperscan使用@import
将mapperscannerregistrar
导入。
mapperscannerregistrar
1 public class mapperscannerregistrar implements importbeandefinitionregistrar, resourceloaderaware { 2 private resourceloader resourceloader; 3 @override 4 public void registerbeandefinitions(annotationmetadata importingclassmetadata, beandefinitionregistry registry) { 5 // 获取mapperscan 注解,如@mapperscan("com.chenhao.mapper") 6 annotationattributes annoattrs = annotationattributes.frommap(importingclassmetadata.getannotationattributes(mapperscan.class.getname())); 7 // 创建路径扫描器,下面的一大段都是将mapperscan 中的属性设置到classpathmapperscanner ,做的就是一个set操作 8 classpathmapperscanner scanner = new classpathmapperscanner(registry); 9 // this check is needed in spring 3.1 10 if (resourceloader != null) { 11 // 设置资源加载器,作用:扫描指定包下的class文件。 12 scanner.setresourceloader(resourceloader); 13 } 14 class<? extends annotation> annotationclass = annoattrs.getclass("annotationclass"); 15 if (!annotation.class.equals(annotationclass)) { 16 scanner.setannotationclass(annotationclass); 17 } 18 class<?> markerinterface = annoattrs.getclass("markerinterface"); 19 if (!class.class.equals(markerinterface)) { 20 scanner.setmarkerinterface(markerinterface); 21 } 22 class<? extends beannamegenerator> generatorclass = annoattrs.getclass("namegenerator"); 23 if (!beannamegenerator.class.equals(generatorclass)) { 24 scanner.setbeannamegenerator(beanutils.instantiateclass(generatorclass)); 25 } 26 class<? extends mapperfactorybean> mapperfactorybeanclass = annoattrs.getclass("factorybean"); 27 if (!mapperfactorybean.class.equals(mapperfactorybeanclass)) { 28 scanner.setmapperfactorybean(beanutils.instantiateclass(mapperfactorybeanclass)); 29 } 30 scanner.setsqlsessiontemplatebeanname(annoattrs.getstring("sqlsessiontemplateref")); 31 //设置sqlsessionfactory的名称 32 scanner.setsqlsessionfactorybeanname(annoattrs.getstring("sqlsessionfactoryref")); 33 list<string> basepackages = new arraylist<string>(); 34 //获取配置的包路径,如com.chenhao.mapper 35 for (string pkg : annoattrs.getstringarray("value")) { 36 if (stringutils.hastext(pkg)) { 37 basepackages.add(pkg); 38 } 39 } 40 for (string pkg : annoattrs.getstringarray("basepackages")) { 41 if (stringutils.hastext(pkg)) { 42 basepackages.add(pkg); 43 } 44 } 45 for (class<?> clazz : annoattrs.getclassarray("basepackageclasses")) { 46 basepackages.add(classutils.getpackagename(clazz)); 47 } 48 // 注册过滤器,作用:什么类型的mapper将会留下来。 49 scanner.registerfilters(); 50 // 扫描指定包 51 scanner.doscan(stringutils.tostringarray(basepackages)); 52 } 53 }
classpathmapperscanner
接着我们来看看扫描过程 scanner.doscan(stringutils.tostringarray(basepackages));
1 @override 2 public set<beandefinitionholder> doscan(string... basepackages) { 3 //调用父类进行扫描,并将basepackages下的class都封装成beandefinitionholder,并注册进spring容器的beandefinition 4 set<beandefinitionholder> beandefinitions = super.doscan(basepackages); 5 6 if (beandefinitions.isempty()) { 7 logger.warn("no mybatis mapper was found in '" + arrays.tostring(basepackages) + "' package. please check your configuration."); 8 } else { 9 //继续对beandefinitions做处理,额外设置一些属性 10 processbeandefinitions(beandefinitions); 11 } 12 13 return beandefinitions; 14 } 15 16 protected set<beandefinitionholder> doscan(string... basepackages) { 17 assert.notempty(basepackages, "at least one base package must be specified"); 18 set<beandefinitionholder> beandefinitions = new linkedhashset<beandefinitionholder>(); 19 //遍历basepackages进行扫描 20 for (string basepackage : basepackages) { 21 //找出匹配的类 22 set<beandefinition> candidates = findcandidatecomponents(basepackage); 23 for (beandefinition candidate : candidates) { 24 scopemetadata scopemetadata = this.scopemetadataresolver.resolvescopemetadata(candidate); 25 candidate.setscope(scopemetadata.getscopename()); 26 string beanname = this.beannamegenerator.generatebeanname(candidate, this.registry); 27 if (candidate instanceof abstractbeandefinition) { 28 postprocessbeandefinition((abstractbeandefinition) candidate, beanname); 29 } 30 if (candidate instanceof annotatedbeandefinition) { 31 annotationconfigutils.processcommondefinitionannotations((annotatedbeandefinition) candidate); 32 } 33 if (checkcandidate(beanname, candidate)) { 34 //封装成beandefinitionholder 对象 35 beandefinitionholder definitionholder = new beandefinitionholder(candidate, beanname); 36 definitionholder = annotationconfigutils.applyscopedproxymode(scopemetadata, definitionholder, this.registry); 37 beandefinitions.add(definitionholder); 38 //将beandefinition对象注入spring的beandefinitionmap中,后续getbean时,就是从beandefinitionmap获取对应的beandefinition对象,取出其属性进行实例化bean 39 registerbeandefinition(definitionholder, this.registry); 40 } 41 } 42 } 43 return beandefinitions; 44 }
我们重点看下第4行和第10行代码,第4行是调用父类的doscan方法,获取basepackages下的所有class,并将其生成beandefinition,注入spring的beandefinitionmap中,也就是class的描述类,在调用getbean方法时,获取beandefinition进行实例化。此时,所有的mapper接口已经被生成了beandefinition。接着我们看下第10行,对生成的beandefinition做一些额外的处理。
processbeandefinitions
上面beandefinition已经注入进spring容器了,接着我们看对beandefinition进行哪些额外的处理
1 private void processbeandefinitions(set<beandefinitionholder> beandefinitions) { 2 genericbeandefinition definition; 3 for (beandefinitionholder holder : beandefinitions) { 4 definition = (genericbeandefinition) holder.getbeandefinition(); 5 6 if (logger.isdebugenabled()) { 7 logger.debug("creating mapperfactorybean with name '" + holder.getbeanname() 8 + "' and '" + definition.getbeanclassname() + "' mapperinterface"); 9 } 10 11 // 设置definition构造器的输入参数为definition.getbeanclassname(),这里就是com.chenhao.mapper.usermapper 12 // 那么在getbean实例化时,通过反射调用构造器实例化时要将这个参数传进去 13 definition.getconstructorargumentvalues().addgenericargumentvalue(definition.getbeanclassname()) 14 // 修改definition对应的class 15 // 看过spring源码的都知道,getbean()返回的就是beandefinitionholder中beanclass属性对应的实例 16 // 所以我们后面ac.getbean(usermapper.class)的返回值也就是mapperfactorybean的实例 17 // 但是最终被注入到spring容器的对象的并不是mapperfactorybean的实例,根据名字看,我们就知道mapperfactorybean实现了factorybean接口 18 // 那么最终注入spring容器的必定是从mapperfactorybean的实例的getobject()方法中返回 19 definition.setbeanclass(this.mapperfactorybean.getclass()); 20 21 definition.getpropertyvalues().add("addtoconfig", this.addtoconfig); 22 23 boolean explicitfactoryused = false; 24 if (stringutils.hastext(this.sqlsessionfactorybeanname)) { 25 definition.getpropertyvalues().add("sqlsessionfactory", new runtimebeanreference(this.sqlsessionfactorybeanname)); 26 explicitfactoryused = true; 27 } else if (this.sqlsessionfactory != null) { 28 //往definition设置属性值sqlsessionfactory,那么在mapperfactorybean实例化后,进行属性赋值时populatebean(beanname, mbd, instancewrapper);,会通过反射调用sqlsessionfactory的set方法进行赋值 29 //也就是在mapperfactorybean创建实例后,要调用setsqlsessionfactory方法将sqlsessionfactory注入进mapperfactorybean实例 30 definition.getpropertyvalues().add("sqlsessionfactory", this.sqlsessionfactory); 31 explicitfactoryused = true; 32 } 33 34 if (stringutils.hastext(this.sqlsessiontemplatebeanname)) { 35 if (explicitfactoryused) { 36 logger.warn("cannot use both: sqlsessiontemplate and sqlsessionfactory together. sqlsessionfactory is ignored."); 37 } 38 definition.getpropertyvalues().add("sqlsessiontemplate", new runtimebeanreference(this.sqlsessiontemplatebeanname)); 39 explicitfactoryused = true; 40 } else if (this.sqlsessiontemplate != null) { 41 if (explicitfactoryused) { 42 logger.warn("cannot use both: sqlsessiontemplate and sqlsessionfactory together. sqlsessionfactory is ignored."); 43 } 44 definition.getpropertyvalues().add("sqlsessiontemplate", this.sqlsessiontemplate); 45 explicitfactoryused = true; 46 } 47 48 if (!explicitfactoryused) { 49 if (logger.isdebugenabled()) { 50 logger.debug("enabling autowire by type for mapperfactorybean with name '" + holder.getbeanname() + "'."); 51 } 52 definition.setautowiremode(abstractbeandefinition.autowire_by_type); 53 } 54 } 55 }
看第19行,将definition的beanclass属性设置为mapperfactorybean.class,我们知道,在getbean的时候,会通过反射创建bean的实例,也就是创建beanclass的实例,如下spring的getbean的部分代码:
public object instantiate(rootbeandefinition bd, @nullable string beanname, beanfactory owner) { // 没有覆盖 // 直接使用反射实例化即可 if (!bd.hasmethodoverrides()) { // 重新检测获取下构造函数 // 该构造函数是经过前面 n 多复杂过程确认的构造函数 constructor<?> constructortouse; synchronized (bd.constructorargumentlock) { // 获取已经解析的构造函数 constructortouse = (constructor<?>) bd.resolvedconstructororfactorymethod; // 如果为 null,从 class 中解析获取,并设置 if (constructortouse == null) { final class<?> clazz = bd.getbeanclass(); if (clazz.isinterface()) { throw new beaninstantiationexception(clazz, "specified class is an interface"); } try { if (system.getsecuritymanager() != null) { constructortouse = accesscontroller.doprivileged( (privilegedexceptionaction<constructor<?>>) clazz::getdeclaredconstructor); } else { //利用反射获取构造器 constructortouse = clazz.getdeclaredconstructor(); } bd.resolvedconstructororfactorymethod = constructortouse; } catch (throwable ex) { throw new beaninstantiationexception(clazz, "no default constructor found", ex); } } } // 通过beanutils直接使用构造器对象实例化bean return beanutils.instantiateclass(constructortouse); } else { // 生成cglib创建的子类对象 return instantiatewithmethodinjection(bd, beanname, owner); } }
看到没,是通过bd.getbeanclass();从beandefinition中获取beanclass属性,然后通过反射实例化bean,如上,所有的mapper接口扫描封装成的beandefinition的beanclass都设置成了mapperfactorybean,我们知道在spring初始化的最后,会获取所有的beandefinition,并通过getbean创建所有的实例注入进spring容器,那么意思就是说,在getbean时,创建的的所有mapper对象是mapperfactorybean这个对象了。
我们看下第13行处,设置了beandefinition构造器参数,那么当getbean中通过构造器创建实例时,需要将设置的构造器参数definition.getbeanclassname(),这里就是com.chenhao.mapper.usermapper传进去。
还有一个点要注意,在第30行处,往beandefinition的propertyvalues设置了sqlsessionfactory,那么在创建完mapperfactorybean的实例后,会对mapperfactorybean进行属性赋值,也就是spring创建bean的这句代码,populatebean(beanname, mbd, instancewrapper);,这里会通过反射调用mapperfactorybean的setsqlsessionfactory方法将sqlsessionfactory注入进mapperfactorybean实例,所以我们接下来重点看的就是mapperfactorybean这个对象了。
mapperfactorybean
接下来我们看最重要的一个类mapperfactorybean
//继承sqlsessiondaosupport、实现factorybean,那么最终注入spring容器的对象要从getobject()中取 public class mapperfactorybean<t> extends sqlsessiondaosupport implements factorybean<t> { private class<t> mapperinterface; private boolean addtoconfig = true; public mapperfactorybean() { } //构造器,我们上一节中在beandefinition中已经设置了构造器输入参数 //所以在通过反射调用构造器实例化时,会获取在beandefinition设置的构造器输入参数 //也就是对应得每个mapper接口class public mapperfactorybean(class<t> mapperinterface) { this.mapperinterface = mapperinterface; } protected void checkdaoconfig() { super.checkdaoconfig(); assert.notnull(this.mapperinterface, "property 'mapperinterface' is required"); configuration configuration = this.getsqlsession().getconfiguration(); if (this.addtoconfig && !configuration.hasmapper(this.mapperinterface)) { try { configuration.addmapper(this.mapperinterface); } catch (exception var6) { this.logger.error("error while adding the mapper '" + this.mapperinterface + "' to configuration.", var6); throw new illegalargumentexception(var6); } finally { errorcontext.instance().reset(); } } } //最终注入spring容器的就是这里的返回对象 public t getobject() throws exception { //获取父类setsqlsessionfactory方法中创建的sqlsessiontemplate //通过sqlsessiontemplate获取mapperinterface的代理类 //我们例子中就是通过sqlsessiontemplate获取com.chenhao.mapper.usermapper的代理类 //获取到mapper接口的代理类后,就把这个mapper的代理类对象注入spring容器 return this.getsqlsession().getmapper(this.mapperinterface); } public class<t> getobjecttype() { return this.mapperinterface; } public boolean issingleton() { return true; } public void setmapperinterface(class<t> mapperinterface) { this.mapperinterface = mapperinterface; } public class<t> getmapperinterface() { return this.mapperinterface; } public void setaddtoconfig(boolean addtoconfig) { this.addtoconfig = addtoconfig; } public boolean isaddtoconfig() { return this.addtoconfig; } } public abstract class sqlsessiondaosupport extends daosupport { private sqlsession sqlsession; private boolean externalsqlsession; public sqlsessiondaosupport() { } //还记得上一节中我们往beandefinition中设置的sqlsessionfactory这个属性吗? //在实例化mapperfactorybean后,进行属性赋值时,就会通过反射调用setsqlsessionfactory public void setsqlsessionfactory(sqlsessionfactory sqlsessionfactory) { if (!this.externalsqlsession) { //创建一个sqlsessiontemplate并赋值给sqlsession this.sqlsession = new sqlsessiontemplate(sqlsessionfactory); } } public void setsqlsessiontemplate(sqlsessiontemplate sqlsessiontemplate) { this.sqlsession = sqlsessiontemplate; this.externalsqlsession = true; } public sqlsession getsqlsession() { return this.sqlsession; } protected void checkdaoconfig() { assert.notnull(this.sqlsession, "property 'sqlsessionfactory' or 'sqlsessiontemplate' are required"); } }
我们看到mapperfactorybean extends sqlsessiondaosupport implements factorybean,那么getbean获取的对象是从其getobject()中获取,并且mapperfactorybean是一个单例,那么其中的属性sqlsessiontemplate对象也是一个单例,全局唯一,供所有的mapper代理类使用。
这里我大概讲一下getbean时,这个类的过程:
1、mapperfactorybean通过反射调用构造器实例化出一个对象,并且通过上一节中definition.getconstructorargumentvalues().addgenericargumentvalue(definition.getbeanclassname())设置的构造器参数,在构造器实例化时,传入mapper接口的class,并设置为mapperfactorybean的mapperinterface属性。
2、进行属性赋值,通过上一节中definition.getpropertyvalues().add("sqlsessionfactory", this.sqlsessionfactory);设置的属性值,在populatebean属性赋值过程中通过反射调用setsqlsessionfactory方法,并创建sqlsessiontemplate对象设置到sqlsession属性中。
3、由于mapperfactorybean实现了factorybean,最终注册进spring容器的对象是从getobject()方法中取,接着获取sqlsessiontemplate这个sqlsession调用getmapper(this.mapperinterface);生成mapper接口的代理对象,将mapper接口的代理对象注册进spring容器
至此,所有com.chenhao.mapper中的mapper接口都生成了代理类,并注入到spring容器了。接着我们就可以在service中直接从spring的beanfactory中获取了,如下
sqlsessiontemplate
还记得我们前面分析mybatis源码时,获取的sqlsession实例是什么吗?我们简单回顾一下
/** * executortype 指定executor的类型,分为三种:simple, reuse, batch,默认使用的是simple * transactionisolationlevel 指定事务隔离级别,使用null,则表示使用数据库默认的事务隔离界别 * autocommit 是否自动提交,传过来的参数为false,表示不自动提交 */ private sqlsession opensessionfromdatasource(executortype exectype, transactionisolationlevel level, boolean autocommit) { transaction tx = null; try { // 获取配置中的环境信息,包括了数据源信息、事务等 final environment environment = configuration.getenvironment(); // 创建事务工厂 final transactionfactory transactionfactory = gettransactionfactoryfromenvironment(environment); // 创建事务,配置事务属性 tx = transactionfactory.newtransaction(environment.getdatasource(), level, autocommit); // 创建executor,即执行器 // 它是真正用来java和数据库交互操作的类,后面会展开说。 final executor executor = configuration.newexecutor(tx, exectype); // 创建defaultsqlsession对象返回,其实现了sqlsession接口 return new defaultsqlsession(configuration, executor, autocommit); } catch (exception e) { closetransaction(tx); throw exceptionfactory.wrapexception("error opening session. cause: " + e, e); } finally { errorcontext.instance().reset(); } }
大家应该还有印象,就是上面的defaultsqlsession,那上一节的sqlsessiontemplate是什么鬼???我们来看看
// 实现sqlsession接口,单例、线程安全,使用spring的事务管理器的sqlsession, // 具体的sqlsession的功能,则是通过内部包含的sqlsessionproxy来来实现,这也是静态代理的一种实现。 // 同时内部的sqlsessionproxy实现invocationhandler接口,则是动态代理的一种实现,而线程安全也是在这里实现的。 // 注意mybatis默认的sqlsession不是线程安全的,需要每个线程有一个单例的对象实例。 // sqlsession的主要作用是提供sql操作的api,执行指定的sql语句,mapper需要依赖sqlsession来执行其方法对应的sql。 public class sqlsessiontemplate implements sqlsession, disposablebean { private final sqlsessionfactory sqlsessionfactory; private final executortype executortype; // 一个代理类,由于sqlsessiontemplate为单例的,被所有mapper,所有线程共享, // 所以sqlsessionproxy要保证这些mapper中方法调用的线程安全特性: // sqlsessionproxy的实现方式主要为实现了invocationhandler接口实现了动态代理, // 由动态代理的知识可知,invocationhandler的invoke方法会拦截所有mapper的所有方法调用, // 故这里的实现方式是在invoke方法内部创建一个sqlsession局部变量,从而实现了每个mapper的每个方法调用都使用 private final sqlsession sqlsessionproxy; private final persistenceexceptiontranslator exceptiontranslator; public sqlsessiontemplate(sqlsessionfactory sqlsessionfactory) { this(sqlsessionfactory, sqlsessionfactory.getconfiguration().getdefaultexecutortype()); } public sqlsessiontemplate(sqlsessionfactory sqlsessionfactory, executortype executortype) { this(sqlsessionfactory, executortype, new mybatisexceptiontranslator(sqlsessionfactory.getconfiguration().getenvironment().getdatasource(), true)); } public sqlsessiontemplate(sqlsessionfactory sqlsessionfactory, executortype executortype, persistenceexceptiontranslator exceptiontranslator) { assert.notnull(sqlsessionfactory, "property 'sqlsessionfactory' is required"); assert.notnull(executortype, "property 'executortype' is required"); this.sqlsessionfactory = sqlsessionfactory; this.executortype = executortype; this.exceptiontranslator = exceptiontranslator; this.sqlsessionproxy = (sqlsession)proxy.newproxyinstance(sqlsessionfactory.class.getclassloader(), new class[]{sqlsession.class}, new sqlsessiontemplate.sqlsessioninterceptor()); } public sqlsessionfactory getsqlsessionfactory() { return this.sqlsessionfactory; } public executortype getexecutortype() { return this.executortype; } public persistenceexceptiontranslator getpersistenceexceptiontranslator() { return this.exceptiontranslator; } public <t> t selectone(string statement) { //由真实的对象sqlsessionproxy执行查询 return this.sqlsessionproxy.selectone(statement); } public <t> t selectone(string statement, object parameter) { //由真实的对象sqlsessionproxy执行查询 return this.sqlsessionproxy.selectone(statement, parameter); } public <k, v> map<k, v> selectmap(string statement, string mapkey) { //由真实的对象sqlsessionproxy执行查询 return this.sqlsessionproxy.selectmap(statement, mapkey); } public <k, v> map<k, v> selectmap(string statement, object parameter, string mapkey) { //由真实的对象sqlsessionproxy执行查询 return this.sqlsessionproxy.selectmap(statement, parameter, mapkey); } public <k, v> map<k, v> selectmap(string statement, object parameter, string mapkey, rowbounds rowbounds) { //由真实的对象sqlsessionproxy执行查询 return this.sqlsessionproxy.selectmap(statement, parameter, mapkey, rowbounds); } public <t> cursor<t> selectcursor(string statement) { return this.sqlsessionproxy.selectcursor(statement); } public <t> cursor<t> selectcursor(string statement, object parameter) { return this.sqlsessionproxy.selectcursor(statement, parameter); } public <t> cursor<t> selectcursor(string statement, object parameter, rowbounds rowbounds) { return this.sqlsessionproxy.selectcursor(statement, parameter, rowbounds); } public <e> list<e> selectlist(string statement) { return this.sqlsessionproxy.selectlist(statement); } public <e> list<e> selectlist(string statement, object parameter) { return this.sqlsessionproxy.selectlist(statement, parameter); } public <e> list<e> selectlist(string statement, object parameter, rowbounds rowbounds) { return this.sqlsessionproxy.selectlist(statement, parameter, rowbounds); } public void select(string statement, resulthandler handler) { this.sqlsessionproxy.select(statement, handler); } public void select(string statement, object parameter, resulthandler handler) { this.sqlsessionproxy.select(statement, parameter, handler); } public void select(string statement, object parameter, rowbounds rowbounds, resulthandler handler) { this.sqlsessionproxy.select(statement, parameter, rowbounds, handler); } public int insert(string statement) { return this.sqlsessionproxy.insert(statement); } public int insert(string statement, object parameter) { return this.sqlsessionproxy.insert(statement, parameter); } public int update(string statement) { return this.sqlsessionproxy.update(statement); } public int update(string statement, object parameter) { return this.sqlsessionproxy.update(statement, parameter); } public int delete(string statement) { return this.sqlsessionproxy.delete(statement); } public int delete(string statement, object parameter) { return this.sqlsessionproxy.delete(statement, parameter); } public <t> t getmapper(class<t> type) { return this.getconfiguration().getmapper(type, this); } public void commit() { throw new unsupportedoperationexception("manual commit is not allowed over a spring managed sqlsession"); } public void commit(boolean force) { throw new unsupportedoperationexception("manual commit is not allowed over a spring managed sqlsession"); } public void rollback() { throw new unsupportedoperationexception("manual rollback is not allowed over a spring managed sqlsession"); } public void rollback(boolean force) { throw new unsupportedoperationexception("manual rollback is not allowed over a spring managed sqlsession"); } public void close() { throw new unsupportedoperationexception("manual close is not allowed over a spring managed sqlsession"); } public void clearcache() { this.sqlsessionproxy.clearcache(); } public configuration getconfiguration() { return this.sqlsessionfactory.getconfiguration(); } public connection getconnection() { return this.sqlsessionproxy.getconnection(); } public list<batchresult> flushstatements() { return this.sqlsessionproxy.flushstatements(); } public void destroy() throws exception { } private class sqlsessioninterceptor implements invocationhandler { private sqlsessioninterceptor() { } public object invoke(object proxy, method method, object[] args) throws throwable { sqlsession sqlsession = sqlsessionutils.getsqlsession(sqlsessiontemplate.this.sqlsessionfactory, sqlsessiontemplate.this.executortype, sqlsessiontemplate.this.exceptiontranslator); object unwrapped; try { object result = method.invoke(sqlsession, args); if (!sqlsessionutils.issqlsessiontransactional(sqlsession, sqlsessiontemplate.this.sqlsessionfactory)) { sqlsession.commit(true); } unwrapped = result; } catch (throwable var11) { unwrapped = exceptionutil.unwrapthrowable(var11); if (sqlsessiontemplate.this.exceptiontranslator != null && unwrapped instanceof persistenceexception) { sqlsessionutils.closesqlsession(sqlsession, sqlsessiontemplate.this.sqlsessionfactory); sqlsession = null; throwable translated = sqlsessiontemplate.this.exceptiontranslator.translateexceptionifpossible((persistenceexception)unwrapped); if (translated != null) { unwrapped = translated; } } throw (throwable)unwrapped; } finally { if (sqlsession != null) { sqlsessionutils.closesqlsession(sqlsession, sqlsessiontemplate.this.sqlsessionfactory); } } return unwrapped; } } }
我们看到sqlsessiontemplate实现了sqlsession接口,那么mapper代理类中执行所有的数据库操作,都是通过sqlsessiontemplate来执行,如上我们看到所有的数据库操作都由对象sqlsessionproxy执行查询
静态代理的使用
sqlsessiontemplate在内部访问数据库时,其实是委派给sqlsessionproxy来执行数据库操作的,sqlsessiontemplate不是自身重新实现了一套mybatis数据库访问的逻辑。
sqlsessiontemplate通过静态代理机制来提供sqlsession接口的行为,即实现sqlsession接口来获取sqlsession的所有方法;sqlsessiontemplate的定义如下:标准的静态代理实现模式,即实现sqlsession接口并在内部包含一个sqlsession接口实现类引用sqlsessionproxy。那我们就要看看sqlsessionproxy这个sqlsession,我们先来看看sqlsessiontemplate的构造方法
public sqlsessiontemplate(sqlsessionfactory sqlsessionfactory, executortype executortype, persistenceexceptiontranslator exceptiontranslator) { assert.notnull(sqlsessionfactory, "property 'sqlsessionfactory' is required"); assert.notnull(executortype, "property 'executortype' is required"); this.sqlsessionfactory = sqlsessionfactory; this.executortype = executortype; this.exceptiontranslator = exceptiontranslator; this.sqlsessionproxy = (sqlsession)proxy.newproxyinstance(sqlsessionfactory.class.getclassloader(), new class[]{sqlsession.class}, new sqlsessiontemplate.sqlsessioninterceptor()); }
动态代理的使用
不是吧,又使用了动态代理??真够曲折的,那我们接着看 new sqlsessiontemplate.sqlsessioninterceptor() 这个invocationhandler
private class sqlsessioninterceptor implements invocationhandler { //很奇怪,这里并没有真实目标对象? private sqlsessioninterceptor() { } public object invoke(object proxy, method method, object[] args) throws throwable { // 获取一个sqlsession来执行proxy的method对应的sql, // 每次调用都获取创建一个sqlsession线程局部变量,故不同线程相互不影响,在这里实现了sqlsessiontemplate的线程安全性 sqlsession sqlsession = sqlsessionutils.getsqlsession(sqlsessiontemplate.this.sqlsessionfactory, sqlsessiontemplate.this.executortype, sqlsessiontemplate.this.exceptiontranslator); object unwrapped; try { //直接通过新创建的sqlsession反射调用method //这也就解释了为什么不需要目标类属性了,这里每次都会创建一个 object result = method.invoke(sqlsession, args); // 如果当前操作没有在一个spring事务中,则手动commit一下 // 如果当前业务没有使用@transation,那么每次执行了mapper接口的方法直接commit // 还记得我们前面讲的mybatis的一级缓存吗,这里一级缓存不能起作用了,因为每执行一个mapper的方法,sqlsession都提交了 // sqlsession提交,会清空一级缓存 if (!sqlsessionutils.issqlsessiontransactional(sqlsession, sqlsessiontemplate.this.sqlsessionfactory)) { sqlsession.commit(true); } unwrapped = result; } catch (throwable var11) { unwrapped = exceptionutil.unwrapthrowable(var11); if (sqlsessiontemplate.this.exceptiontranslator != null && unwrapped instanceof persistenceexception) { sqlsessionutils.closesqlsession(sqlsession, sqlsessiontemplate.this.sqlsessionfactory); sqlsession = null; throwable translated = sqlsessiontemplate.this.exceptiontranslator.translateexceptionifpossible((persistenceexception)unwrapped); if (translated != null) { unwrapped = translated; } } throw (throwable)unwrapped; } finally { if (sqlsession != null) { sqlsessionutils.closesqlsession(sqlsession, sqlsessiontemplate.this.sqlsessionfactory); } } return unwrapped; } }
这里大概讲一下mapper代理类调用方法执行逻辑:
1、sqlsessiontemplate生成mapper代理类时,将本身传进去做为mapper代理类的属性,调用mapper代理类的方法时,最后会通过sqlsession类执行,也就是调用sqlsessiontemplate中的方法。
2、sqlsessiontemplate中操作数据库的方法中又交给了sqlsessionproxy这个代理类去执行,那么每次执行的方法都会回调其sqlsessioninterceptor这个invocationhandler的invoke方法
3、在invoke方法中,为每个线程创建一个新的sqlsession,并通过反射调用sqlsession的method。这里sqlsession是一个线程局部变量,不同线程相互不影响,实现了sqlsessiontemplate的线程安全性
4、如果当前操作并没有在spring事务中,那么每次执行一个方法,都会提交,相当于数据库的事务自动提交,mysql的一级缓存也将不可用
接下来我们还要看一个地方,invoke中是如何创建sqlsession的?
public static sqlsession getsqlsession(sqlsessionfactory sessionfactory, executortype executortype, persistenceexceptiontranslator exceptiontranslator) { assert.notnull(sessionfactory, "no sqlsessionfactory specified"); assert.notnull(executortype, "no executortype specified"); //通过transactionsynchronizationmanager内部的threadlocal中获取 sqlsessionholder holder = (sqlsessionholder)transactionsynchronizationmanager.getresource(sessionfactory); sqlsession session = sessionholder(executortype, holder); if(session != null) { return session; } else { if(logger.isdebugenabled()) { logger.debug("creating a new sqlsession"); } //这里我们知道实际上是创建了一个defaultsqlsession session = sessionfactory.opensession(executortype); //将创建的sqlsession对象放入transactionsynchronizationmanager内部的threadlocal中 registersessionholder(sessionfactory, executortype, exceptiontranslator, session); return session; } }
通过sessionfactory.opensession(executortype)实际创建的sqlsession还是defaultsqlsession。如果读过我前面spring源码的朋友,肯定知道transactionsynchronizationmanager这个类,其内部维护了一个threadlocal的map,这里同一线程创建了sqlsession后放入threadlocal中,同一线程中其他mapper接口调用方法时,将会直接从threadlocal中获取。