咱們分析的時候先本身猜想實現方式再對比mybatis的源碼實現方式spring
咱們本身實現mapper建立工廠代理類:sql
public class MySessionFactoryProxy {
public static Object getMapper(Class c){
Class[] classes = new Class[]{c};
//動態代理獲取mapper
Object o = Proxy.newProxyInstance(MySessionFactoryProxy.class.getClassLoader(), classes, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//解析sql
//執行sql
Select annotation = method.getAnnotation(Select.class);
String sql = annotation.value()[0];//一個註解可能有多個sql語句
System.out.println("sql:"+sql);
return null;
}
});
return o;
}
}複製代碼
那麼由誰來調用這個getMapper方法呢,毫無疑問是mybatis,這個時候須要一個工廠bean,用來調用該方法,每次調用,建立一個factoryBean,傳入一個mapper類,來建立該mapper(這樣就能夠解決代碼寫死的狀況)springboot
public class MyMapperFactoryBean<T> implements FactoryBean<T> {
//實例化的時候傳入
public MyMapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
//使用全局變量存儲不一樣的mapper類
private Class<T> mapperInterface;
public T getObject() throws Exception {
System.out.println("get mapper");
return (T) MySessionFactoryProxy.getMapper(mapperInterface);
}
public Class<?> getObjectType() {
return this.mapperInterface;
}
}複製代碼
再看mybatis的實現mybatis
public class MapperRegistry {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//主要調用
return mapperProxyFactory.newInstance(sqlSession);
}複製代碼
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
//主要方法以下
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}複製代碼
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
public Class<T> getObjectType() {
return this.mapperInterface;
}
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
}
複製代碼
與咱們建立的大致一致app
建立完成mapper後,咱們須要把mapper交給ioc管理函數
請區別
添加註解或者配置,將類交個Spring管理,由Spring爲咱們建立對象
,mapper是由mybatis經過動態代理建立的源碼分析
本身建立對象交給Spring管理測試
@Configuration
@ComponentScan("top.dzou.mybatis")
public class Appconfig {
@Bean
public UserMapper userMapper(){
return (UserMapper) MySessionFactoryProxy.getMapper(UserMapper.class);
}
}複製代碼
每個都要建立一個@bean註解,不切實際,想一想Spring怎麼實現的?ui
相似下面寫法:this
使用咱們本身的mapper工廠代理類建立mapper
<bean id="roleMapper" class="top.dzou.mybatis.mapper_to_spring.my_mybatis.MyMapperFactoryBean">
<property name="mapperInterface" value="top.dzou.mybatis.mapper_to_spring.RoleMapper"/>
</bean>
<bean id="userMapper" class="top.dzou.mybatis.mapper_to_spring.my_mybatis.MyMapperFactoryBean">
<property name="mapperInterface" value="top.dzou.mybatis.mapper_to_spring.UserMapper"/>
</bean>
</beans>複製代碼
這樣的代碼是否是很熟悉,可是這樣任然要配置不少的bean,咱們想到了springboot中使用的mapperscan註解
MyMapperScan
,包括一個包,在MapperScan註解上導入mapper導入類,把包下面的所有建立並放到spring中 這裏咱們須要知道的是Spring建立bean的時候是先加載類並建立BeanDefinition
,在經過BeanDefinition建立相應的bean,咱們由於mapper
咱們本身實現:
根據basePackages導入mapper
//導入ImportBeanDefinitionRegister
@Import(MyBeanDefinitionRegister.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMapperScan {
String[] basePackages() default {};
}
複製代碼
使用Spring註冊bean時使用的ImportBeanDefinitionRegistrar註冊mapper
public class MyBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
//class->beanDefinition->map->bean
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//獲取包信息並把包中類所有註冊動態添加到beanDefinition參數中
{
//僞代碼
basePackages = 獲取包名下的所用mapper類,保存到集合basePackages
baseName = 從mapper類獲取beanName
}
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MyMapperFactoryBean.class);
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(basePackages);
beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition);
}
}複製代碼
@Configuration
@ComponentScan("top.dzou.mybatis.mapper_to_spring")
@MyMapperScan(basePackages = "top.dzou.mapper_to_spring.mapper")//自定義mapper掃描註解
public class Appconfig {}複製代碼
咱們看一下mybatis怎麼實現的
它導入了一個MapperScannerRegistrar
掃描註冊mapper的類
@Import({MapperScannerRegistrar.class})
public @interface MapperScan {複製代碼
咱們能夠看到它實現了ImportBeanDefinitionRegistrar
的bean定義導入註冊類,實現了具體的registerBeanDefinitions
註冊bean定義的方法,把包下的mapper所有添加到一個集合中,而後把這個集合進行註冊到ioc中,和咱們的想法基本一致
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
List<String> basePackages = new ArrayList();
basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll((Collection)Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}複製代碼
它使用一個集合保存了全部的mapper類,並把他們放在一個beanDefinition中進行註冊
整個過程主要分爲
重點: