Mybatis-Spring源码分析图解
作者:Jame! 发布时间:2023-07-18 13:35:07
Mybatis-Spring
当我们使用mybatis和spring整合后为什么下面的代码可以运行?
一个问题:
我就写了个mapper接口为什么能用?
首先来看,在spring的配置xml中有一段
<bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.jame.dao"/>
</bean>
这段xml的作用是将一个类添加到spring容器中,点进这个类看看
它实现了一个BeanDefinitionRegistryPostProcessor
接口,关于这个接口的作用和执行时机上篇博客写过了,这里就不再赘述
那么它必然实现postProcessBeanDefinitionRegistry
方法,点击这个方法查看
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
..........
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
其中将接口注册到spring容器中在最后一行,先来看ClassPathMapperScanner
这个类,它继承了ClassPathBeanDefinitionScanner
这个扫描器
scan的具体代码
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
这个是spring内部的扫描方法,当它走到doScan的时候,因为ClassPathMapperScanner这个类重写了doScan方法,所以会调用子类重写的方法
@Override
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 {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
通过包名获取BeanDefinitionHolder,现在它获取到了User接口的BeanDefinitionHolder,然后判断如果BeanDefinitionHolder的集合为空,也就是没有找到mapper的情况则不做任何处理,而现在有一个UserMapper的,进入else
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
BeanDefinitionRegistry registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
.........
//主要看这行
definition.setBeanClass(this.mapperFactoryBeanClass);
.........
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());
}
}
}
将MapperFactoryBean类设置为了UserMapperBeanDefinition的class
spring在创建这个userMapper这个Bean的时候会使用这个有参构造将当前这个UserMapper类型设置到mapperInterface属性上(为啥使用有参构造而不是无参来初始化对象我也不知道.....这和spring推断构造方法有关,以后学会了在来写)
这个MapperFactoryBean实现了一个FactoryBean
接口,这个接口可以让我们自定义获取bean的操作
回到spring的代码,例如当我们使用context.getBean(xxx.class)的时候
spring将xxx.class类型解析为bean名称,通过名称去获取
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
//获取对应的beanName
String beanName = transformedBeanName(name);
Object bean;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
.......
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
//真正创建对象的地方
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
首先是调用getSingleton
方法,尝试获取存在缓存中的bean(其实就是三个Map,key为bean名称,value是对象),那现在是首次获取map中没有
然后执行到下面的createBean,当创建完这个bean后spring需要判断这个bean是一个普通bean还是一个FactoryBean,程序员是想要获取普通bean还是FactoryBean,还是FactoryBean的getObject方法返回的从工厂生成的对象
咱们一段一段看
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
}
.....
}
BeanFactoryUtils.isFactoryDereference(name)
的作用是一个字符串判断,当返回传入名称是否为工厂,如果name不为空,并且以&开头返回true
这个方法在下面的判断也使用到了,记一下它的作用即可
来看例子
在我们使用FactoryBean通过context.getBean("工厂Bean名称")的时候获取的是FactoryBean的getObject生成的对象,如果我们想获取FactoryBean的引用则需要在名称前面加一个&
符号
回来看代码,如果这个bean的引用是一个NullBean类型则直接返回引用,下面有做了一个判断
if (!(beanInstance instanceof FactoryBean))
再次判断这个bean是不是一个FactoryBean,如果为true则抛出异常,这个好理解,因为我们在getBean的时候完全可以将一个普通的bean名称前面加上&符号
主要的判断在下面的这个if
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
现在有3中情况
1.当前的bean是一个普通的bean
第一个条件false 取反 true 第二个条件false 结果true,直接返回bean实例
2.当前是一个FactoryBean,想通过工厂获取Bean
第一个条件 true 取反false 第二个条件false 结果false,进行下面的操作
3.当前是一个FactoryBean,想获取工厂的引用
第一个条件 true 取反 false 第二个条件 true 结果 true 直接返回factoryBean实例
当前我们是想通过FactoryBean获取对象,那么不进if,继续下面的代码
Object object = null;
// 如果beanDefinition为null,则尝试从缓存中获取给定的FactoryBean公开的对象
if (mbd == null) {
//尝试从缓存中加载bean
object = getCachedObjectForFactoryBean(beanName);
}
// 未能从缓存中获得FactoryBean公开的对象,则说明该bean是一个新创建的bean
if (object == null) {
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 从给定的FactoryBean中获取指定的beanName对象
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
主要来看getObjectFromFactoryBean
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
//调用factoryBean的getObject方法
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
}
..........
}
}
}
doGetObjectFromFactoryBean
方法
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//调用重写的getObject方法
object = factory.getObject();
}
}
.......
return object;
}
也就是说当我们getBean("userMapper")的时候其实是调用FactoryBean的getObject方法,代码回到mybatis-spring项目的MapperFactoryBean类中的getObject方法
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
到最后发现是通过jdk的 * 来生成的对象,那么回答开始的问题
我就写了个接口为什么能用?
因为mybatis在spring加载bean之前修改了beanDefinition,通过MapperScannerConfigurer类实现的BeanDefinitionRegistryPostProcessor接口中将我们定义的一些mapper接口的BeanDefinition的BeanClass属性修改为了MapperFactoryBean,而这个类实现了FactoryBean,我们获取接口实际上是通过FactoryBean的getObject方法
来源:https://www.cnblogs.com/sunankang/p/15562425.html


