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;
   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM