mybatis中如何实现executor包懒加载功能
这篇文章主要为大家展示了“mybatis中如何实现executor包懒加载功能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“mybatis中如何实现executor包懒加载功能”这篇文章吧。
ProxyFactory
是创建代理类的工厂接口,其中的setProperties
方法用来对工厂进行属性设置,但是mybatis内置的两个实现类都没有实现该接口,所以不支持属性设置。createProxy
方法用来创建一个代理对象
publicinterfaceProxyFactory{//设置工厂属性defaultvoidsetProperties(Propertiesproperties){}//创建代理对象ObjectcreateProxy(Objecttarget,ResultLoaderMaplazyLoader,Configurationconfiguration,ObjectFactoryobjectFactory,List<Class<?>>constructorArgTypes,List<Object>constructorArgs);}
ProxyFactory
接口有2个实现类,CglibProxyFactory
和JavassistProxyFactor
类。这两个实现类整体结构高度一致,内部类、方法设置都一样,只是实现原理不同。CglibProxyFactory
基于cglib实现,JavassistProxyFactor
基于javassist实现。
接下来以CglibProxyFactory类为源码进行分析:
CglibProxyFactory
类中提供了两个创建代理对象的方法。其中createProxy
方法重写了一个普通的代理对象,createDeserializationProxy方法用来创建一个反序列化的代理对象。
publicclassCglibProxyFactoryimplementsProxyFactory{privatestaticfinalStringFINALIZE_METHOD="finalize";privatestaticfinalStringWRITE_REPLACE_METHOD="writeReplace";publicCglibProxyFactory(){try{Resources.classForName("net.sf.cglib.proxy.Enhancer");}catch(Throwablee){thrownewIllegalStateException("CannotenablelazyloadingbecauseCGLIBisnotavailable.AddCGLIBtoyourclasspath.",e);}}//创建一个代理@OverridepublicObjectcreateProxy(Objecttarget,ResultLoaderMaplazyLoader,Configurationconfiguration,ObjectFactoryobjectFactory,List<Class<?>>constructorArgTypes,List<Object>constructorArgs){returnEnhancedResultObjectProxyImpl.createProxy(target,lazyLoader,configuration,objectFactory,constructorArgTypes,constructorArgs);}//创建一个反序列化的代理publicObjectcreateDeserializationProxy(Objecttarget,Map<String,ResultLoaderMap.LoadPair>unloadedProperties,ObjectFactoryobjectFactory,List<Class<?>>constructorArgTypes,List<Object>constructorArgs){returnEnhancedDeserializationProxyImpl.createProxy(target,unloadedProperties,objectFactory,constructorArgTypes,constructorArgs);}privatestaticclassEnhancedResultObjectProxyImplimplementsMethodInterceptor{//被代理类privatefinalClass<?>type;//要懒加载的属性MapprivatefinalResultLoaderMaplazyLoader;//是否是激进懒加载privatefinalbooleanaggressive;//能够触发懒加载的方法名“equals”,“clone”,“hashCode”,“toString”。这四个方法名在Configuration中被初始化。privatefinalSet<String>lazyLoadTriggerMethods;//对象工厂privatefinalObjectFactoryobjectFactory;//被代理类构造函数的参数类型列表privatefinalList<Class<?>>constructorArgTypes;//被代理类构造函数的参数列表privatefinalList<Object>constructorArgs;privateEnhancedResultObjectProxyImpl(Class<?>type,ResultLoaderMaplazyLoader,Configurationconfiguration,ObjectFactoryobjectFactory,List<Class<?>>constructorArgTypes,List<Object>constructorArgs){this.type=type;this.lazyLoader=lazyLoader;this.aggressive=configuration.isAggressiveLazyLoading();this.lazyLoadTriggerMethods=configuration.getLazyLoadTriggerMethods();this.objectFactory=objectFactory;this.constructorArgTypes=constructorArgTypes;this.constructorArgs=constructorArgs;}publicstaticObjectcreateProxy(Objecttarget,ResultLoaderMaplazyLoader,Configurationconfiguration,ObjectFactoryobjectFactory,List<Class<?>>constructorArgTypes,List<Object>constructorArgs){finalClass<?>type=target.getClass();EnhancedResultObjectProxyImplcallback=newEnhancedResultObjectProxyImpl(type,lazyLoader,configuration,objectFactory,constructorArgTypes,constructorArgs);Objectenhanced=crateProxy(type,callback,constructorArgTypes,constructorArgs);PropertyCopier.copyBeanProperties(type,target,enhanced);returnenhanced;}/***代理类的拦截方法*@paramenhanced代理对象本身*@parammethod被调用的方法*@paramargs每调用的方法的参数*@parammethodProxy用来调用父类的代理*@return方法返回值*@throwsThrowable*/@OverridepublicObjectintercept(Objectenhanced,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{//取出被代理类中此次被调用的方法的名称finalStringmethodName=method.getName();try{synchronized(lazyLoader){//防止属性的并发加载if(WRITE_REPLACE_METHOD.equals(methodName)){//被调用的是writeReplace方法//创建一个原始对象Objectoriginal;if(constructorArgTypes.isEmpty()){original=objectFactory.create(type);}else{original=objectFactory.create(type,constructorArgTypes,constructorArgs);}//将被代理对象的属性拷贝进入新创建的对象PropertyCopier.copyBeanProperties(type,enhanced,original);if(lazyLoader.size()>0){//存在懒加载属性//则此时返回的信息要更多,不仅仅是原对象,还有相关的懒加载的设置等信息。因此使用CglibSerialStateHolder进行一次封装returnnewCglibSerialStateHolder(original,lazyLoader.getProperties(),objectFactory,constructorArgTypes,constructorArgs);}else{//没有未懒加载的属性了,那直接返回原对象进行序列化returnoriginal;}}else{if(lazyLoader.size()>0&&!FINALIZE_METHOD.equals(methodName)){//存在懒加载属性且被调用的不是finalize方法if(aggressive||lazyLoadTriggerMethods.contains(methodName)){//设置了激进懒加载或者被调用的方法是能够触发全局懒加载的方法//完成所有属性的懒加载lazyLoader.loadAll();}elseif(PropertyNamer.isSetter(methodName)){//调用了属性写方法//则先清除该属性的懒加载设置。该属性不需要被懒加载了finalStringproperty=PropertyNamer.methodToProperty(methodName);lazyLoader.remove(property);}elseif(PropertyNamer.isGetter(methodName)){//调用了属性读方法finalStringproperty=PropertyNamer.methodToProperty(methodName);//如果该属性是尚未加载的懒加载属性,则进行懒加载if(lazyLoader.hasLoader(property)){lazyLoader.load(property);}}}}}//触发被代理类的相应方法。能够进行到这里的是除去writeReplace方法外的方法,例如读写方法、toString方法等returnmethodProxy.invokeSuper(enhanced,args);}catch(Throwablet){throwExceptionUtil.unwrapThrowable(t);}}}}
代理类最核心的方法是intercept
方法,当被代理对象的其他方法被调用时,intercept方法的处理方式是:
如果设置了激进懒加载或者被调用的是触发全局加载的方法,则直接加载所有未加载的属性。
如果被调用的是属性写方法,则将该方法从懒加载列表中删除,因为此时数据库中的数据已经不是最新的,没有必要再去加载,然后进行属性的写入操作。
如果被调用的是读方法,则该属性尚未被懒加载的情况下,则加载该属性,如果该属性已经被懒加载过,则直接读取该属性。
ResultLoaderMap类:
被代理对象可能会有多个属性可以被懒加载,这些尚未完成加载的属性是在ResultLoaderMap
类的实例中存储的。ResultLoaderMap
类主要就是一个map类,该类key为属性名的大写,value
为LoadPair对象。LoadPair类是ResultLoaderMap类的内部类,它能实现对应属性的懒加载功能。
publicstaticclassLoadPairimplementsSerializable{privatestaticfinallongserialVersionUID=20130412;//用来根据反射得到数据库连接的方法名privatestaticfinalStringFACTORY_METHOD="getConfiguration";//判断是否经过了序列化的标志位,因为该属性被设置了transient,经过一次序列化和反序列化后会变为nullprivatefinaltransientObjectserializationCheck=newObject();//输出结果对象的封装privatetransientMetaObjectmetaResultObject;//用以加载未加载属性的加载器privatetransientResultLoaderresultLoader;//日志记录器privatetransientLoglog;//用来获取数据库连接的工厂privateClass<?>configurationFactory;//未加载的属性的属性名privateStringproperty;//能够加载未加载属性的SQL的编号privateStringmappedStatement;//能够加载未加载属性的SQL的参数privateSerializablemappedParameter;privateLoadPair(finalStringproperty,MetaObjectmetaResultObject,ResultLoaderresultLoader){this.property=property;this.metaResultObject=metaResultObject;this.resultLoader=resultLoader;if(metaResultObject!=null&&metaResultObject.getOriginalObject()instanceofSerializable){finalObjectmappedStatementParameter=resultLoader.parameterObject;if(mappedStatementParameterinstanceofSerializable){this.mappedStatement=resultLoader.mappedStatement.getId();this.mappedParameter=(Serializable)mappedStatementParameter;this.configurationFactory=resultLoader.configuration.getConfigurationFactory();}else{Loglog=this.getLogger();if(log.isDebugEnabled()){log.debug("Property["+this.property+"]of["+metaResultObject.getOriginalObject().getClass()+"]cannotbeloaded"+"afterdeserialization.Makesureit'sloadedbeforeserializing"+"forenamedobject.");}}}}publicvoidload()throwsSQLException{if(this.metaResultObject==null){thrownewIllegalArgumentException("metaResultObjectisnull");}if(this.resultLoader==null){thrownewIllegalArgumentException("resultLoaderisnull");}this.load(null);}/***进行加载操作*@paramuserObject需要被懒加载的对象(只有当this.metaResultObject==null||this.resultLoader==null才生效,否则会采用属性metaResultObject对应的对象)*@throwsSQLException*/publicvoidload(finalObjectuserObject)throwsSQLException{if(this.metaResultObject==null||this.resultLoader==null){//输出结果对象的封装不存在或者输出结果加载器不存在//判断用以加载属性的对应的SQL语句存在if(this.mappedParameter==null){thrownewExecutorException("Property["+this.property+"]cannotbeloadedbecause"+"requiredparameterofmappedstatement["+this.mappedStatement+"]isnotserializable.");}finalConfigurationconfig=this.getConfiguration();//取出用来加载结果的SQL语句finalMappedStatementms=config.getMappedStatement(this.mappedStatement);if(ms==null){thrownewExecutorException("Cannotlazyloadproperty["+this.property+"]ofdeserializedobject["+userObject.getClass()+"]becauseconfigurationdoesnotcontainstatement["+this.mappedStatement+"]");}//创建结果对象的包装this.metaResultObject=config.newMetaObject(userObject);//创建结果加载器this.resultLoader=newResultLoader(config,newClosedExecutor(),ms,this.mappedParameter,metaResultObject.getSetterType(this.property),null,null);}//只要经历过持久化,则可能在别的线程中了。为这次惰性加载创建的新线程ResultLoaderif(this.serializationCheck==null){//取出原来的ResultLoader中的必要信息,然后创建一个新的//这是因为load函数可能在不同的时间多次执行(第一次加载属性A,又过了好久加载属性B)。//而该对象的各种属性是跟随对象的,加载属性B时还保留着加载属性A时的状态,即ResultLoader是加载属性A时设置的//则此时ResultLoader中的Executor在ResultLoader中被替换成了一个能运行的Executor,而不是ClosedExecutor//能运行的Executor的状态可能不是close,这将导致它被复用,从而引发多线程问题//是不是被两次执行的一个关键点就是有没有经过序列化,因为执行完后会被序列化并持久化finalResultLoaderold=this.resultLoader;this.resultLoader=newResultLoader(old.configuration,newClosedExecutor(),old.mappedStatement,old.parameterObject,old.targetType,old.cacheKey,old.boundSql);}this.metaResultObject.setValue(property,this.resultLoader.loadResult());}privateConfigurationgetConfiguration(){if(this.configurationFactory==null){thrownewExecutorException("CannotgetConfigurationasconfigurationfactorywasnotset.");}ObjectconfigurationObject;try{finalMethodfactoryMethod=this.configurationFactory.getDeclaredMethod(FACTORY_METHOD);if(!Modifier.isStatic(factoryMethod.getModifiers())){thrownewExecutorException("CannotgetConfigurationasfactorymethod["+this.configurationFactory+"]#["+FACTORY_METHOD+"]isnotstatic.");}if(!factoryMethod.isAccessible()){configurationObject=AccessController.doPrivileged((PrivilegedExceptionAction<Object>)()->{try{factoryMethod.setAccessible(true);returnfactoryMethod.invoke(null);}finally{factoryMethod.setAccessible(false);}});}else{configurationObject=factoryMethod.invoke(null);}}catch(finalExecutorExceptionex){throwex;}catch(finalNoSuchMethodExceptionex){thrownewExecutorException("CannotgetConfigurationasfactoryclass["+this.configurationFactory+"]ismissingfactorymethodofname["+FACTORY_METHOD+"].",ex);}catch(finalPrivilegedActionExceptionex){thrownewExecutorException("CannotgetConfigurationasfactorymethod["+this.configurationFactory+"]#["+FACTORY_METHOD+"]threwanexception.",ex.getCause());}catch(finalExceptionex){thrownewExecutorException("CannotgetConfigurationasfactorymethod["+this.configurationFactory+"]#["+FACTORY_METHOD+"]threwanexception.",ex);}if(!(configurationObjectinstanceofConfiguration)){thrownewExecutorException("CannotgetConfigurationasfactorymethod["+this.configurationFactory+"]#["+FACTORY_METHOD+"]didn'treturn["+Configuration.class+"]but["+(configurationObject==null?"null":configurationObject.getClass())+"].");}returnConfiguration.class.cast(configurationObject);}privateLoggetLogger(){if(this.log==null){this.log=LogFactory.getLog(this.getClass());}returnthis.log;}}
以上是“mybatis中如何实现executor包懒加载功能”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。