Spring整合MyBatis底层原理

Spring整合MyBatis底层原理

项目结构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kpbVmnfT-1687513246717)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623170457676.png)]

项目代码
  1. build.gradle需要进入的依赖

    //    testImplementation(platform("org.junit:junit-bom:5.9.1"))
    //    testImplementation("org.junit.jupiter:junit-jupiter")
    	implementation("org.aspectj:aspectjweaver:1.9.7")
        annotationProcessor("org.aspectj:aspectjweaver:1.9.7")
        implementation("javax.annotation:javax.annotation-api:1.3.2")
        annotationProcessor("javax.annotation:javax.annotation-api:1.3.2")
        implementation("javax.inject:javax.inject:1")
        annotationProcessor("javax.inject:javax.inject:1")
        implementation(project(":spring-context"))
        implementation(project(":spring-jdbc"))
        implementation(project(":spring-tx"))
        implementation(project(":spring-webmvc"))  // mvc
        implementation(project(":spring-context-support")) // freemarker
    
        implementation("mysql:mysql-connector-java:5.1.40")
        implementation("org.mybatis:mybatis:3.5.7")
        implementation("org.mybatis:mybatis-spring:1.3.2")
    
  2. 自定义注解XiaogeScan

    package com.xiaoge.springmybatis.annotation;
    
    import com.xiaoge.springmybatis.springintegrationmybatis.XiaogeBeanDefinitionRegister;
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Import(XiaogeBeanDefinitionRegister.class)  // import注解是可以这样用的
    public @interface XiaogeScan {
    
    	String value() default "";
    
    }
    
  3. 定义三个mapper

    package com.xiaoge.springmybatis.mapper;
    
    import org.apache.ibatis.annotations.Select;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public interface UserMapper {
    
    	@Select("select user")
    	String selectById(Integer id);
    }
    
    package com.xiaoge.springmybatis.mapper;
    
    import org.apache.ibatis.annotations.Select;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public interface MemberMapper {
    
    	@Select("select member")
    	String selectById(Integer id);
    
    }
    
    package com.xiaoge.springmybatis.mapper;
    
    import org.apache.ibatis.annotations.Select;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public interface OrderMapper {
    
    	@Select("select order")
    	String selectById(Integer id);
    
    }
    
  4. 定义一个service

    package com.xiaoge.springmybatis.service;
    
    import com.xiaoge.springmybatis.mapper.MemberMapper;
    import com.xiaoge.springmybatis.mapper.OrderMapper;
    import com.xiaoge.springmybatis.mapper.UserMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    @Component
    public class UserService {
    
    	@Autowired
    	private UserMapper userMapper; // 代理对象赋值给这个属性, spring属性注入, 对象
    
    	@Autowired
    	private OrderMapper orderMapper;
    
    	@Autowired
    	private MemberMapper memberMapper;
    
    
    	public void test() {
    		System.out.println(userMapper);
    		System.out.println(userMapper.getClass().getSimpleName());
    		System.out.println(orderMapper);
    		System.out.println(orderMapper.getClass().getSimpleName());
    		System.out.println(memberMapper);
    		System.out.println(memberMapper.getClass().getSimpleName());
    		userMapper.selectById(1);
    		orderMapper.selectById(1);
    		memberMapper.selectById(1);
    	}
    
    }
    
  5. 重点怎么代理Mapper

    package com.xiaoge.springmybatis.springintegrationmybatis;
    
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * TODO  spring 整合 mybatis
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public class SpringIntegrationMybatis implements FactoryBean {
    	// todo FactoryBean 本身就是一个特殊bean
    	private Class mapper; // 灵活设置属性
    
    	public SpringIntegrationMybatis(Class mapper) {
    		this.mapper = mapper;
    	}
    
    	@Override
    	public Object getObject() throws Exception {
    		// mybatis 代理对象
    		Object o = Proxy.newProxyInstance(SpringIntegrationMybatis.class.getClassLoader(), new Class[]{mapper}, new InvocationHandler() {
    			@Override
    			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    				if (Object.class.equals(method.getDeclaringClass())) {
    					System.out.println(method.getName());
    					return method.invoke(this, args);
    				}
    				System.out.println(method.getName());
    				return null;
    			}
    		});
    		return o; // bean
    	}
    
    	@Override
    	public Class<?> getObjectType() {
    		return mapper;
    	}
    }
    
  6. 重点怎么把扫描到的mapper生成BeanDefinition的(这里就是整合核心)

    package com.xiaoge.springmybatis.springintegrationmybatis;
    
    import com.xiaoge.springmybatis.annotation.XiaogeScan;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    import java.beans.Introspector;
    import java.io.File;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    /**
     * TODO 该类通过 import 注解导入, 它会去执行 该类下面的方法, 原因可以去看@Import注解源码
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public class XiaogeBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
    
    	@Override
    	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
    		// 获取XiaoScan注解的属性名和值
    		Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(XiaogeScan.class.getName());
    		// 获取我们扫描注解上面的路径
    		String path = (String) annotationAttributes.get("value");
    
    		// 扫描
    		List<Class> mappers = new ArrayList<>();
    		doScan(path, mappers);
    
    		mappers.forEach(mapper -> {
    			BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
    			AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    
    			// 给构造方法添加参数
    			beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(mapper);  // new SpringIntegrationMybatis(mapper)
    			// 设置类型
    			beanDefinition.setBeanClass(SpringIntegrationMybatis.class);
    
    			// 注册BeanDefinition  (beanName是根据spring源码用到的方法生成的)
    			registry.registerBeanDefinition(Introspector.decapitalize(mapper.getSimpleName()), beanDefinition);
    
    		});
    	}
    
    	/**
    	 * 扫描当前包下所有带@Mapper注解的接口
    	 */
    	private void doScan(String path, List<Class> mappers) {
    
    		// 获取路径
    		path = path.replace(".", "/");
    
    		// 获取资源路径
    		ClassLoader classLoader = XiaogeBeanDefinitionRegister.class.getClassLoader();
    		URL resource = classLoader.getResource(path);
    
    		// 拿到当前目录下面所有的文件
    		File files = new File(resource.getFile());
    
    		// 当前文件是不是目录
    		if (files.isDirectory()) {
    			// 获取目录下所有文件
    			for (File file : files.listFiles()) {
    				// 获取文件绝对路径
    				String fileName = file.getAbsolutePath();
    				// 获取com->.class结尾的名称
    				String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
    				// 获取权限定类名
    				className = className.replace("\\", ".");
    
    				try {
    					// 加载class
    					Class<?> clazz = classLoader.loadClass(className);
    					mappers.add(clazz);
    				} catch (ClassNotFoundException e) {
    
    				}
    			}
    		}
    	}
    
    }
    
  7. 配置类

    package com.xiaoge.springmybatis;
    
    import com.xiaoge.springmybatis.annotation.XiaogeScan;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.ComponentScan;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    @ComponentScan
    @XiaogeScan("com.xiaoge.springmybatis.mapper")
    public class AppConfig {
    }
    
  8. 启动类

    package com.xiaoge.springmybatis;
    
    import com.xiaoge.springmybatis.mapper.UserMapper;
    import com.xiaoge.springmybatis.service.UserService;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    public class SpringIntegrationMybatisMain {
    	public static void main(String[] args) {
    
    		// 创建 ApplicationContext 容器
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    		UserService userService = applicationContext.getBean(UserService.class);
    		userService.test();
    
    		System.out.println(applicationContext.getBean("userMapper"));
    
    	}
    }
    
    
    // 执行结果
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatis$1@2aa5fe93
    $Proxy11
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatis$1@5c1a8622
    $Proxy12
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatis$1@5ad851c9
    $Proxy13
    selectById
    selectById
    selectById
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatis$1@2aa5fe93
    
