-
【面试题】我们在开发mybaits时候,有几种写sql语句的方式?
一种是注解、一种是xml文件、还有一种是@Provider
注解形式:简单但是不是很灵活,对于动态条件查询是无法实现的,这时,我们可以使用xml的方式。
xml形式:有xml文件,且约束严格。
@Provider:结合上两种优点,注意:方法不能重载 -
【面试题】我们都知道mybatis默认是有一级缓存的,但mybatis和spring整合后,一级缓存失效。
-
如果想了解mybatis的源码,有两种方向,一个old(原生的),一个new(结合spring的),两者是完全不同的工作机制。这次我们从spring方面去接触源码,那么 spring和mybatis整合的时候,有哪几个关联点?主要有两点。
@MapperScan
@Bean ——>SqlSessionFactoryBean
-
先看@MapperScan
通过源码得知MapperScan主要利用spring的Import技术
和spring的扩展点ImportBeanDefinitionRegistear
@Mapperscan里@Import(MapperScannerRegistrar.class),而MapperScannerRegistrar
实现了关键类ImportBeanDefinitionRegistrar
(是spring提供的一个扩展类,作用:[spring会先处理import,解析这个类,调用registerBeanDefinitions方法,产生springBean的注册器,就是bean的前身]) -
@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对象
那么我们实战一下看看,手写
- 依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.9.RELEASEversion>
dependency>
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 {
}
- 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);
}
}
- 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;
}
}
- MyInvocationHandler
public class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---------MyInvocationHandler----------");
return null;
}
}
- 测试
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