浅析Spring-Mybatis的结合--Mapper接口代理的创建


本篇文章基于Spring-5.1.x,以java-config形式配置
附:Spring实例化一个单例Bean的大致流程
学习Spring-Mybatis的整合首先需要明白以下几个Spring的扩展点

一、Spring的几个扩展点:

1.@Import

Spring的@Import组件可以为Spring导入组件。常用的@EnableXXX,一般底层都会带有@Import注解。
@Import注解加在配置类上时,在配置类解析的时候就会为容器注册导入的类。
详见ConfigurationClassParser类的processImports方法

  • 普通类
    为容器导入Import注解的类
  • 实现了ImportSelector的类
    会调用其中的selectImports()方法为容器中导入组件。此处实现了DeferredImportSelector接口的类会等到配置类解析完毕后再被导入。
  • 实现了ImportBeanDefinitionRegistrar的类
    会调用实现的registerBeanDefinitions()方法为容器中添加BeanDefinition。

2.FactoryBean

FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。(Mapper接口的实现类的动态代理就是通过FactoryBean的getObject为Mapper创建了一个实例化的类),下面看一个简单伪代码实现:

@Component("temp")
public class TempFactoryBean implements FactoryBean {
    private Class<T> mapperInterface;
	@Override
	public Object getObject () throws Exception {
		//可以在此处自定义类的生成过程
		//伪代码实现
	return 	Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);		
	}

	@Override
	public Class<?> getObjectType () {
		return Test.class;
	}
}

此处Spring容器在获取temp的时候,会调用到TempFactoryBean的getObject ()方法。
如果要获取FactoryBean本身,则需要在获取的BeanName前加上&符号。

3.Spring中的PostProcessor

Spring中定义了很多PostProcessor,会在容器注册、实例化Bean中穿插起作用。
此处只关注BeanDefinitionRegistryPostProcessor接口。
BeanDefinitionRegistryPostProcessor接口继承自BeanFactoryPostProcessor。在刷新容器—执行invokeBeanFactoryPostProcessors(beanFactory)方法的时候会回调该接口中的方法。

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
   //在标准初始化之后,修改应用程序上下文的内部bean定义注册表。
   //所有常规bean定义都将被加载,但尚未实例化任何bean。 
   //在下一阶段开始处理之前,向容器中注册更多的Bean定义信息。
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

4.InitializingBean

InitializingBean接口有一个方法:afterPropertiesSet()
实现了该接口的类,会在创建对象设置完属性后调用该方法。

5.Spring实例化一个bean的大致逻辑

另,在Java中描述一个类的类为Class,通过Class我们可以了解这个类有什么属性、方法、构造函数等,也可通过Class去获取这个类。在Spring中描述一个Spring管理的Bean的类叫做BeanDefinition,Spring实例化一个交给Spring管理的Bean都是通过BeanDefinition去实例化的。

二、简单案例

下面是一个简单的Mybatis-Spring查询功能的实现案例:
配置类:

@Configuration
@ComponentScan("spring.mybatis")
@MapperScan("spring.mybatis.dao")
public class AppConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory (DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
    @Bean
    public DataSource dataSource () {
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/alen?useSSL=false&serverTimezone=GMT%2B8");
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setPassword("123456");
        return driverManagerDataSource;
    }
}

Mapper接口

@Mapper
public interface UserMapper {
    @Select("Select * from user_table where id=#{userID}")
    User find (@Param("userID") String userID);
}

服务类:

@Component
public class UserService {
    @Resource
    UserMapper userMapper;
    public void findUser () {
        System.out.println(userMapper.find("1"));
    }
}

测试类:

public class Test {
    public static void main (String[] args) throws Exception {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService service = applicationContext.getBean(UserService.class);
        service.findUser();
    }
}

三、@MapperScan注解做了什么?

1、导入MapperScannerConfigurer类

该注解类有以下标识:

@Import(MapperScannerRegistrar.class)

说明容器在解析配置类的时候为我们导入了相关的类:
进入该类:

void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {

    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
   .....................
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
  }

可以看到,为我们注册了一个MapperScannerConfigurer类,并将该类交由Spring去管理,

