MyBatis和Spring整合,流程浅析 - 20181208

  • 【面试题】我们在开发mybaits时候,有几种写sql语句的方式?
    一种是注解、一种是xml文件、还有一种是@Provider
    注解形式:简单但是不是很灵活,对于动态条件查询是无法实现的,这时,我们可以使用xml的方式。
    xml形式:有xml文件,且约束严格。
    @Provider:结合上两种优点,注意:方法不能重载

  • 【面试题】我们都知道mybatis默认是有一级缓存的,但mybatis和spring整合后,一级缓存失效。

  • 如果想了解mybatis的源码,有两种方向,一个old(原生的),一个new(结合spring的),两者是完全不同的工作机制。这次我们从spring方面去接触源码,那么 spring和mybatis整合的时候,有哪几个关联点?主要有两点。
    @MapperScan
    @Bean ——>SqlSessionFactoryBean

  1. 先看@MapperScan
    通过源码得知MapperScan主要利用spring的Import技术和spring的扩展点ImportBeanDefinitionRegistear
    @Mapperscan里@Import(MapperScannerRegistrar.class),而MapperScannerRegistrar实现了关键类ImportBeanDefinitionRegistrar(是spring提供的一个扩展类,作用:[spring会先处理import,解析这个类,调用registerBeanDefinitions方法,产生springBean的注册器,就是bean的前身])

  2. @Bean

所以spring整合mybatis执行流程:

首先解析@Mapperscan,发现Import导入的类,执行类中的方法,这个类扩展了spring的扫描器,doScan方法,只扫描了所有接口。
之后将接口变成了对象:
1、首先拿到接口里的beanDefinitions对象(用来描述bean的,记录了bean的信息)
​ ps.spring创建对象跟接口无关,和bd有关
2、之后,接口无法直接产生对象,所以spring是产生MapperFactoryBean(FactoryBean对象),里边的getObject方法可以返回代理对象(动态代理),实现了接口
3、getObject方法中是:
​ getSqlSession().getMapper(this.mapperInterface) //生成mapper对象
​ ps.底层MapperProxy实现了jdk中动态代理的InvocationHandler,所以mapper.方法实际上就是调用了动态代理中的invoke方法(就是接口产生代理对象,然后调用的是invoke方法)
4、调用这个代理对象时,里边的invoke方法执行mapperMethod.execute方法,可以分别执行对应的CRUD操作。
5、那么为什么getMapper(this.mapperInterface)需要传接口呢?因为返回代理对象,所以是接口。
​ ​ 那么所有接口都变成了MapperFactoryBean对象后,又是如何区分的呢? definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
​ 在生成MapperFactoryBean对象的时候,立即在一参构造中设置了一个值,这样就有名称了
​ ps.扫描遍历过程中,defintion可以拿到每次传进来的信息,和beanDefinitions相关
6、之后循环拿到所有构造方法,筛出有参,根据有参来创建对应名称的MapperFactoryBean对象

那么我们实战一下看看,手写

  1. 依赖
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-contextartifactId>
      <version>5.0.9.RELEASEversion>
    dependency>
  1. 包结构
    MyBatis和Spring整合,流程浅析 - 20181208_第1张图片
  2. 类:UserMapper、App、MyScan
public interface UserMapper {
    public void query();
}
@Configuration
@ComponentScan("com.play")
@MyScan     //相当于Mybatis的@MapperScan
public class App {
}
/**
 * 模拟mybatis的@MapperScan
 * Created by Turing on 2018/12/8 16:12
 */
@Retention(RetentionPolicy.RUNTIME)
@Import(MyScanRegistar.class)   //类似MapperScanRegistar
public @interface MyScan {
}
  1. MyScanRegistar
public class MyScanRegistar implements ImportBeanDefinitionRegistrar {
    /**
     * 方法作用:扫描包
     * @param importingClassMetadata
     * @param registry
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //System.err.println("scan scan scan ...");
        //假设扫描出来!
        //1、把扫描出来的bd的beanClass改成FactoryBean
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserMapper.class);//暂不模拟扫描了,正常应该是List,直接拿到类
        //ps.spring不是根据你的类来创建对象的,而是通过build来创建对象的

        //2、得到beanDefinition
        GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) builder.getBeanDefinition();

        //4、里边的getObject方法可以返回代理对象(动态代理),实现了接口
        genericBeanDefinition.setBeanClass(MyFactoryBean.class);

        //3、利用registy,让spring帮助实例化,但是接口无法实例化【Failed to instantiate】,这时候需要第4步了
        registry.registerBeanDefinition("userMapper",genericBeanDefinition);

    }
}
  1. MyFactoryBean
/**
 * 当你get这个bean时,返回getObject中return的对象
 *
 * Created by Turing on 2018/12/8 16:26
 */
public class MyFactoryBean implements FactoryBean {
    /**
     * 当然,实际情况更复杂
     */
    @Override
    public Object getObject() throws Exception {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),new Class[]{
                UserMapper.class
        },new MyInvocationHandler());
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}
  1. MyInvocationHandler
public class MyInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("---------MyInvocationHandler----------");
        return null;
    }
}
  1. 测试
public class MyTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                =new AnnotationConfigApplicationContext(App.class);

        annotationConfigApplicationContext.start();

        //代理对象
        UserMapper userMapper = (UserMapper) annotationConfigApplicationContext.getBean("userMapper");

        userMapper.query(); //会执行InvocationHandler中的代码
        //结果为:---------MyInvocationHandler----------
    }
}
  • spring AOP的编码原理,亦是如此!!!Spring另一个扩展点是BeanFactoryPostProcessor

转载于:https://www.cnblogs.com/gospurs/p/10460642.html

你可能感兴趣的:(MyBatis和Spring整合,流程浅析 - 20181208)