Mybatis源码分析(3)—— 从Mybatis的视角去看Bean的初始化流程

2024-09-03 05:32

本文主要是介绍Mybatis源码分析(3)—— 从Mybatis的视角去看Bean的初始化流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

不涉及Spring完整的启动流程,仅仅从Mybatis的视角去分析几个关键的方法,找到Mybatis是如何通过这几个扩展点植入进去的,反过来看Spring是如何设计,埋下这些伏笔,实现其可扩展性。

springContext-mybatis.xml的配置:

<!-- simplest possible SqlSessionFactory configuration -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="configLocation" value="classpath:/mybatis-config.xml"></property>
</bean><!-- Scan for mappers and let them be autowired; notice there is no UserDaoImplementation needed. The required SqlSessionFactory will be autowired. -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.fcs.**.dao,fcs.common.**.dao" />
</bean>

一般情况下,要植入进去,必须通过接口实现,这也是Spring的扩展点。SqlSessionFactoryBean和MapperScannerConfigurer分别实现了这些接口:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>,InitializingBean,ApplicationListener<ApplicationEvent> {
}public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
}public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;}

从AbstractApplicationContext的invokeBeanFactoryPostProcessors方法开始分析:

Map<String, BeanDefinitionRegistryPostProcessor> beanMap = beanFactory.getBeansOfType(BeanDefinitionRegistryPostProcessor.class, true, false);
List<BeanDefinitionRegistryPostProcessor> registryPostProcessorBeans = new ArrayList<BeanDefinitionRegistryPostProcessor>(beanMap.values());OrderComparator.sort(registryPostProcessorBeans);
for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessorBeans) {postProcessor.postProcessBeanDefinitionRegistry(registry);
}

第一步:获取指定类型(BeanDefinitionRegistryPostProcessor)的beanMap

DefaultListableBeanFactory#getBeansOfType:

public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)throws BeansException {String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);Map<String, T> result = new LinkedHashMap<String, T>(beanNames.length);for (String beanName : beanNames) {try {result.put(beanName, getBean(beanName, type));}catch (BeanCreationException ex) {Throwable rootCause = ex.getMostSpecificCause();if (rootCause instanceof BeanCurrentlyInCreationException) {BeanCreationException bce = (BeanCreationException) rootCause;if (isCurrentlyInCreation(bce.getBeanName())) {if (this.logger.isDebugEnabled()) {this.logger.debug("Ignoring match to currently created bean '" + beanName + "': " +ex.getMessage());}onSuppressedException(ex);// Ignore: indicates a circular reference when autowiring constructors.// We want to find matches other than the currently created bean itself.continue;}}throw ex;}}return result;
}
  • String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);

获取匹配类型的beanNames

  • result.put(beanName, getBean(beanName, type));

根据名称和类型获取bean,放到result集合中

接下来会走bean的创建流程,在AbstractAutowireCapableBeanFactory的doCreateBean方法中:

// Initialize the bean instance.
Object exposedObject = bean;
try {populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {exposedObject = initializeBean(beanName, exposedObject, mbd);}
}

这里会调用MapperScannerConfigurer的afterPropertiesSet方法:

public void afterPropertiesSet() throws Exception {notNull(this.basePackage, "Property 'basePackage' is required");
}

第二步:执行postProcessor的postProcessBeanDefinitionRegistry方法

postProcessor.postProcessBeanDefinitionRegistry(registry);

这样就会调用MapperScannerConfigurer的postProcessBeanDefinitionRegistry方法:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {if (this.processPropertyPlaceHolders) {processPropertyPlaceHolders();}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.registerFilters();scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

这里会开启扫描mapper.xml,ClassPathMapperScanner继承自Spring的ClassPathBeanDefinitionScanner,重写了doScan方法:

public Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");} else {for (BeanDefinitionHolder holder : beanDefinitions) {GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();if (logger.isDebugEnabled()) {logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");}// the mapper interface is the original class of the bean// but, the actual class of the bean is MapperFactoryBeandefinition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());definition.setBeanClass(MapperFactoryBean.class);definition.getPropertyValues().add("addToConfig", this.addToConfig);// ......}}return beanDefinitions;}
  • Set beanDefinitions = super.doScan(basePackages);

调用父类的doScan方法构造每个Mapper的BeanDefinitionHolder,同service一样。

definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
definition.setBeanClass(MapperFactoryBean.class);definition.getPropertyValues().add("addToConfig", this.addToConfig);

往definition里设置相关属性,方便后面构造mapper代理类。

第三步:获取指定类型的postProcessorNames(BeanFactoryPostProcessor)并处理

String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String ppName : postProcessorNames) {if (processedBeans.contains(ppName)) {// skip - already processed in first phase above}else if (isTypeMatch(ppName, PriorityOrdered.class)) {priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));}else if (isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}
}// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
OrderComparator.sort(priorityOrderedPostProcessors);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

processedBeans集合包含之前两个beanName:

0 = "org.mybatis.spring.mapper.MapperScannerConfigurer#0"
1 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"

postProcessorNames数组包含三个beanName

0 = "propertyConfigurer"
1 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
2 = "org.mybatis.spring.mapper.MapperScannerConfigurer#0"

之前的两个已经创建过了,现在处理剩下的”propertyConfigurer” 一个了。

根据类关系图,自定义的propertyConfigurer刚好也实现了PriorityOrdered接口:

image

所以将会调用其postProcessBeanFactory方法:

private void invokeBeanFactoryPostProcessors(Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {for (BeanFactoryPostProcessor postProcessor : postProcessors) {postProcessor.postProcessBeanFactory(beanFactory);}
}

propertyConfigurer可以是我们自定义的XxPropertyPlaceholderConfigurer,继承自PropertyPlaceholderConfigurer就行了。这里也是一个切入点(关于dataSource的配置等)。

AbstractApplicationContext的registerBeanPostProcessors方法:

第四步:处理BeanPostProcessor相关

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

这里找到7个类:

org.springframework.context.annotation.internalAutowiredAnnotationProcessororg.springframework.context.annotation.internalRequiredAnnotationProcessororg.springframework.context.annotation.internalCommonAnnotationProcessororg.springframework.aop.config.internalAutoProxyCreator&shiroFilterlifecycleBeanPostProcessororg.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor

同处理BeanFactoryPostProcessor一样,也是分为PriorityOrdered、Ordered和其他类型的顺序分别获取bean,然后调用其registerBeanPostProcessors方法注册:

private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) {for (BeanPostProcessor postProcessor : postProcessors) {beanFactory.addBeanPostProcessor(postProcessor);}
}

lifecycleBeanPostProcessor在shiroFilter之前初始化。

这里重点看postProcessorNames中name为&shiroFilter。

在AbstractAutowireCapableBeanFactory的applyPropertyValues方法中找到了securityMannger属性,然后又在安全管理器中带出了realm属性,接着又找到adminUserService属性。

第五步:由一个Mapper的初始化引起所有Mapper的创建

这个的重点在于sqlSessionFactory这个属性,中间是根据这个特殊的type去匹配所有的beandefine,adminUserService中有个@autowire注解的adminUserMapper:

if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;
}