2、MapperScannerConfigurer类的作用

扫描并注册Mapper。

简单看一下该类(此处只说明重要逻辑方法)

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

此类实现了BeanDefinitionRegistryPostProcessor接口和InitializingBean接口。

  • BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor接口,此处有两个回调方法:
  1. postProcessBeanFactory在此处为空白实现。
  2. postProcessBeanDefinitionRegistry此方法为主要处理逻辑。
  • InitializingBean接口的回调方法为afterPropertiesSet
    此处只做了一个简单判断:
 @Override
  public void afterPropertiesSet() throws Exception {
    notNull(this.basePackage, "Property 'basePackage' is required");
  }

下面看该类的主要处理逻辑:postProcessBeanDefinitionRegistry方法:

在执行容器刷新时,会调用到MapperScannerConfigurer的postProcessBeanDefinitionRegistry方法
在这里插入图片描述
最终调用的为ClassPathMapperScanner的doScan方法

  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  //调用父类的doScan方法,将扫描的包类符合条件的类转化为BeanDefinition,此处案例只有一个
  //name=userMapper
    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 {
    //此处为处理Mapper的具体逻辑实现
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }

进入processBeanDefinitions可以看到

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      //获取beanName  案例此处为spring.mybatis.dao.UserMapper
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      //设置构造函数参数为当前的接口spring.mybatis.dao.UserMapper类
      /**
       * public MapperFactoryBean(Class<T> mapperInterface) {
       * this.mapperInterface = mapperInterface;
       *  }
       */
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      //设置BeanClass
      definition.setBeanClass(this.mapperFactoryBeanClass);
     //设置属性addToConfig
      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
        。。。。。。。
 if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        //设置注入模式为BY_TYPE,此处会根据set方法去设置属性
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }

此处将扫描的UserMapper的BeanDefinition修改,

  • 将BeanClass设置为MapperFactoryBean类,
  • BeanName还是为UserMapper,
  • MapperFactoryBean的构造函数参数为spring.mybatis.dao.UserMapper
  • 设置注入模式为AUTOWIRE_BY_TYPE

四、UserMapper接口代理对象的实例化

Proxy中的h为MapperProxy
Spring容器先实例化了MapperFactoryBean,再根据MapperFactoryBean的getSqlSession().getObject()方法获取到代理对象。
MapperFactoryBean在实例化的时候会调用到:

在这里插入图片描述

AbStractBeanFactory类:
protected Object getObjectForBeanInstance (Object beanInstance, String name, String beanName,
											   @Nullable RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
		//如果BeanName是以&开始,则进入下方法
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			if (beanInstance instanceof NullBean) {
				return beanInstance;
			}
			//如果获取到的beanInstance不是FactoryBean类型,则抛出错误
			if (!(beanInstance instanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
			}
		}
		//如果不是工厂Bean或者名字是以&开头直接返回获取到的beanInstance
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}

		Object object = null;
		if (mbd == null) {
			//尝试从缓存中获取Bean
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			//这里可以肯定beanInstance为FactoryBean类型
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			//检测beanDefinitionMap是否包含beanName
			if (mbd == null && containsBeanDefinition(beanName)) {
				//合并父子类定义信息
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			//是否使用户定义而不是程序本身定义的,isSynthetic默认为false
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			//从FactoryBean中获取Bean
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

最终调用到FactoryBeanRegistrySupport的doGetObjectFromFactoryBean()方法:

object = factory.getObject();
*******************
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

实际创建逻辑:

MapperRegistry类的
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    *************
      return mapperProxyFactory.newInstance(sqlSession);
    *************
  }
 ----->
MapperProxyFactory<T>类的:
 public T newInstance(SqlSession sqlSession) {
 /**
  * MapperProxy实现了InvocationHandler接口,最终代理类调用的方法逻辑就在此类中
  */
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
----->
 /**
  * JDK动态代理逻辑
  */
 protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

到此处,一个Mapper的接口代理类就创建完成了。

posted @ 2020-05-31 11:13  寻找艾伦  阅读(34)  评论(0编辑  收藏  举报