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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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:[email protected]">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(SpringIntegrationMybatisTwo.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:[email protected]">Zhang Xiao</a>
     * @since
     */
    public class XiaogeBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
          
          
    
    	@Override
    	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
          
          
    		// 获取我们扫描注解上面的路径
    		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 SpringIntegrationMybatisTwo(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:[email protected]">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:[email protected]">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.SpringIntegrationMybatisTwo$1@2aa5fe93
    $Proxy11
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@5c1a8622
    $Proxy12
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$1@5ad851c9
    $Proxy13
    selectById
    selectById
    selectById
    toString
    com.xiaoge.springmybatis.springintegrationmybatis.SpringIntegrationMybatisTwo$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);
    }
    

猜你喜欢

转载自blog.csdn.net/zsx1314lovezyf/article/details/131352561
今日推荐