MyBatis和Spring整合,流程淺析 - 20181208
-
【面試題】我們在開發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.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.9.RELEASE</version>
</dependency>
- 包結構
- 類: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 {
}
- 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