简单模拟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、输出结果
在这里插入图片描述

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(1)


相关推荐

  • Semantic Drone Dataset(语义无人机数据集)使用方法「建议收藏」

    Semantic Drone Dataset(语义无人机数据集)使用方法「建议收藏」数据集介绍SemanticDroneDataset数据集下载地址该语义无人机数据集专注于城市场景的语义理解,以提高无人机自主飞行和着陆程序的安全性。该图像描绘了在距地面5至30米的高度从最低点(鸟瞰)视角拍摄的20多座房屋。高分辨率相机用于获取尺寸为6000x4000px(24Mpx)的图像。训练集包含400张公开可用的图像,测试集包含200张私有图像。该语义分割数据集包括20个种类:树、草、其他植被、污垢、碎石岩石、水、铺砌面积、水池、人狗、车、自行车、屋顶、墙

  • 神经网络BP反向传播算法原理和详细推导流程

    神经网络BP反向传播算法原理和详细推导流程1反向传播算法和BP网络简介误差反向传播算法简称反向传播算法(即BP算法)。使用反向传播算法的多层感知器又称为BP神经网络。BP算法是一个迭代算法,它的基本思想为:(1)先计算每一层的状态和激活值,直到最后一层(即信号是前向传播的);(2)计算每一层的误差,误差的计算过程是从最后一层向前推进的(这就是反向传播算法名字的由来);(3)更新参数(目标是误差变小)。迭代前面两个步骤,直到满足…

  • 函数去抖(debounce)& 函数节流(throttle)总结

    函数去抖(debounce)& 函数节流(throttle)总结//todo

  • Java—java.util.calendar类详解「建议收藏」

    Java—java.util.calendar类详解「建议收藏」目录一、概述二、静态常量三、静态方法四、实例五、GregorianCalendar类一、概述java.util.Calendar类是一个抽象类,是java日期处理的核心类之一。Calendar类为操作日历字段,及其与特定瞬间之间的转换提供了方法。日历字段包含YEAR、MONTH、DAY_OF_MONTH、HOUR等,它们都是Calendar类的静态常量。二、静态常量…

  • Spring Data JPA 之 JpaRepository

    Spring Data JPA 之 JpaRepositoryJpaRepository是Spring提供的非常强大的基本接口。1JpaRepository1.1JpaRepository接口定义JpaRepository接口的官方定义如下:publicinterfaceJpaRepository&lt;T,ID&gt;extendsPagingAndSortingRepository&lt;T,ID&gt;,Q…

    2022年10月20日

发表回复

您的电子邮箱地址不会被公开。

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