zoukankan      html  css  js  c++  java
  • 浅析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的接口代理类就创建完成了。

  • 相关阅读:
    提取轮廓后的画图处理
    提取视频中的前景物体
    写入视频帧
    处理视频序列
    视频读写
    三维重建-相机标定
    图像拼接相关
    计算两幅图像之间的单应矩阵
    ransac算法(随机抽样一致性)
    图像间投影关系
  • 原文地址:https://www.cnblogs.com/demo-alen/p/13547208.html
Copyright © 2011-2022 走看看