mybatis-spring源码它是怎么整合的
  1. 先通过MapperScan找到对应的MapperScannerRegistrar

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUDGGZdm-1687513246718)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623171512324.png)]

  2. 主要看这个类的这个方法registerBeanDefinitions

    @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
          // 获取MapperScan注解相关信息, 就是MapperScan里面我们会配置路径什么啊等
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
         // 获取spring扫描器
         ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    
        // this check is needed in Spring 3.1
        if (resourceLoader != null) {
          scanner.setResourceLoader(resourceLoader);
        }
    
        // 解析MapperScan annotationClass 属性
        Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
        if (!Annotation.class.equals(annotationClass)) {
          scanner.setAnnotationClass(annotationClass);
        }
    
        // 解析MapperScan markerInterface 属性
        Class<?> markerInterface = annoAttrs.getClass("markerInterface");
        if (!Class.class.equals(markerInterface)) {
          scanner.setMarkerInterface(markerInterface);
        }
    
        // 解析MapperScan nameGenerator 属性
        Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
        if (!BeanNameGenerator.class.equals(generatorClass)) {
          scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
        }
    
        // 解析MapperScan factoryBean 属性
        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
        if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
          scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
        }
    
        // 解析MapperScan sqlSessionTemplateRef 属性
        scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
        // 解析MapperScan sqlSessionFactoryRef 属性
        scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
    
        // 添加扫描包路径
        List<String> basePackages = new ArrayList<String>();
        for (String pkg : annoAttrs.getStringArray("value")) {
          if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
          }
        }
        
        // 添加扫描包路径
        for (String pkg : annoAttrs.getStringArray("basePackages")) {
          if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
          }
        }
        // 添加class权限定类名
        for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
          basePackages.add(ClassUtils.getPackageName(clazz));
        }
        // 添加一些过滤器, 因为spring扫描到了也不一定注册到BeanDefinitionMap中它还需要通过过滤器条件
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(basePackages));
      }
    

    看doScan方法

    @Override
      public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 扫描该包, 拿到是所有的BeanDefinition
        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;
      }
    

    看processBeanDefinitions方法

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
        // 循环beanDefinitions
        for (BeanDefinitionHolder holder : beanDefinitions) {
          // 拿到对应的beanDefinitions
          definition = (GenericBeanDefinition) holder.getBeanDefinition();
    
          if (logger.isDebugEnabled()) {
            logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
              + "' and '" + definition.getBeanClassName() + "' mapperInterface");
          }
    
          // 给beanDefinition设置相关属性
          definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
          
          // 设置bean类型
          definition.setBeanClass(this.mapperFactoryBean.getClass());
    
          definition.getPropertyValues().add("addToConfig", this.addToConfig);
    
          boolean explicitFactoryUsed = false;
          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;
          }
    
          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) {
            if (logger.isDebugEnabled()) {
              logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            }
            // 设置它的依赖注入类型
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          }
        }
      }
    

    看mapperFactoryBean这个类,可以看到它跟我们写的那个差不多, 都是实现了FactoryBean,mapper都是class类型 private Class mapperInterface,看getObject方法getMapper它是怎么代理mapper的

    package org.mybatis.spring.mapper;
    
    import static org.springframework.util.Assert.notNull;
    
    import org.apache.ibatis.executor.ErrorContext;
    import org.apache.ibatis.session.Configuration;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.support.SqlSessionDaoSupport;
    import org.springframework.beans.factory.FactoryBean;
    
    /**
     * 可以看到它跟我们写的那个差不多, 都是实现了FactoryBean  
     * mapper都是class类型 private Class<T> mapperInterface;
     * 看getObject方法getMapper它是怎么代理mapper的
     */
    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
      private Class<T> mapperInterface;
    
      private boolean addToConfig = true;
    
      public MapperFactoryBean() {
        //intentionally empty 
      }
      
      public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      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) {
            logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
            throw new IllegalArgumentException(e);
          } finally {
            ErrorContext.instance().reset();
          }
        }
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public Class<T> getObjectType() {
        return this.mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public boolean isSingleton() {
        return true;
      }
    
      //------------- mutators --------------
    
      /**
       * Sets the mapper interface of the MyBatis mapper
       *
       * @param mapperInterface class of the interface
       */
      public void setMapperInterface(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      /**
       * Return the mapper interface of the MyBatis mapper
       *
       * @return class of the interface
       */
      public Class<T> getMapperInterface() {
        return mapperInterface;
      }
    
      /**
       * If addToConfig is false the mapper will not be added to MyBatis. This means
       * it must have been included in mybatis-config.xml.
       * <p/>
       * If it is true, the mapper will be added to MyBatis in the case it is not already
       * registered.
       * <p/>
       * By default addToCofig is true.
       *
       * @param addToConfig
       */
      public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
      }
    
      /**
       * Return the flag for addition into MyBatis config.
       *
       * @return true if the mapper will be added to MyBatis in the case it is not already
       * registered.
       */
      public boolean isAddToConfig() {
        return addToConfig;
      }
    }
    
    

    看getMapper

    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
    }
    

    看getMapper

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }
    

    这里就用了动态代理看newInstance

    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);
    }
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只因为你温柔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值