zoukankan      html  css  js  c++  java
  • 聊聊、手写Mybatis 注解配置方式

    导航:  

    聊聊、Mybatis API  

    聊聊、Mybatis XML 

    聊聊、Mybatis集成Spring XML 方式

    聊聊、Mybatis集成Spring 注解方式

    聊聊、手写Mybatis XML配置方式

    聊聊、手写Mybatis SpringBoot Starter

    在《聊聊、手写Mybatis XML配置方式》中聊了通过 XML配置方式 来实现 Mybatis,也聊到了 Mybatis 中用到的动态代理技术。我手动实现了 AccountMapperFactoryBean,但是有一个缺点,需要 XML 配置才可以用。类似 MapperFactoryBean。

    这篇文章主要是聊聊通过注解方式来手写 Mybatis。我们向 Spring 注册 Bean 的方式有很多种,这里会用到 ImportBeanDefinitionRegistrar,它就是今天的主角。

    自定义 AccountImportBeanDefinitionRegistrar  实现 ImportBeanDefinitionRegistrar 

    AccountImportBeanDefinitionRegistrar   


    package org.rockcode.factory;


    import org.rockcode.mappers.AccountMapper;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;

    public class AccountImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
      BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(AccountMapperFactoryBean.class);
      AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
      beanDefinition.getPropertyValues().add("mapperInterface", AccountMapper.class);
      beanDefinitionRegistry.registerBeanDefinition("accountMapperFactoryBean",beanDefinition);
    }
    }

    AccountConfig 


    package org.rockcode.config; 

    import com.alibaba.druid.pool.DruidDataSource;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.rockcode.factory.AccountImportBeanDefinitionRegistrar;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;

    import javax.sql.DataSource;  

    @Configuration
    @Import(AccountImportBeanDefinitionRegistrar.class)
    public class AccountConfig { 

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);
    return sqlSessionFactoryBean;
    } 

    @Bean
    public DataSource dataSource(){
    DruidDataSource driverManagerDataSource = new DruidDataSource();
    driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
    driverManagerDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?useSSL=false");
    driverManagerDataSource.setUsername("root");
    driverManagerDataSource.setPassword("root");
    return driverManagerDataSource;
    } 

    }

    AccountMapperFactoryBean 


    package org.rockcode.factory;


    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.support.SqlSessionDaoSupport;
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import static org.springframework.util.Assert.notNull;

    public class AccountMapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean {

    private Class mapperInterface;

    public void setMapperInterface(Class mapperInterface) {
    this.mapperInterface = mapperInterface;
    }

    @Autowired
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    super.setSqlSessionFactory(sqlSessionFactory);
    }

    @Override
    protected void checkDaoConfig() {
    super.checkDaoConfig();
    notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    Configuration configuration = getSqlSession().getConfiguration();
    configuration.addMapper(this.mapperInterface);
    }

    @Override
    public Object getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
    }

    @Override
    public Class<?> getObjectType() {
    return this.mapperInterface;
    }
    }  

    Main方法 


    package org.rockcode.config; 

    import org.rockcode.mappers.AccountMapper;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;

    public class Main {
    public static void main(String[] args) {
      AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AccountConfig.class);
      AccountMapper accountMapper = (AccountMapper) ac.getBean("accountMapperFactoryBean");
      System.out.println(accountMapper.queryAll());
    }
    } 

    上面代码的重点在于  @Import(AccountImportBeanDefinitionRegistrar.class),这是 Spring 很重要的一个扩展点,与 ImportBeanDefinitionRegistrar 相同的扩展点还有 ImportSelector,它们都可以实现 Bean 的注册。 

    但是这里有一个不足,没有实现包扫描功能,在《聊聊、Mybatis集成Spring XML方式》里聊到了

     <mybatis:scan base-package="org.rockcode.mappers" /> 

    或者

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="basePackage" value="org.rockcode.mappers" />
    </bean>

    既然是注解方式,上面这两种肯定不行,那就实现自己的 @MapperScan 吧

    @AccountMapperScan 


    package org.rockcode.annotations;


    import org.rockcode.factory.AccountImportBeanDefinitionRegistrar;
    import org.springframework.context.annotation.Import;
    import java.lang.annotation.*;

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(AccountImportBeanDefinitionRegistrar.class)
    public @interface AccountMapperScan {
      String[] value() default {};
      String[] basePackages() default {};
    }

    修改上面的 AccountConfig 

    AccountConfig 


    内容和上面一样,只需要修改成 

    @Configuration
    @AccountMapperScan("org.rockcode.mappers")
    public class AccountConfig { }

    实现自己的 AccountClassPathMapperScanner 

    AccountClassPathMapperScanner 


    public class AccountClassPathMapperScanner extends ClassPathBeanDefinitionScanner {


    public AccountClassPathMapperScanner(BeanDefinitionRegistry registry) {
      super(registry, false);
    }

    public void registerFilters() {
      addIncludeFilter(new TypeFilter() {
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return true;
        }
      });
    }

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
      Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
      GenericBeanDefinition definition;
      for (BeanDefinitionHolder holder : beanDefinitionHolders) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();// 获取到 org.rockcode.mappers.AccountMapper 名称
      try {
      Class<?> clazz = Class.forName(beanClassName);// 获取到 AccountMapper.class
      definition.setBeanClass(AccountMapperFactoryBean.class); // 这里注意了,beanClass 设置为 AccountMapperFactoryBean
      definition.getPropertyValues().add("mapperInterface", clazz);// 注册 mapperInterface 属性
      } catch (ClassNotFoundException e) {
      e.printStackTrace();
      }
      }
    return beanDefinitionHolders;
    }

    //这个方法非常重要,不然是获取不到 AccountMapper 接口的  BeanDefinitionHolder
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
      return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }
    }

      

    修改上面的 AccountImportBeanDefinitionRegistrar

    AccountImportBeanDefinitionRegistrar


    public class AccountImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {


    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
      Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(AccountMapperScan.class.getName());
      String[] value = (String[]) annotationAttributes.get("value");
      String packages = value[0]; //获取到 @AccountMapperScan("org.rockcode.mappers") 中的 value,也就是 org.rockcode.mappers

      // 下面就是扫描包
      AccountClassPathMapperScanner myClassPathBeanDefinitionScanner = new AccountClassPathMapperScanner(beanDefinitionRegistry);
      myClassPathBeanDefinitionScanner.registerFilters();
      myClassPathBeanDefinitionScanner.scan(packages);
    }
    }

    Main方法 


    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AccountConfig.class);
    AccountMapper accountMapper = (AccountMapper) ac.getBean("accountMapper");// 扫描 org.rockcode.mappers 包,AccountMapper 接口类被注册为 "accountMapper" 名称
    System.out.println(accountMapper.queryAll());

      

    到这里,Mybatis 聊得差不多了,最后一篇聊聊 Mybatis SpringBoot Starter 。

  • 相关阅读:
    JavaScript的正则表达式的基础
    运用JS判断代码可以参考学习
    调用百度地图代码
    运用了css,js
    中国地图(Highmaps)
    Centos
    代理模式【 动态代理与静态代理】
    java集合 collection-list-LinkedList 模拟一个堆栈或者队列数据结构。
    java集合 collection-list-LinkedList
    java集合 collection-list-vector
  • 原文地址:https://www.cnblogs.com/xums/p/12658761.html
Copyright © 2011-2022 走看看