Mybatis中怎么利用 mapper实现动态代理
Mybatis中怎么利用 mapper实现动态代理,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
前言
在开始动态代理的原理讲解以前,我们先看一下集成mybatis以后dao层不使用动态代理以及使用动态代理的两种实现方式,通过对比我们自己实现dao层接口以及mybatis动态代理可以更加直观的展现出mybatis动态代理替我们所做的工作,有利于我们理解动态代理的过程,讲解完以后我们再进行动态代理的原理解析,此讲解基于mybatis的环境已经搭建完成,并且已经实现了基本的用户类编写以及用户类的Dao接口的声明,下面是Dao层的接口代码
publicinterfaceUserDao{/*查询所有用户信息*/List<User>findAll();/***保存用户*@paramuser*/voidsave(Useruser);/***更新用户*@return*/voidupdate(Useruser);/***删除用户*/voiddelete(IntegeruserId);/***查找一个用户*@paramuserId*@return*/UserfindOne(IntegeruserId);/***根据名字模糊查询*@paramname*@return*/List<User>findByName(Stringname);/***根据组合对象进行模糊查询*@paramvo*@return*/List<User>findByQueryVo(QueryVovo);}
一、Mybatis dao层两种实现方式的对比
1.dao层不使用动态代理
dao层不使用动态代理的话,就需要我们自己实现dao层的接口,为了简便起见,我只是实现了Dao接口中的findAll方法,以此方法为例子来展现我们自己实现Dao的方式的情况,让我们来看代码:
publicclassUserDaoImplimplementsUserDao{privateSqlSessionFactoryfactory;publicUserDaoImpl(SqlSessionFactoryfactory){this.factory=factory;}publicList<User>findAll(){//1.获取sqlSession对象SqlSessionsqlSession=factory.openSession();//2.调用selectList方法List<User>list=sqlSession.selectList("com.example.dao.UserDao.findAll");//3.关闭流sqlSession.close();returnlist;}publicvoidsave(Useruser){}publicvoidupdate(Useruser){}publicvoiddelete(IntegeruserId){}publicUserfindOne(IntegeruserId){returnnull;}publicList<User>findByName(Stringname){returnnull;}publicList<User>findByQueryVo(QueryVovo){returnnull;}
这里的关键代码 List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll"),需要我们自己手动调用SqlSession里面的方法,基于动态代理的方式最后的目标也是成功的调用到这里。
注意:如果是添加,更新或者删除操作的话需要在方法中增加事务的提交。
2.dao层使用Mybatis的动态代理
使用动态代理的话Dao层的接口声明完成以后只需要在使用的时候通过SqlSession对象的getMapper方法获取对应Dao接口的代理对象,关键代码如下:
//3.获取SqlSession对象SqlSessionsession=factory.openSession();//4.获取dao的代理对象UserDaomapper=session.getMapper(UserDao.class);//5.执行查询所有的方法List<User>list=mapper.findAll();
获取到dao层的代理对象以后通过代理对象调用查询方法就可以实现查询所有用户列表的功能。
二、Mybatis动态代理实现方式的原理解析
动态代理中最重要的类:SqlSession、MapperProxy、MapperMethod,下面开始从入口方法到调用结束的过程分析。
1.调用方法的开始:
//4.获取dao的代理对象
UserDao mapper = session.getMapper(UserDao.class); 因为SqlSesseion为接口,所以我们通过Debug方式发现这里使用的实现类为DefaultSqlSession。
2.找到DeaultSqlSession中的getMapper方法,发现这里没有做其他的动作,只是将工作继续抛到了Configuration类中,Configuration为类不是接口,可以直接进入该类的getMapper方法中
@Overridepublic<T>TgetMapper(Class<T>type){returnconfiguration.<T>getMapper(type,this);}
3. 找到Configuration类的getMapper方法,这里也是将工作继续交到MapperRegistry的getMapper的方法中,所以我们继续向下进行。
public<T>TgetMapper(Class<T>type,SqlSessionsqlSession){returnmapperRegistry.getMapper(type,sqlSession);}
4. 找到MapperRegistry的getMapper的方法,看到这里发现和以前不一样了,通过MapperProxyFactory的命名方式我们知道这里将通过这个工厂生成我们所关注的MapperProxy的代理类,然后我们通过mapperProxyFactory.newInstance(sqlSession);进入MapperProxyFactory的newInstance方法中
public<T>TgetMapper(Class<T>type,SqlSessionsqlSession){finalMapperProxyFactory<T>mapperProxyFactory=(MapperProxyFactory<T>)knownMappers.get(type);if(mapperProxyFactory==null){thrownewBindingException("Type"+type+"isnotknowntotheMapperRegistry.");}try{returnmapperProxyFactory.newInstance(sqlSession);}catch(Exceptione){thrownewBindingException("Errorgettingmapperinstance.Cause:"+e,e);}}
5. 找到MapperProxyFactory的newIntance方法,通过参数类型SqlSession可以得知,上面的调用先进入第二个newInstance方法中并创建我们所需要重点关注的MapperProxy对象,第二个方法中再调用第一个newInstance方法并将MapperProxy对象传入进去,根据该对象创建代理类并返回。这里已经得到需要的代理类了,但是我们的代理类所做的工作还得继续向下看MapperProxy类。
protectedTnewInstance(MapperProxy<T>mapperProxy){return(T)Proxy.newProxyInstance(mapperInterface.getClassLoader(),newClass[]{mapperInterface},mapperProxy);}publicTnewInstance(SqlSessionsqlSession){finalMapperProxy<T>mapperProxy=newMapperProxy<T>(sqlSession,mapperInterface,methodCache);returnnewInstance(mapperProxy);}
6. 找到MapperProxy类,发现其确实实现了JDK动态代理必须实现的接口InvocationHandler,所以我们重点关注invoke()方法,这里看到在invoke方法里先获取MapperMethod类,然后调用mapperMethod.execute(),所以我们继续查看MapperMethod类的execute方法。
publicclassMapperProxy<T>implementsInvocationHandler,Serializable{privatestaticfinallongserialVersionUID=-6424540398559729838L;privatefinalSqlSessionsqlSession;privatefinalClass<T>mapperInterface;privatefinalMap<Method,MapperMethod>methodCache;publicMapperProxy(SqlSessionsqlSession,Class<T>mapperInterface,Map<Method,MapperMethod>methodCache){this.sqlSession=sqlSession;this.mapperInterface=mapperInterface;this.methodCache=methodCache;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{try{if(Object.class.equals(method.getDeclaringClass())){returnmethod.invoke(this,args);}elseif(isDefaultMethod(method)){returninvokeDefaultMethod(proxy,method,args);}}catch(Throwablet){throwExceptionUtil.unwrapThrowable(t);}finalMapperMethodmapperMethod=cachedMapperMethod(method);returnmapperMethod.execute(sqlSession,args);}privateMapperMethodcachedMapperMethod(Methodmethod){MapperMethodmapperMethod=methodCache.get(method);if(mapperMethod==null){mapperMethod=newMapperMethod(mapperInterface,method,sqlSession.getConfiguration());methodCache.put(method,mapperMethod);}returnmapperMethod;}@UsesJava7privateObjectinvokeDefaultMethod(Objectproxy,Methodmethod,Object[]args)throwsThrowable{finalConstructor<MethodHandles.Lookup>constructor=MethodHandles.Lookup.class.getDeclaredConstructor(Class.class,int.class);if(!constructor.isAccessible()){constructor.setAccessible(true);}finalClass<?>declaringClass=method.getDeclaringClass();returnconstructor.newInstance(declaringClass,MethodHandles.Lookup.PRIVATE|MethodHandles.Lookup.PROTECTED|MethodHandles.Lookup.PACKAGE|MethodHandles.Lookup.PUBLIC).unreflectSpecial(method,declaringClass).bindTo(proxy).invokeWithArguments(args);}/***Backportofjava.lang.reflect.Method#isDefault()*/privatebooleanisDefaultMethod(Methodmethod){return((method.getModifiers()&(Modifier.ABSTRACT|Modifier.PUBLIC|Modifier.STATIC))==Modifier.PUBLIC)&&method.getDeclaringClass().isInterface();}}
7. 找到类MapperMethod类的execute方法,发现execute中通过调用本类中的其他方法获取并封装返回结果,我们来看一下MapperMethod整个类。
publicObjectexecute(SqlSessionsqlSession,Object[]args){Objectresult;switch(command.getType()){caseINSERT:{Objectparam=method.convertArgsToSqlCommandParam(args);result=rowCountResult(sqlSession.insert(command.getName(),param));break;}caseUPDATE:{Objectparam=method.convertArgsToSqlCommandParam(args);result=rowCountResult(sqlSession.update(command.getName(),param));break;}caseDELETE:{Objectparam=method.convertArgsToSqlCommandParam(args);result=rowCountResult(sqlSession.delete(command.getName(),param));break;}caseSELECT:if(method.returnsVoid()&&method.hasResultHandler()){executeWithResultHandler(sqlSession,args);result=null;}elseif(method.returnsMany()){result=executeForMany(sqlSession,args);}elseif(method.returnsMap()){result=executeForMap(sqlSession,args);}elseif(method.returnsCursor()){result=executeForCursor(sqlSession,args);}else{Objectparam=method.convertArgsToSqlCommandParam(args);result=sqlSession.selectOne(command.getName(),param);}break;caseFLUSH:result=sqlSession.flushStatements();break;default:thrownewBindingException("Unknownexecutionmethodfor:"+command.getName());}if(result==null&&method.getReturnType().isPrimitive()&&!method.returnsVoid()){thrownewBindingException("Mappermethod'"+command.getName()+"attemptedtoreturnnullfromamethodwithaprimitivereturntype("+method.getReturnType()+").");}returnresult;}
8. MapperMethod类是整个代理机制的核心类,对SqlSession中的操作进行了封装使用。
该类里有两个内部类SqlCommand和MethodSignature。 SqlCommand用来封装CRUD操作,也就是我们在xml中配置的操作的节点。每个节点都会生成一个MappedStatement类。
MethodSignature用来封装方法的参数以及返回类型,在execute的方法中我们发现在这里又回到了SqlSession中的接口调用,和我们自己实现UerDao接口的方式中直接用SqlSession对象调用DefaultSqlSession的实现类的方法是一样的,经过一大圈的代理又回到了原地,这就是整个动态代理的实现过程了。
publicclassMapperMethod{privatefinalSqlCommandcommand;privatefinalMethodSignaturemethod;publicMapperMethod(Class<?>mapperInterface,Methodmethod,Configurationconfig){this.command=newSqlCommand(config,mapperInterface,method);this.method=newMethodSignature(config,mapperInterface,method);}publicObjectexecute(SqlSessionsqlSession,Object[]args){Objectresult;switch(command.getType()){caseINSERT:{Objectparam=method.convertArgsToSqlCommandParam(args);result=rowCountResult(sqlSession.insert(command.getName(),param));break;}caseUPDATE:{Objectparam=method.convertArgsToSqlCommandParam(args);result=rowCountResult(sqlSession.update(command.getName(),param));break;}caseDELETE:{Objectparam=method.convertArgsToSqlCommandParam(args);result=rowCountResult(sqlSession.delete(command.getName(),param));break;}caseSELECT:if(method.returnsVoid()&&method.hasResultHandler()){executeWithResultHandler(sqlSession,args);result=null;}elseif(method.returnsMany()){result=executeForMany(sqlSession,args);}elseif(method.returnsMap()){result=executeForMap(sqlSession,args);}elseif(method.returnsCursor()){result=executeForCursor(sqlSession,args);}else{Objectparam=method.convertArgsToSqlCommandParam(args);result=sqlSession.selectOne(command.getName(),param);}break;caseFLUSH:result=sqlSession.flushStatements();break;default:thrownewBindingException("Unknownexecutionmethodfor:"+command.getName());}if(result==null&&method.getReturnType().isPrimitive()&&!method.returnsVoid()){thrownewBindingException("Mappermethod'"+command.getName()+"attemptedtoreturnnullfromamethodwithaprimitivereturntype("+method.getReturnType()+").");}returnresult;}privateObjectrowCountResult(introwCount){finalObjectresult;if(method.returnsVoid()){result=null;}elseif(Integer.class.equals(method.getReturnType())||Integer.TYPE.equals(method.getReturnType())){result=rowCount;}elseif(Long.class.equals(method.getReturnType())||Long.TYPE.equals(method.getReturnType())){result=(long)rowCount;}elseif(Boolean.class.equals(method.getReturnType())||Boolean.TYPE.equals(method.getReturnType())){result=rowCount>0;}else{thrownewBindingException("Mappermethod'"+command.getName()+"'hasanunsupportedreturntype:"+method.getReturnType());}returnresult;}privatevoidexecuteWithResultHandler(SqlSessionsqlSession,Object[]args){MappedStatementms=sqlSession.getConfiguration().getMappedStatement(command.getName());if(void.class.equals(ms.getResultMaps().get(0).getType())){thrownewBindingException("method"+command.getName()+"needseithera@ResultMapannotation,a@ResultTypeannotation,"+"oraresultTypeattributeinXMLsoaResultHandlercanbeusedasaparameter.");}Objectparam=method.convertArgsToSqlCommandParam(args);if(method.hasRowBounds()){RowBoundsrowBounds=method.extractRowBounds(args);sqlSession.select(command.getName(),param,rowBounds,method.extractResultHandler(args));}else{sqlSession.select(command.getName(),param,method.extractResultHandler(args));}}private<E>ObjectexecuteForMany(SqlSessionsqlSession,Object[]args){List<E>result;Objectparam=method.convertArgsToSqlCommandParam(args);if(method.hasRowBounds()){RowBoundsrowBounds=method.extractRowBounds(args);result=sqlSession.<E>selectList(command.getName(),param,rowBounds);}else{result=sqlSession.<E>selectList(command.getName(),param);}//issue#510Collections&arrayssupportif(!method.getReturnType().isAssignableFrom(result.getClass())){if(method.getReturnType().isArray()){returnconvertToArray(result);}else{returnconvertToDeclaredCollection(sqlSession.getConfiguration(),result);}}returnresult;}private<T>Cursor<T>executeForCursor(SqlSessionsqlSession,Object[]args){Cursor<T>result;Objectparam=method.convertArgsToSqlCommandParam(args);if(method.hasRowBounds()){RowBoundsrowBounds=method.extractRowBounds(args);result=sqlSession.<T>selectCursor(command.getName(),param,rowBounds);}else{result=sqlSession.<T>selectCursor(command.getName(),param);}returnresult;}private<E>ObjectconvertToDeclaredCollection(Configurationconfig,List<E>list){Objectcollection=config.getObjectFactory().create(method.getReturnType());MetaObjectmetaObject=config.newMetaObject(collection);metaObject.addAll(list);returncollection;}@SuppressWarnings("unchecked")private<E>ObjectconvertToArray(List<E>list){Class<?>arrayComponentType=method.getReturnType().getComponentType();Objectarray=Array.newInstance(arrayComponentType,list.size());if(arrayComponentType.isPrimitive()){for(inti=0;i<list.size();i++){Array.set(array,i,list.get(i));}returnarray;}else{returnlist.toArray((E[])array);}}private<K,V>Map<K,V>executeForMap(SqlSessionsqlSession,Object[]args){Map<K,V>result;Objectparam=method.convertArgsToSqlCommandParam(args);if(method.hasRowBounds()){RowBoundsrowBounds=method.extractRowBounds(args);result=sqlSession.<K,V>selectMap(command.getName(),param,method.getMapKey(),rowBounds);}else{result=sqlSession.<K,V>selectMap(command.getName(),param,method.getMapKey());}returnresult;}publicstaticclassParamMap<V>extendsHashMap<String,V>{privatestaticfinallongserialVersionUID=-2212268410512043556L;@OverridepublicVget(Objectkey){if(!super.containsKey(key)){thrownewBindingException("Parameter'"+key+"'notfound.Availableparametersare"+keySet());}returnsuper.get(key);}}publicstaticclassSqlCommand{privatefinalStringname;privatefinalSqlCommandTypetype;publicSqlCommand(Configurationconfiguration,Class<?>mapperInterface,Methodmethod){finalStringmethodName=method.getName();finalClass<?>declaringClass=method.getDeclaringClass();MappedStatementms=resolveMappedStatement(mapperInterface,methodName,declaringClass,configuration);if(ms==null){if(method.getAnnotation(Flush.class)!=null){name=null;type=SqlCommandType.FLUSH;}else{thrownewBindingException("Invalidboundstatement(notfound):"+mapperInterface.getName()+"."+methodName);}}else{name=ms.getId();type=ms.getSqlCommandType();if(type==SqlCommandType.UNKNOWN){thrownewBindingException("Unknownexecutionmethodfor:"+name);}}}publicStringgetName(){returnname;}publicSqlCommandTypegetType(){returntype;}privateMappedStatementresolveMappedStatement(Class<?>mapperInterface,StringmethodName,Class<?>declaringClass,Configurationconfiguration){StringstatementId=mapperInterface.getName()+"."+methodName;if(configuration.hasStatement(statementId)){returnconfiguration.getMappedStatement(statementId);}elseif(mapperInterface.equals(declaringClass)){returnnull;}for(Class<?>superInterface:mapperInterface.getInterfaces()){if(declaringClass.isAssignableFrom(superInterface)){MappedStatementms=resolveMappedStatement(superInterface,methodName,declaringClass,configuration);if(ms!=null){returnms;}}}returnnull;}}publicstaticclassMethodSignature{privatefinalbooleanreturnsMany;privatefinalbooleanreturnsMap;privatefinalbooleanreturnsVoid;privatefinalbooleanreturnsCursor;privatefinalClass<?>returnType;privatefinalStringmapKey;privatefinalIntegerresultHandlerIndex;privatefinalIntegerrowBoundsIndex;privatefinalParamNameResolverparamNameResolver;publicMethodSignature(Configurationconfiguration,Class<?>mapperInterface,Methodmethod){TyperesolvedReturnType=TypeParameterResolver.resolveReturnType(method,mapperInterface);if(resolvedReturnTypeinstanceofClass<?>){this.returnType=(Class<?>)resolvedReturnType;}elseif(resolvedReturnTypeinstanceofParameterizedType){this.returnType=(Class<?>)((ParameterizedType)resolvedReturnType).getRawType();}else{this.returnType=method.getReturnType();}this.returnsVoid=void.class.equals(this.returnType);this.returnsMany=(configuration.getObjectFactory().isCollection(this.returnType)||this.returnType.isArray());this.returnsCursor=Cursor.class.equals(this.returnType);this.mapKey=getMapKey(method);this.returnsMap=(this.mapKey!=null);this.rowBoundsIndex=getUniqueParamIndex(method,RowBounds.class);this.resultHandlerIndex=getUniqueParamIndex(method,ResultHandler.class);this.paramNameResolver=newParamNameResolver(configuration,method);}publicObjectconvertArgsToSqlCommandParam(Object[]args){returnparamNameResolver.getNamedParams(args);}publicbooleanhasRowBounds(){returnrowBoundsIndex!=null;}publicRowBoundsextractRowBounds(Object[]args){returnhasRowBounds()?(RowBounds)args[rowBoundsIndex]:null;}publicbooleanhasResultHandler(){returnresultHandlerIndex!=null;}publicResultHandlerextractResultHandler(Object[]args){returnhasResultHandler()?(ResultHandler)args[resultHandlerIndex]:null;}publicStringgetMapKey(){returnmapKey;}publicClass<?>getReturnType(){returnreturnType;}publicbooleanreturnsMany(){returnreturnsMany;}publicbooleanreturnsMap(){returnreturnsMap;}publicbooleanreturnsVoid(){returnreturnsVoid;}publicbooleanreturnsCursor(){returnreturnsCursor;}privateIntegergetUniqueParamIndex(Methodmethod,Class<?>paramType){Integerindex=null;finalClass<?>[]argTypes=method.getParameterTypes();for(inti=0;i<argTypes.length;i++){if(paramType.isAssignableFrom(argTypes[i])){if(index==null){index=i;}else{thrownewBindingException(method.getName()+"cannothavemultiple"+paramType.getSimpleName()+"parameters");}}}returnindex;}privateStringgetMapKey(Methodmethod){StringmapKey=null;if(Map.class.isAssignableFrom(method.getReturnType())){finalMapKeymapKeyAnnotation=method.getAnnotation(MapKey.class);if(mapKeyAnnotation!=null){mapKey=mapKeyAnnotation.value();}}returnmapKey;}}
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。