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 。

  • 相关阅读:
    tensorflow2.0 GPU和CPU 时间对比
    第一次使用FileZilla Server
    PremiumSoft Navicat 15 for Oracle中文破解版安装教程
    Unmapped Spring configuration files found. Please configure Spring facet or use 'Create Default Context' to add one including all unmapped files.
    ng : 无法加载文件 D: odejs ode_global g.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
    angular
    Github上优秀的go项目
    win10---file explore 中remove quick access folder
    react--useEffect使用
    linux---cat 和 grep 的妙用
  • 原文地址:https://www.cnblogs.com/xums/p/12658761.html
Copyright © 2011-2022 走看看