手写mybatis-spring

一、回顾一下Spring整合Mybatis的正常流程

1.导入以下的依赖

     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>5.1.11.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.3</version>
     </dependency>
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis-spring</artifactId>
         <version>2.0.7</version>
      </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-jdbc</artifactId>
         <version>5.1.11.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.46</version>
     </dependency>

2.MyBatis的配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 配置数据库信息 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mydb?allowMultiQueries=true"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

</configuration>

3.Spring的配置类

这里大家思考一下!为什么要将SqlSessionFactory注入到容器中?

@ComponentScan("com.lzl")
@Configuration
@MapperScan("com.lzl.mapper")
public class MainConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        return sessionFactory;
    }


}

4.Mapper接口

public interface OrderMapper {

    @Select("select 'order'; ")
    String getOrder();

}

5.Service

@Service
public class MyService {
    @Autowired
    private OrderMapper orderMapper;

    public void test(){
        System.out.println(orderMapper.getOrder());
    }
}

6.测试类

    public static void main(String[] args) {
        AbstractApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        MyService bean = context.getBean(MyService.class);
        bean.test();
    }

以上代码是能够正常运行的,但如果去掉mybatis-spring依赖提供的@MapperScan注解,就会导致service中依赖的OrderMapper注入失败,程序就会发生报错。而本文的目的就是手写mybatis-spring的功能,并也提供一个类似@MapperScan的注解来完成spring对Mybatis的接入。从而使我们更加的清楚为什么Spring接入MyBatis需要将SqlSessionFactory 注到IOC中。

二、正文

首先我们要移除掉上面项目中的mybatis-spring依赖 和 配置类上的@MapperScan注解,避免它对我们编写造成影响!

1.书写下面的FactoryBean

//无需任何注解
public class LuzelongFactoryBean implements FactoryBean {
    private SqlSession session;

    private Class mapperClass;

    public LuzelongFactoryBean(Class mapperClass){
        this.mapperClass = mapperClass;
    }

    @Autowired
    public void setSession(SqlSessionFactory sqlSessionFactory) {
        sqlSessionFactory.getConfiguration().addMapper(mapperClass);
        this.session = sqlSessionFactory.openSession();
    }

    @Override
    public Object getObject() throws Exception {
        return session.getMapper(mapperClass);
    }

    @Override
    public Class<?> getObjectType() {
        return mapperClass;
    }
}

2.书写下面的Register

将上面的FactoryBean注册进BeanDefinition中,通过构造方法完成上面mapperClass字段的赋值操作。

//这个类比较特殊,只能通过@Import进行导入
public class LuzelongRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition.setBeanClass(LuzelongFactoryBean.class);
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
        beanDefinitionRegistry.registerBeanDefinition("orderMapper",beanDefinition);

		//当有其他Mapper需要注入时,需要复制上面的逻辑
        AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition1.setBeanClass(LuzelongFactoryBean.class);
        beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
        beanDefinitionRegistry.registerBeanDefinition("userMapper",beanDefinition1);
    }
}

3.在配置类上面加上@Import注解,导入上面的Register

@ComponentScan("com.lzl")
@Configuration
@Import(LuzelongRegister.class)
public class MainConfig {
.....
}

通过这样的操作,代码也能正常的运行!但我们发现一旦添加新的Mapper,我们就需要在LuzelongRegister类中对registerBeanDefinitions方法进行修改。解决办法请看下面!

三、正文2

1.创造LuzelongScan注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(LuzelongRegister.class)
public @interface LuzelongScan {
	String value() default "";
}

2.配置类修改

因为@LuzelongScan注解上面有@Import,所以我们可以删掉配置类上的@Import注解,加上了@LuzelongScan注解即可!

@ComponentScan("com.lzl")
@Configuration
@LuzelongScan("com.lzl.mapper")
public class MainConfig {
.....
}

3.继承Spring提供的扫描类,快速完成扫描逻辑

public class LuzelongScanner extends ClassPathBeanDefinitionScanner {

    public LuzelongScanner(BeanDefinitionRegistry registry){
        super(registry);
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface();
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
            BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
            beanDefinition.setBeanClassName(LuzelongFactoryBean.class.getName());
        }
        return beanDefinitionHolders;
    }
}

4.修改LuzelongRegister 的方法逻辑

//这个类比较特殊,只能通过@Import进行导入
public class LuzelongRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(LuzelongScan.class.getName());
        //获取@LuzelongScan的扫描路径
        String path = (String)annotationAttributes.get("value");
//        System.out.println(path);

        LuzelongScanner luzelongScanner = new LuzelongScanner(beanDefinitionRegistry);
        luzelongScanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                return true;
            }
        });
        luzelongScanner.scan(path);
    }
}

通过上述操作!即完成了@MapperScan注解提供的所以有功能!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

键盘歌唱家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值