zoukankan      html  css  js  c++  java
  • MapperScan实现原理分析

    mybatis.spring中一个关键注解MapperScan,通过它可以扫描指定包下面的所有mapper(mybatis自己实现了一个扫描器

    public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {}

    最终调用父类的doScan()方法,把bean定义交给了spring初始化管理),然后我们就可以在service中注入使用了:

    UserMapper:

    @Mapper
    public interface UserMapper {
       @Select("SELECT * FROM user WHERE id = #{userId}")
       User getUser(@Param("userId") String userId);
    
    
       @Select("SELECT * FROM user")
       List<User> getAll();
    }

    UserService

    @Service
    public class UserService {
       @Autowired
       UserMapper userMapper;
    
    
       public User getUser(String userId) {
          return userMapper.getUser(userId);
       }
    
    
       public List<User> getAll() {
          return userMapper.getAll();
       }
    

    有两个关键的点:

    1. 这些mapper是怎么交给spring容器管理的呢?
    2. mapper都是接口类型都是怎么实例化的呢?

    很好奇,其实答案就在MapperScan注解当中,通过查看其源码:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(MapperScannerRegistrar.class)
    @Repeatable(MapperScans.class)
    public @interface MapperScan {}

    很关键的一个:@Import(MapperScannerRegistrar.class),其中:

    public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {}

    看到这就会明白了吧,实现了ImportBeanDefinitionRegistrar接口的方法,这样就可以通过BeanDefinitionRegistry registry注册了。

    registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)

    另外一个问题就是它是接口类型的到底是怎么实例化调用的呢?毫无疑问肯定是采用了代理模式,最终通过代理对象实现调用的。但这又会引出另外一个疑问:既然是实现了代理,那到底是怎么动态注册到容器之中的呢?定义BeanDefinition,是需要指定BeanClass的,既然是代理对象,怎么动态拿到它的BeanClass的呢?

    其实它是采用了FactoryBean,如下可以简单模拟MapperScan的实现:

    自定义一个MyScan注解:

    @Import(MyImportBeanDefinitionRegistrar.class)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyScan {
    
    
    }

    MyImportBeanDefinitionRegistrar实现:

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
    
       @Override
       public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
          //得到bd
    
    
          //根据包名扫描所有的class,这里就直接写死了
          BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserMapper.class);
          GenericBeanDefinition beanDefinition = (GenericBeanDefinition) builder.getBeanDefinition();
          //System.out.println(beanDefinition.getBeanClassName());
          //通过构造函数注入,spring内部调用下面指定的 MyFactoryBean的构造
          beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
          //指定bd类型,因为它是一个代理对象,我们只能通过FactoryBean去动态获取其类型
          beanDefinition.setBeanClass(MyFactoryBean.class);
          registry.registerBeanDefinition("userMapper", beanDefinition);
       }
    

    MyFactoryBean实现:

    public class MyFactoryBean implements FactoryBean, InvocationHandler {
    
    
       Class clazz;
       //通过构造函数注入
       public MyFactoryBean(Class clazz) {
          this.clazz = clazz;
       }
    
    
       @Override
       public Object getObject() throws Exception {
          Class[] clazzs = new Class[]{clazz};
          Object instance = Proxy.newProxyInstance(this.getClass().getClassLoader(), clazzs, this);
          return instance;
       }
    
    
       @Override
       public Class<?> getObjectType() {
          return clazz;
       }
    
    
       @Override
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          System.out.println("proxy");
          //可以动态拿到每个方法注解的sql语句
          Method method1 = proxy.getClass().getInterfaces()[0].getMethod(method.getName(), String.class);
          Select annotations = method1.getDeclaredAnnotation(Select.class);
          System.out.println(annotations.value()[0]);
          return null;
       
  • 相关阅读:
    C语言寒假大作战01
    C语言I作业12—学期总结
    C语言I博客作业11
    C语言I博客作业10
    浅谈js模块加载方式(初级)
    浅谈.net的后台校验
    api接口访问限制
    系统操作日志表单形式构建
    RedisUtil(未完,持续更新中....)
    定时处理组件---Quartz.net
  • 原文地址:https://www.cnblogs.com/tianboblog/p/12618057.html
Copyright © 2011-2022 走看看