简单模拟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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • idea2021 激活码_在线激活

    (idea2021 激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~ML…

    2022年3月21日
    46
  • 秦九韶计算多项式的方法_秦九韶

    秦九韶计算多项式的方法_秦九韶本人水平有限,题解不到为处,请多多谅解本蒟蒻谢谢大家观看秦九韶公式:可不断的提取x作公因式,写成如上形式不断将x=4由里往外扩展,大大方便了计算

    2022年8月3日
    4
  • 中缀表达式转后缀表达式(非常简单易懂)

    中缀表达式转后缀表达式(非常简单易懂)前言数据结构与算法中经常遇到中缀表达式转前缀表达式的题目 网上的教程大都很不直观 自己学的时候 也走了很多弯路 现在把一个简单易懂的算法教程分享出来 中缀转后缀举个例子 一个式子 5 20 1 3 14 5 20 1 3 14 5 20 1 3 14 如何把该式子转换成后缀表达式呢 其实就是分三步 1 按运算符优先级对所有运算符和它的运算数加括号 原本的括号不用加 2 把运算

    2025年11月21日
    2
  • navicat 中文注册码「建议收藏」

    navicat 中文注册码「建议收藏」中文版Navicatmysql9.x下载地址:http://download2.navicat.com/download/navicat091_mysql_cs.exe下载地址:http://download2.navicat.com/download/navicat091_mysql_cs.tar.gz注册码:NAVL-KSG4-K8D8-8TV6中文版Navicatm…

    2022年10月13日
    3
  • linux常用命令汇总_unix命令大全

    linux常用命令汇总_unix命令大全目录一、进程管理二、系统信息三、关机(系统的关机、重启以及登出)四、文件和目录五、文件搜索六、挂载一个文件系统七、磁盘空间八、系统负载–top九、用户和群组十、文件的权限-使用”+”设置权限,使用”-“用于取消十一、文件的特殊属性-使用”+”设置权限,使用”-“用于取消十二、打包和压缩文件十三、OS包管理器①、RPM包-(Fedora,Redhat及类似系统)②、YUM软件包升级………

    2022年9月23日
    4
  • Java正则表达式替换所有特殊字符

    Java正则表达式替换所有特殊字符Java正则表达式替换所有特殊字符 /** *正则替换所有特殊字符 *@paramorgStr *@return */ publicstaticStringreplaceSpecStr(StringorgStr){ if(null!=orgStr&&!””.equals(orgStr.trim())){ StringregEx=”[\\

    2022年5月16日
    61

发表回复

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

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