看了下AbstractAutowireCapableBeanFactory中的autowireByType方法:

protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {TypeConverter converter = getCustomTypeConverter();if (converter == null) {converter = bw;}Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);for (String propertyName : propertyNames) {try {PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);// Don't try autowiring by type for type Object: never makes sense,// even if it technically is a unsatisfied, non-simple property.if (!Object.class.equals(pd.getPropertyType())) {MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);// Do not allow eager init for type matching in case of a prioritized post-processor.boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);if (autowiredArgument != null) {pvs.add(propertyName, autowiredArgument);}for (String autowiredBeanName : autowiredBeanNames) {registerDependentBean(autowiredBeanName, beanName);if (logger.isDebugEnabled()) {logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +propertyName + "' to bean named '" + autowiredBeanName + "'");}}autowiredBeanNames.clear();}}catch (BeansException ex) {throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);}}
}
  • String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);

此方法获取了两个属性:

0 = "sqlSessionFactory"
1 = "sqlSessionTemplate"

MapperFactoryBean继承了SqlSessionDaoSupport类:

public abstract class SqlSessionDaoSupport extends DaoSupport {private SqlSession sqlSession;private boolean externalSqlSession;public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {if (!this.externalSqlSession) {this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);}}public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {this.sqlSession = sqlSessionTemplate;this.externalSqlSession = true;}// ......}

遍历这两个属性名称,对于sqlSessionFactory:

MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// Do not allow eager init for type matching in case of a prioritized post-processor.
boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());

MethodParameter跟SqlSessionDaoSupport中的set方法有关,eager判断也为true,接着:

Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);

public Object resolveDependency(DependencyDescriptor descriptor, String beanName,Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());if (descriptor.getDependencyType().equals(ObjectFactory.class)) {return new DependencyObjectFactory(descriptor, beanName);}else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) {return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);}else {return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter);}
}

在doResolveDependency方法中:

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

DefaultListableBeanFactory的findAutowireCandidates方法:

String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());

在这个方法中传入的还是adminUserMapper,要求的类型是SqlSessionFactory,还有个参数是DependencyDescriptor。

BeanFactoryUtils的beanNamesForTypeIncludingAncestors方法中:

String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);

然后在DefaultListableBeanFactory中getBeanNamesForType:

public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {if (!isConfigurationFrozen() || type == null || !allowEagerInit) {return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);}//......
}

doGetBeanNamesForType方法中遍历所有beanDefinitionName并判断是否匹配:

boolean matchFound = (allowEagerInit || !isFactoryBean || containsSingleton(beanName)) &&(includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type);

isTypeMatch方法中就会针对不同的beanName,与type为SqlSessionFactory做匹配:

if (FactoryBean.class.isAssignableFrom(beanType)) {if (!BeanFactoryUtils.isFactoryDereference(name)) {// If it's a FactoryBean, we want to look at what it creates, not the factory class.beanType = getTypeForFactoryBean(beanName, mbd);if (beanType == null) {return false;}}
}

beanType由于是MapperFactoryBean,就会获取相关bean。

getTypeForFactoryBean方法到这里也该终结了:

 FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);

这样就开始创建一个个mapper了。

试想一下,就是有没有这样一种可能,当Spring在创建bean的过程中依赖了其他bean,它就会去处理其依赖(创建依赖对象),然后它根据某种情况判断,将所有用到该属性的bean一起都创建了,这样就引起了所有Mapper对象的创建,合乎逻辑。

留下几个问题:

  • 其他的属性都是通过配置找到的 mapper是通过注解 如何处理这些差异
  • 如何牵一发而动全身 引起200多个mapper的初始化
  • 如果没有使用shiro,初始化过程又会是怎么样的 怎么触发 有无规律可循

这篇关于Mybatis源码分析(3)—— 从Mybatis的视角去看Bean的初始化流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132163

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

mybatis的整体架构

mybatis的整体架构分为三层: 1.基础支持层 该层包括:数据源模块、事务管理模块、缓存模块、Binding模块、反射模块、类型转换模块、日志模块、资源加载模块、解析器模块 2.核心处理层 该层包括:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件 3.接口层 该层包括:SqlSession 基础支持层 该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57