简单模拟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)
上一篇 2022年5月6日 下午10:40
下一篇 2022年5月6日 下午10:40


相关推荐

  • 大数据数据建模[通俗易懂]

    大数据数据建模[通俗易懂]今天给大家分享一下 数据开发工作中数据建模的步骤,                           第一步:选择模型或者自定义模型这第一步需要我们基于业务问题,来决定我们需要选择哪种模型,目前市场中有很多模型可以供我们选择,比如,如果要预测产品销量,则可以选择数值预测模型(比如回归模型,时序预测……);如果要预测员工是否离职,则可…

    2022年5月3日
    102
  • linux下如何保存退出vim编辑器

    linux下如何保存退出vim编辑器命令:vimapp.py如果不存在app.py则会自动创建1.进入编辑器后按字母“i”即可进入编辑状态(此时左下角会出现 “插入”)2.退出的时候分为4种情况:保存退出、正常退出、不保存退出以及强制退出 2.1:保存退出:按“Esc”键后此时的“插入”会消失,然后按Shift+zz就可以保存修改内容并退出 2.2:不保存退出:当修改修改了一部分内容后发现修改错了,此时就会进行不保存退…

    2022年6月3日
    242
  • 一、什么是 GPT-5 Thinking 模式?

    一、什么是 GPT-5 Thinking 模式?

    2026年3月16日
    2
  • JS对象与JSON字符串之间的转换

    JS对象与JSON字符串之间的转换JSON JS 中的对象只有 JS 自己认识 其他的语言都不认识 JSON 就是一个特殊格式的字符串 这个字符串可以被任意的语言所识别 并且可以转换为任意语言中的对象 JSON 在开发中主要用来数据的交互 JSON 和 JS 对象的格式一样 只不过 JSON 字符串中的属性名必须加双引号其他的和 JS 语法一致 JSON 分类 1 对象 2 数组 JSON 中允许的值 1 字符串 2 数值 3 布尔值 4 null5 对象 6 数组将 JSON 字符串转换为 JS 中的对象在 JS

    2026年3月19日
    1
  • gradle下载太慢_苹果6网络慢怎么解决

    gradle下载太慢_苹果6网络慢怎么解决由于网络的原因,有些地方连接下载Gradle没问题,有些地方就不行,所以需要想办法解决先说下载,打开文件gradle-wrapper.properties文件,修改distributionUrl把services.gradle.org改成downloads.gradle-dn.com当然这方法可能有些地方不行,实在不行就上代理吧再说Gradle下载jar包慢,在build.gradle文件的repositories{}内添加上阿里的仓库当然,如果添加了阿里仓库还不行的话只能找其他方

    2025年7月31日
    4
  • java怎么输出保留两位小数_剖析Java输出怎么保留两位小数「建议收藏」

    java怎么输出保留两位小数_剖析Java输出怎么保留两位小数「建议收藏」Java中,当两个整数相除时,由于小数点以后的数字会被截断,运算结果将为整数,此时若希望得到运算结果为浮点数,必须将两整数其一或是两者都强制转换为浮点数,也就是Java输出怎么保留两位小数?接下来郑州达内Java培训老师给大家以实例说明:Java怎么取两位小数?题目:项目中有一个小需求,两个整数相除,结果需要保留两位小数,即1.00、0.50这种数据格式。以下做法不行,因为两整数相除,小数点以后的…

    2022年7月8日
    21

发表回复

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

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