手写 Mybatis篇

首先 Mybatis 的工作原理

Mybatis 有四大对象,是一个责任链的设计模式,而Mybatis的工作原理就是这一条链。

第一个对象 Executor 判断应该执行哪一种方法,在Mybatis中只有两种方法 select/update

第二个对象 ParameterHandler 解析请求参数 #{} 会预编译对比数据库字段和请求参数类型,${}不会对比,如果不匹配则直接抛出异常

第三个对象 StatementHandler 数据库对象,拿到sql语句并执行,分页插件就是重写了这条sql。

第四个对象 ResultSetHandler 映射结果集对象。

mybatis 是如何将接口交给spring管理的。最重要的一步。

比如 我们在自己的Spring MVC中整合并写了一个mapper 的接口

手写Spring MVC 地址:https://gitee.com/wanganfen/xx-spring/tree/master/simple-mvc

@XxMapper
@XxNameSpace("userMapper")
public interface UserMapper {

    @XxSelect(value = "select * from user_bo where id = #{id} and user_age = #{age}",resultType = "com.waf.entity.User")
    List<User> findSysUserById(@XxParam("id") String id,@XxParam("age") Integer age);

    @XxSelect(value = "select * from user_bo where id = ${id} and user_age = ${age}",resultType = "com.waf.entity.User")
    List<User> findSysUserById2(@XxParam("id") String id,@XxParam("age") Integer age);
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface XxMapperScanners {

    String[] value();
}

配置类的XxMapperScanners 注解会指定扫描路径下面被XxMapper注解修饰的接口

拿到这一批接口的class,我们在前面的文章中提到了注册BeanDefinition有一个很重要的属性就是beanClass。但是这个beanClass是一个接口类型的,无法通过反射去实例化

那么如何将接口交给Spring 容器管理呢。

接口->jdk动态代理->代理对象->Spring Bean

Spring提供了一个接口 叫FactoryBean(源码里面使用的是构造方法注入,这边使用的是属性注入的方式)

public class XxMapperProxyFactory<T> implements XxFactoryBean<T>, InvocationHandler {

    @XxManualwired
    private Class<T> clazz;

    private static final List<XxExecutor> XX_EXECUTORS = new ArrayList<>();

    static {
        XX_EXECUTORS.add(new XxExecutorSelect());
        XX_EXECUTORS.add(new XxExecutorUpdate());
    }

    public Class<T> getClazz() {
        return clazz;
    }

    public void setClazz(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public T getObject() throws Exception {
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz},this);
    }

    @Override
    public Class<T> getObjectType() {
        return clazz;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args){
        try {
            Class<?>[] interfaces = proxy.getClass().getInterfaces();
            Method[] declaredMethods = interfaces[0].getDeclaredMethods();
            Optional<Method> optional = Arrays.stream(declaredMethods).filter(x -> compareMethodIsIdentical(x, method)).findFirst();
            if(optional.isPresent()){
                return doExecutors(optional.get(),args);
            }
            return null;
        }catch (Exception e){

        }
        return null;
    }

代理对象就生成出来了

那么beanDefinition 如何注册进去呢

Spring 也有一个接口 叫 ImportBeanDefinitionRegistrar 源码

public interface ImportBeanDefinitionRegistrar {

	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 */
	void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

提供了一个注册器,当然我们也可以写一个XxImportBeanDefinitionRegistrar 接口,而且beanClass 就是使用的FactoryBean ,

public class XxMapperScannerRegistry implements XxImportBeanDefinitionRegistrar{

    private static final XxClassPathBeanDefinitionScanner scanner = new XxClassPathBeanDefinitionScanner();

    private static List<Class<?>> classList = new ArrayList<>();

    @Override
    public void registerBeanDefinitions(XxBeanDefinitionRegistry registry) {
        XxDefaultListableBeanFactory beanFactory = (XxDefaultListableBeanFactory) registry;
        for (String beanName : beanFactory.getXxBeanDefinitionMaps().keySet()) {
            XxBeanDefinition xxBeanDefinition = beanFactory.getBeanDefinition(beanName);
            if(xxBeanDefinition.getBeanClass().isAnnotationPresent(XxMapperScanners.class)){
                XxMapperScanners xxMapperScanners = (XxMapperScanners)xxBeanDefinition.getBeanClass().getAnnotation(XxMapperScanners.class);
                doScanner(xxMapperScanners.value());
            }
        }
        if(CollectionUtils.isNotEmpty(classList)){
            classList.forEach(clazz->{
                XxBeanDefinition beanDefinition = new XxBeanDefinition();
                beanDefinition.setBeanClass(XxMapperProxyFactory.class);
                beanDefinition.setObjectField(clazz);
                registry.registerBeanDefinition(GenerateBeanNameUtils.generateBeanName(clazz.getSimpleName()),beanDefinition);
            });
        }
    }


    public void doScanner(String[] value){
        List<Class<?>> classes = scanner.loadResources(value);
        classList.addAll(classes.stream().filter(x->x.isAnnotationPresent(XxMapper.class)).distinct().collect(Collectors.toList()));
    }
}

然后将这两个XxMapperScannerRegistry ,XxImportBeanDefinitionRegistrar 注入到Spring MVC的配置类中 @XxImport({XxMapperScannerRegistry.class,XxMapperProxyFactory.class})

beanDefinition.setObjectField(clazz); 这个set的属性就是我们传进去的mapper接口

@XxManualwired
private Class<T> clazz;

然后属性注入 (Spring的生命周期第三步),这样一个代理对象就交给Spring容器管理了。

    private Object handleXxMangaloreAnnotation(String beanName, Object instance) {
        try {
            XxBeanDefinition xxBeanDefinition = beanFactory.getXxBeanDefinitionMaps().get(beanName);
            if(Objects.nonNull(xxBeanDefinition.getObjectField())){
                Field[] fields = instance.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if(field.isAnnotationPresent(XxManualwired.class)){
                        field.setAccessible(true);
                        field.set(instance,xxBeanDefinition.getObjectField());
                    }
                }
            }
        } catch (Exception ignored) {
            return instance;
        }
        return instance;
    }

然后就开始在XxMapperProxyFactory invoke方法里面写处理sql的逻辑了

就是四大对象的执行过程了。

手写 Mybatis 的gitee地址:https://gitee.com/wanganfen/xx-spring/tree/master/simple-mybatis

映射结果集的时候,因为@select 注解里面有个 resultType 属性,当整合到Spring MVC的时候,

这个属性里面的对象是可以直接load到内存中去的,这个对象里面的属性加了一个注解,注解的值就是数据库的字段,通过循环结果集,在判断注解值是否匹配即可完成最终的映射,如果返回的是一个对象,一条数据直接循环resultType 属性对象的字段去匹配,如果返回的是一个集合,就在循环的结果集里面去多次 生成resultType 属性对象 在去匹配字段,封装结果集。其实查询就只有两种selectOne 和 selectList 。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值