猜你喜欢
- 第一篇讨论了面向对象编程和它的特点,关于Java和它的功能的常见问题,Java的集合类,垃圾收集器,本章主要讨论异常处理,Java小应用程序
- 人机猜拳小游戏,只要用到的java面向对象的思维模式。本游戏中有游戏玩家和计算机两个参与者。玩家和计算机都有的属性是姓名和得分。共分为4个类
- 前言相信大家在使用spring的项目中,前台传递参数到后台是经常遇到的事, 我们必须熟练掌握一些常用的参数传递方式和注解的使用,本文将给大家
- 本文实例为大家分享了android TextView跑马灯效果的具体代码,供大家参考,具体内容如下一、要点设置四个属性android:sin
- 表单提交这个方法是挺方便的,但在java来说就显得有些麻烦了,怎么个麻烦呢,就是当你字段多的时候,你就得一个一个的获取其对应的值,这样代码量
- 编程是一门艺术,大批量的改动显然是非常丑陋的做法,用心的琢磨写的代码让它变的更美观。在现实生活中,存在很多“部分-整体&
- 下面给大家分享一个有趣的动画:这里比较适合一张图片的翻转,如果是多张图片,可以参考APIDemo里的例子,就是加个ArrayAdapter,
- 前言我们都知道WebApi是依赖于Asp.Net MVC的 ,所以,想创建WebApi,就需要先创建一个Asp.Net MVC项目。但用Vi
- 为了提升编译速度,这几天用上了 AS 3.0 和 Gradle 3.0 插件,不得不说不论是 AS 3.0,还是 Gradle 3.0 都变
- 简介一般情况下我们在flutter中搭建的app基本上都是用的是MaterialApp这种设计模式,MaterialApp中为我们接下来使用
- public static String toUtf8String(String s) {
- 数据结构是数据存储的方式,算法是数据计算的方式。所以在开发中,算法和数据结构息息相关。今天的讲义中会涉及部分数据结构的专业名词,如果各位铁粉
- 我们都知道可以用爬虫来找寻一些想要的数据,除了可以使用python进行操作,我们最近学习的java同样也支持爬虫的运行,本篇小编就教大家用j
- 一、前言 网上有许多的多线程断点续传操作,但总是写的很云里雾里,或者
- 一、 进程 简单来说,进程是对资源的抽象,是资源的容器,
- 本文实例为大家分享了Unity实现聊天室功能的具体代码,供大家参考,具体内容如下简单聊天室功能,客户端发送消息后,服务器接收到消息后分发到其
- 本文实例讲述了Android开发实现Launcher3应用列表修改透明背景的方法。分享给大家供大家参考,具体如下:Launcher时开机完成
- Jetty和tomcat的比较Tomcat和Jetty都是一种Servlet引擎,他们都支持标准的servlet规范和JavaEE的规范。架
- 对象嵌套关联查询一对多List集合查询mybatis嵌套关联查询如下由于我的是一对集合查询,所以我有两个类。@Data@TableName(
- 如下所示:@Overridepublic void onWindowFocusChanged(boolean hasFocus) {supe