mybatis中mapper代理的生成過(guò)程全面分析
mybatis中mapper代理的生成過(guò)程
構(gòu)建代理類(lèi)工廠
從入口點(diǎn)開(kāi)始一步一步看,首先SqlSessionFactoryBuilder
類(lèi)中build()
方法加載配置文件
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { // ...省略 } }
將配置文件讀取為XMLConfigBuilder
對(duì)象,并調(diào)用parse()
方法來(lái)解析文件,進(jìn)到parse()
中
public Configuration parse() { // ...省略 parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; }
可以看到具體的解析過(guò)程是在parseConfiguration
方法中進(jìn)行的。
private void parseConfiguration(XNode root) { try { // ...省略 //解析mapper mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
這里重點(diǎn)看一下最后解析mapper的方法mapperElement(root.evalNode("mappers"))
,進(jìn)到方法里,
private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { // package 形式加載 ,加載package下的所有class文件 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) { // 通過(guò)Mapper.xml 加載 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) { // 通過(guò)Mapper.xml 加載 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) { // 通過(guò)單個(gè)class文件加載 Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
整個(gè)mapperElement()
方法就是加載mapper的過(guò)程了,可以看到加載mapper
有兩種形式:通過(guò)class文件和通過(guò)xml文件。
構(gòu)建mapper代理的過(guò)程也就是從這開(kāi)始的,那就一步一步分析。
看一下通過(guò)XML文件加載的過(guò)程,mybatis將mapper相關(guān)的配置讀取為一個(gè)XMLMapperBuilder
對(duì)象,并通過(guò)parse()
方法進(jìn)行解析,進(jìn)到這個(gè)方法中
public void parse() { if (!configuration.isResourceLoaded(resource)) { // 加載xml文件 configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); // 加載mapper class文件 bindMapperForNamespace(); } // ...省略 }
parse()
方法做了主要做了兩件事,加載xml文件和加載class文件。
看一下加載xml的過(guò)程
private void configurationElement(XNode context) { try { // 獲取xml文件的namespace String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } // 保存獲取xml文件的namespace builderAssistant.setCurrentNamespace(namespace); // ...省略 } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }
本文是分析mapper代理的生成過(guò)程,所以加載xml的具體細(xì)節(jié)就不詳細(xì)分析了,這里注意的是讀取xml文件中namespace
標(biāo)簽的值,并將值設(shè)置到builderAssistant
對(duì)象中
現(xiàn)在回過(guò)頭來(lái)看一下加載class文件的過(guò)程。進(jìn)到bindMapperForNamespace()
方法中去
private void bindMapperForNamespace() { // 獲取xml文件中設(shè)置的namespace值 String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { // 加載類(lèi) boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); // 添加到configuration中 configuration.addMapper(boundType); } } } }
bindMapperForNamespace()
通過(guò)xml文件中設(shè)置的namespace值加載對(duì)應(yīng)的mapper接口,最后通過(guò)configuration.addMapper()
添加到configuration
中。
還記不記得剛才提到的加載mapper有兩種形式:通過(guò)class文件和通過(guò)xml文件。通過(guò)class文件的方式直接調(diào)用configuration.addMapper()
將mapper接口加載到了configuration
中了。
Configuration
是mybatis的全局配置類(lèi),所有的mybatis相關(guān)的信息都保存在Configuration
中。
繼續(xù)進(jìn)到Configuration
的addMapper
方法中
public <T> void addMapper(Class<T> type) { mapperRegistry.addMapper(type); }
Configuration
把對(duì)應(yīng)的mapper接口添加到mapperRegistry
中,再進(jìn)到mapperRegistry.addMapper()
方法中
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { // ...省略 try { knownMappers.put(type, new MapperProxyFactory<T>(type)); // ...省略 } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
該方法首先判斷是否是接口,如果是接口則將mapper接口添加到knownMappers
中。
看一下knownMappers
的定義
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
knownMappers
是一個(gè)HashMap
,它保存的是所有的mapper接口和對(duì)應(yīng)的mapper代理工廠。
到現(xiàn)在為止,mapper已經(jīng)加載完了,但是并沒(méi)有生成mapper的代理對(duì)象,只是生成了對(duì)應(yīng)的代理工廠。
生成并使用代理對(duì)象
mybatis并沒(méi)有在加載mapper接口的時(shí)候生成代理對(duì)象,而是在調(diào)用的時(shí)候生成的。
首先從入口開(kāi)始
sqlSession.getMapper(XXX.class)
sqlSession
默認(rèn)是DefaultSqlSession
。進(jìn)到DefaultSqlSession
的getMapper()
方法中
@Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }
繼續(xù)到Configuration
的getMapper
中
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
繼續(xù)到mapperRegistry.getMapper()
中
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); // ...省略 } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
從knownMappers
中獲取到對(duì)應(yīng)mapper接口的代理工廠類(lèi)MapperProxyFactory
,然后通過(guò)MapperProxyFactory
獲取真正的代理對(duì)象。進(jìn)到MapperProxyFactory
的newInstance()
方法中
public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
首先生成了MapperProxy
類(lèi),再通過(guò)Proxy
生成真正的代理類(lèi)。
看一下MapperProxy
類(lèi)
public class MapperProxy<T> implements InvocationHandler, Serializable { // ...省略 }
MapperProxy
實(shí)現(xiàn)了InvocationHandler
接口,mapper接口的具體處理邏輯也就是在這類(lèi)中處理。
到此為止,代理對(duì)象才真正的生成。
與Spring集成時(shí)mapper代理的生成過(guò)程
mybatis與Spring集成時(shí)需要用到mybatis-spring
的jar。
Spring注冊(cè)mapper代理類(lèi)
既然是與Spring集成,那么就要配置一下,將mybatis交給Spring管理。
spring的xml文件配置
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="driverClassName"/> <property name="url" value="url"/> <property name="username" value="username"/> <property name="password" value="password"/> </bean> <!--sqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--綁定mybatis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!--注冊(cè)Mapper.xm映射器--> <property name="mapperLocations" value="classpath:cn/ycl/mapper/*.xml"/> </bean> <!--注冊(cè)所有mapper--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--basePackage 屬性是映射器接口文件的包路徑。--> <!--你可以使用分號(hào)或逗號(hào) 作為分隔符設(shè)置多于一個(gè)的包路徑--> <property name="basePackage" value="cn/ycl/mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
將mybatis交給Spring只需要配置3個(gè)bean就可以了
1、 數(shù)據(jù)庫(kù)相關(guān)的dataSource
2、 mybatis的sqlSessionFactory
3、 將mapper委托給Spring的工具類(lèi)MapperScannerConfigurer
生成mapper代理的過(guò)程主要在MapperScannerConfigurer
里,看一下MapperScannerConfigurer
的定義
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { // ...省略 }
關(guān)鍵點(diǎn)在MapperScannerConfigurer
實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor
,BeanDefinitionRegistryPostProcessor
是Spring留的擴(kuò)展點(diǎn),可以往Spring中注冊(cè)自定義的bean。
MapperScannerConfigurer
中實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor
的postProcessBeanDefinitionRegistry()
方法,mapper的注冊(cè)就是在該方法中注冊(cè)的
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // ...省略 // 實(shí)例化ClassPathMapperScanner,并對(duì)scanner相關(guān)屬性進(jìn)行配置 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass); if (StringUtils.hasText(lazyInitialization)) { scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization)); } if (StringUtils.hasText(defaultScope)) { scanner.setDefaultScope(defaultScope); } // 注冊(cè)掃描規(guī)則 scanner.registerFilters(); // 掃描并注冊(cè)所有的mapper scanner.scan( StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
postProcessBeanDefinitionRegistry()
的主要邏輯是定義一個(gè)ClassPathMapperScanner
對(duì)象,然后調(diào)用registerFilters()
注冊(cè)掃描規(guī)則,最后調(diào)用scan()
方法。
在xml中定義MapperScannerConfigurer
bean時(shí)可以設(shè)置一個(gè)annotationClass
屬性,值是一個(gè)注解類(lèi),調(diào)用registerFilters()
時(shí),registerFilters()
會(huì)添加一個(gè)只掃描設(shè)置有annotationClass
注解的類(lèi),這里沒(méi)有設(shè)置,會(huì)掃描所有的接口。SpringBoot集成mybatis時(shí)會(huì)用到這個(gè)字段
看一下ClassPathMapperScanner
類(lèi)的定義
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { // ...省略 }
ClassPathMapperScanner
繼承了ClassPathBeanDefinitionScanner
,ClassPathBeanDefinitionScanner
是Spring中定義的,是一個(gè)從指定包內(nèi)掃描所有bean定義的Spring工具。
看一下ClassPathMapperScanner
的scan()
方法
public Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { // ...省略 } else { processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
通過(guò)super.doScan(basePackages)
已經(jīng)掃描到了所有的mapper,繼續(xù)processBeanDefinitions()
方法
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { AbstractBeanDefinition definition; BeanDefinitionRegistry registry = getRegistry(); // 遍歷掃描到的所有bean for (BeanDefinitionHolder holder : beanDefinitions) { definition = (AbstractBeanDefinition) holder.getBeanDefinition(); boolean scopedProxy = false; if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) { definition = (AbstractBeanDefinition) Optional .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition()) .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException( "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]")); scopedProxy = true; } String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"); // 增加一個(gè)構(gòu)造方法,接口類(lèi)型作為構(gòu)造函數(shù)的入?yún)? definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // 將bean的類(lèi)型轉(zhuǎn)換成mapperFactoryBean definition.setBeanClass(this.mapperFactoryBeanClass); // 增加addToConfig屬性 definition.getPropertyValues().add("addToConfig", this.addToConfig); definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClassName); boolean explicitFactoryUsed = false; // 增加sqlSessionFactory屬性 if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } // 增加sqlSessionTemplate屬性 if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { LOGGER.warn( () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { LOGGER.warn( () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization); if (scopedProxy) { continue; } if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) { definition.setScope(defaultScope); } if (!definition.isSingleton()) { BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true); if (registry.containsBeanDefinition(proxyHolder.getBeanName())) { registry.removeBeanDefinition(proxyHolder.getBeanName()); } registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition()); } } }
這個(gè)方法比較長(zhǎng),但是并不復(fù)雜,主要邏輯為將掃描的bean的類(lèi)型修改成MapperFactoryBean
類(lèi)型,并增加一個(gè)將接口類(lèi)型作為入?yún)⒌臉?gòu)造函數(shù),也就是說(shuō)Spring獲取mapper時(shí)都是通過(guò)FactoryBean生成的。最后通過(guò)調(diào)用egistry.registerBeanDefinition()
方法注冊(cè)到Spring中。
看一下mybatis提供的MapperFactoryBean
的定義
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { }
MapperFactoryBean
實(shí)現(xiàn)了FactoryBean
,FactoryBean
是一個(gè)Spring提供的一個(gè)能生產(chǎn)對(duì)象的工廠Bean
MapperFactoryBean
同時(shí)繼承了SqlSessionDaoSupport
,SqlSessionDaoSupport
繼承了DaoSupport
,DaoSupport
實(shí)現(xiàn)了InitializingBean
。InitializingBean
的作用是在Spring初始化bean對(duì)象時(shí)會(huì)首先調(diào)用InitializingBean
的afterPropertiesSet()
方法。
DaoSupport
的afterPropertiesSet()
中調(diào)用了checkDaoConfig()
方法。
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException { this.checkDaoConfig(); try { this.initDao(); } catch (Exception var2) { throw new BeanInitializationException("Initialization of DAO failed", var2); } }
具體checkDaoConfig()
方法的實(shí)現(xiàn)邏輯在MapperFactoryBean
中
protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); } catch (Exception e) { // ..省略 } finally { ErrorContext.instance().reset(); } } }
OK,到這又回到mybatis了。在前面中說(shuō)了configuration.addMapper()
方法只是生成了對(duì)應(yīng)的代理工廠。
以上整個(gè)過(guò)程,即把mapper注冊(cè)為Spring的bean,又將mapper設(shè)置到mybatis中的configuration
中,所以,在使用時(shí)既可以使用Spring自動(dòng)注入那一套,又可以使用mybatis中通過(guò)sqlSession
來(lái)獲取mapper的代理對(duì)象
Spring生成代理對(duì)象
Spring中所有的mapper對(duì)應(yīng)的bean是mapper對(duì)應(yīng)的MapperFactoryBean
,那么在獲取mapper bean時(shí)是通過(guò)MapperFactoryBean
的getObject()
方法生成的
public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }
MapperFactoryBean
先獲取到sqlsession
,再通過(guò)getMapper()
獲取到的代理對(duì)象。到這里就回到了mybatis生成代理對(duì)象的過(guò)程了。
與SpringBoot集成時(shí)mapper代理的生成過(guò)程
mybatis與Spring集成時(shí)需要用到mybatis-spring-boot-starter
的jar,mybatis-spring-boot-starter
依賴(lài)mybatis-spring-boot-autoconfigure
這個(gè)jar,而mybatis-spring-boot-autoconfigure
這個(gè)jar又依賴(lài)mybatis-spring
這個(gè)jar,所以最終其實(shí)還是mybatis集成Spring那一套
根據(jù)SpringBoot自動(dòng)加載的原理直接看mybatis-spring-boot-autoconfigure
jar下META-INF/spring.factories
文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
SpringBoot會(huì)自動(dòng)加載MybatisAutoConfiguration
這個(gè)類(lèi),直接看這個(gè)類(lèi),MybatisAutoConfiguration
定義了mybtis所需的各個(gè)bean。
//生成SqlSessionFactory @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { // ...省略 } //生成SqlSessionTemplate @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { // ...省略 } //掃描mapper @Configuration @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class}) @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class}) public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { public MapperScannerRegistrarNotFoundConfiguration() { } public void afterPropertiesSet() { MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); } } //掃描mapper public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; public AutoConfiguredMapperScannerRegistrar() { } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!AutoConfigurationPackages.has(this.beanFactory)) { MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); } else { MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper"); List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (MybatisAutoConfiguration.logger.isDebugEnabled()) { packages.forEach((pkg) -> { MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg); }); } //生成MapperScannerConfigurer BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", true); // 注冊(cè)掃描規(guī)則 builder.addPropertyValue("annotationClass", Mapper.class); builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); Stream.of(beanWrapper.getPropertyDescriptors()).filter((x) -> { return x.getName().equals("lazyInitialization"); }).findAny().ifPresent((x) -> { builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"); }); registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); } } public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } }
以上就是mybatis中mapper代理的生成過(guò)程全面分析的詳細(xì)內(nèi)容,更多關(guān)于mybatis mapper代理生成的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringMVC 參數(shù)綁定相關(guān)知識(shí)總結(jié)
這篇文章主要介紹了SpringMVC 參數(shù)綁定相關(guān)知識(shí)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用SpringMVC,感興趣的朋友可以了解下2021-03-03java8 LocalDate LocalDateTime等時(shí)間類(lèi)用法實(shí)例分析
這篇文章主要介紹了java8 LocalDate LocalDateTime等時(shí)間類(lèi)用法,結(jié)合具體實(shí)例形式分析了LocalDate、LocalTime、LocalDateTime等日期時(shí)間相關(guān)類(lèi)的功能與具體使用技巧,需要的朋友可以參考下2017-04-04Java中System.setProperty()用法與實(shí)際應(yīng)用場(chǎng)景
System.setProperty是Java中用于設(shè)置系統(tǒng)屬性的方法,它允許我們?cè)谶\(yùn)行時(shí)為Java虛擬機(jī)(JVM)或應(yīng)用程序設(shè)置一些全局的系統(tǒng)屬性,下面這篇文章主要給大家介紹了關(guān)于Java中System.setProperty()用法與實(shí)際應(yīng)用場(chǎng)景的相關(guān)資料,需要的朋友可以參考下2024-04-04SpringBoot之核心依賴(lài)和自動(dòng)配置方式
這篇文章主要介紹了SpringBoot之核心依賴(lài)和自動(dòng)配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06Java實(shí)現(xiàn)的數(shù)字簽名算法RSA完整示例
這篇文章主要介紹了Java實(shí)現(xiàn)的數(shù)字簽名算法RSA,結(jié)合完整實(shí)例形式詳細(xì)分析了RSA算法的相關(guān)概念、原理、實(shí)現(xiàn)方法及操作技巧,需要的朋友可以參考下2019-09-09MyBatis 實(shí)現(xiàn)數(shù)據(jù)的批量新增和刪除的操作
這篇文章主要介紹了MyBatis 實(shí)現(xiàn)數(shù)據(jù)的批量新增和刪除的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02詳解Spring關(guān)于@Resource注入為null解決辦法
這篇文章主要介紹了詳解Spring關(guān)于@Resource注入為null解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05