简单模拟mybatis的MapperScan

简单模拟mybatis的MapperScan一、问题描述在mybatis中,mapper通常是一个接口,但是我们却可以直接通过这个接口调用方法。按道理来说接口是不能直接调用方法的,只有实现类才能调用接口。但在下面的代码中,我们直接调用applicationContext.getBean(TestMapper.class).list(“”),就可以查询我们的数据库。也就是说applicationContext.getBean(TestMa…

大家好,又见面了,我是你们的朋友全栈君。

一、问题描述

在mybatis中,mapper通常是一个接口,但是我们却可以直接通过这个接口调用方法。按道理来说接口是不能直接调用方法的,只有实现类才能调用接口。但在下面的代码中,我们直接调用applicationContext.getBean(TestMapper.class).list(“”),就可以查询我们的数据库。
也就是说applicationContext.getBean(TestMapper.class)拿到的是一个代理对象并存在spring容器中,只不过这些都由@MapperScan这个注解帮我们实现了。接下来我们自己写一个简单的scan,拿到sql语句。

public interface TestMapper {
    @Select("SELECT * FROM test")
    public List<Map<String,Object>> list(String str);
}
public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        applicationContext.getBean(TestMapper.class).list("");
}
@Configuration
@ComponentScan("com.stay")
@MapperScan("com.stay.dao")
public class AppConfig {
    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("xxx");
        dataSource.setUrl("xxx");
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setLogImpl(Log4jImpl.class);
        sqlSessionFactoryBean.setConfiguration(configuration);
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
}

在这里插入图片描述

二、分析

第一步:将TestMapper接口变成一个对象。
第二步:这个对象必须实现了TestMapper接口。
第三步:把这个对象放入spring容器中。
第一、二步可以使用我们的动态代理,jdk动态代理基于接口,符合我们的需求。
第三步需要把我们的动态代理对象放到spring容器中,这里需要把一个class转成bd,再注册到spring容器中。
1、如果将TestMapper直接注入的话,spring容器是创建不出来的,它是一个接口。
2、怎样才能把我们生成的动态代理给注册到spring容器中。
这里我们通过实现ImportBeanDefinitionRegistrar接口,动态注册给spring,并修改beanDefinition的BeanClass让它变成一个factoryBean,在getObject方法中,返回我们的代理对象。(这里有个知识点FactoryBean,大家可以去了解下)。

三、代码实现

1、自定义注解TestScan

@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface TestScan {
}

2、实现ImportBeanDefinitionRegistrar接口,动态注册beanDefinition到spring容器。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TestMapper.class);
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition)builder.getBeanDefinition();
        //因为factoryBean.getObject方法返回的是代理对象,所以我们需要将TestMapper.class传进去
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(TestMapper.class.getName());
        //动态修改beanClass为FactoryBean
        beanDefinition.setBeanClass(MyFactoryBean.class);
        //将beanDefinition注册到spring容器中
        registry.registerBeanDefinition("testMapper",beanDefinition);
    }
}

3、实现FactoryBean和InvocationHandler接口。FactoryBean实际会创建2个bean,一个是MyFactoryBean对象本身,通过&+beanName来获取,一个是getObject返回的对象,通过当前的beanName来获取。而当我们执行testMapper的list方法的时候,实际上是执行了实现InvocationHandler 接口的invoke方法。

public class MyFactoryBean implements FactoryBean,InvocationHandler {

    Class clazz;
    public MyFactoryBean(Class clazz){
        this.clazz = clazz;
    }

    public Object getObject() throws Exception {
        Class[] clazzs = new Class[]{clazz};
        //jdk动态代理
        Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), clazzs, this);
        return proxy;
    }

    public Class<?> getObjectType() {
        return clazz;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy");
        Method method1 = proxy.getClass().getInterfaces()[0].getMethod(method.getName(), String.class);
        Select select = method1.getDeclaredAnnotation(Select.class);
        //打印sql语句
        System.out.println(select.value()[0]);
        System.out.println("执行sql查询.....");
        return null;
    }
}

4、将MapperScan换成TestScan

@Configuration
@ComponentScan("com.stay")
@TestScan
public class AppConfig {

    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("xxx");
        dataSource.setUrl("xxx");
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setLogImpl(Log4jImpl.class);
        sqlSessionFactoryBean.setConfiguration(configuration);
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
}

5、输出结果
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/138914.html原文链接:https://javaforall.net

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Ubuntu18.04搭建源码搜索引擎Opengrok

    Ubuntu18.04搭建源码搜索引擎OpengrokTableofContents1OpenGrok介绍2安装OpenGrok2.1安装JAVA运行环境2.2安装Web服务器-Tomcat2.3安装OpenGrok2.4配置OpenGrok2.5安装 universal-ctags2.6建立源码索引2.6更新源码索引1OpenGrok介绍OpenGrok isafastand…

    2022年4月29日
    76
  • TimSort算法相关

    TimSort算法相关今天看 Java 的 Collection sort 的源码 发现采用的是 TimSort sort 就在网上查了下 发现一个不错的文章 原链接如下 http www freebuf com vuls 62129 html nbsp nbsp 如何找出 Timsort 算法和玉兔月球车中的 Bug nbsp 0 00 背景形式化方法 FormalMethod 在我们一般人眼中是非常高大上的东西

    2025年6月26日
    1
  • 如何传输大文件(GB级别)

    如何传输大文件(GB级别)一、拆分:压缩工具,压缩并拆分为多个小文件。二、QQ离线传输QQ离线文件有限制条件:1.离线传送的文件,为用户保存7天,逾期接收方不接收文件,系统将自动删除该文件;2.离线传送的文件,单个文

    2022年7月4日
    19
  • yii2的加密解密那些事儿

    yii2的加密解密那些事儿

    2022年4月3日
    36
  • Redis布隆过滤器原理与实践

    Redis布隆过滤器原理与实践背景在高并发请求时,业务数据一般会对数据进行缓存,提高系统并发量,因为磁盘IO和网络IO相对于内存IO的成百上千倍的性能劣势。做个简单计算,如果我们需要某个数据,该数据从数据库磁盘读出来需要0.1s,从交换机传过来需要0.05s,那么每个请求完成最少0.15s(当然,事实上磁盘和网络IO也没有这么慢,这里只是举例),该数据库服务器每秒只能响应67个请求;而如果该数据存在于本机内存里,读出来只需要10us,那么每秒钟能够响应100,000个请求。通过将高频使用的数据存在离cpu更近的位置,以减少数据传

    2022年10月7日
    0
  • git查看用户名和修改用户名「建议收藏」

    git查看用户名和修改用户名「建议收藏」在和同事共同完成一个项目时,上传代码,用户名就得注意下,不然搞个昵称,都不知道是谁上传的简单写下查看用户名和邮箱地址:$gitconfiguser.name$gitconfiguser.email修改用户名和邮箱地址$gitconfig–globaluser.name”xxxx”Sgitconfig–globaluser.emai…

    2022年4月19日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号