资讯专栏INFORMATION COLUMN

Spring中接口动态实现的解决方案

ispring / 1726人阅读

摘要:声明解决方案是基于源码,进行二次开发实现。其是基于层面,不存在任何的接口实现类。因而在实现的过程中,首先要解决的是如何动态实现接口的实例化。其次是如何将使接口根据注解实现相应的功能。

声明解决方案是基于Mybatis源码,进行二次开发实现。

问题领导最近跟我提了一个需求,是有关于实现类Mybatis的@Select、@Insert注解的功能。其是基于interface层面,不存在任何的接口实现类。因而在实现的过程中,首先要解决的是如何动态实现接口的实例化。其次是如何将使接口根据注解实现相应的功能。

我们先来看看Mybatis是如何实现Dao类的扫描的。

MapperScannerConfigurer.java

</>复制代码

  1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
  2. if (this.processPropertyPlaceHolders) {
  3. processPropertyPlaceHolders();
  4. }
  5. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  6. scanner.setAddToConfig(this.addToConfig);
  7. scanner.setAnnotationClass(this.annotationClass);
  8. scanner.setMarkerInterface(this.markerInterface);
  9. scanner.setSqlSessionFactory(this.sqlSessionFactory);
  10. scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  11. scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  12. scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  13. scanner.setResourceLoader(this.applicationContext);
  14. scanner.setBeanNameGenerator(this.nameGenerator);
  15. scanner.registerFilters();
  16. scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  17. }

ClassPathMapperScanner是Mybatis继承ClassPathBeanDefinitionScanner类而来的。这里对于ClassPathMapperScanner的配置参数来源于我们在使用Mybatis时的配置而来,是不是还记得在使用Mybatis的时候要配置basePackage的参数呢?

接着我们就顺着scanner.scan()方法,进入查看一下里面的实现。

ClassPathBeanDefinitionScanner.java

</>复制代码

  1. public int scan(String... basePackages) {
  2. int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
  3. doScan(basePackages);
  4. // Register annotation config processors, if necessary.
  5. if (this.includeAnnotationConfig) {
  6. AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
  7. }
  8. return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
  9. }

这里关键的代码是doScan(basePackages);,那么我们在进去看一下。可能你会看到的是Spring源码的实现方法,但这里Mybatis也实现了自己的一套,我们看一下Mybatis的实现。

ClassPathMapperScanner.java

</>复制代码

  1. public Set doScan(String... basePackages) {
  2. Set beanDefinitions = super.doScan(basePackages);
  3. if (beanDefinitions.isEmpty()) {
  4. logger.warn("No MyBatis mapper was found in "" + Arrays.toString(basePackages) + "" package. Please check your configuration.");
  5. } else {
  6. for (BeanDefinitionHolder holder : beanDefinitions) {
  7. GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
  8. if (logger.isDebugEnabled()) {
  9. logger.debug("Creating MapperFactoryBean with name "" + holder.getBeanName()
  10. + "" and "" + definition.getBeanClassName() + "" mapperInterface");
  11. }
  12. // the mapper interface is the original class of the bean
  13. // but, the actual class of the bean is MapperFactoryBean
  14. definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
  15. definition.setBeanClass(MapperFactoryBean.class);
  16. definition.getPropertyValues().add("addToConfig", this.addToConfig);
  17. boolean explicitFactoryUsed = false;
  18. if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
  19. definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
  20. explicitFactoryUsed = true;
  21. } else if (this.sqlSessionFactory != null) {
  22. definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
  23. explicitFactoryUsed = true;
  24. }
  25. if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
  26. if (explicitFactoryUsed) {
  27. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  28. }
  29. definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
  30. explicitFactoryUsed = true;
  31. } else if (this.sqlSessionTemplate != null) {
  32. if (explicitFactoryUsed) {
  33. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  34. }
  35. definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
  36. explicitFactoryUsed = true;
  37. }
  38. if (!explicitFactoryUsed) {
  39. if (logger.isDebugEnabled()) {
  40. logger.debug("Enabling autowire by type for MapperFactoryBean with name "" + holder.getBeanName() + "".");
  41. }
  42. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  43. }
  44. }
  45. }
  46. return beanDefinitions;
  47. }

definition.setBeanClass(MapperFactoryBean.class);这行代码是非常关键的一句,由于在Spring中存在两种自动实例化的方式,一种是我们常用的本身的接口实例化类进行接口实例化,还有一种就是这里的自定义实例化。而这里的setBeanClass方法就是在BeanDefinitionHolder中进行配置。在Spring进行实例化的时候进行处理。

那么我们在看一下MapperFactoryBean.class

MapperFactoryBean.java

</>复制代码

  1. public class MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean {
  2. private Class mapperInterface;
  3. private boolean addToConfig = true;
  4. /**
  5. * Sets the mapper interface of the MyBatis mapper
  6. *
  7. * @param mapperInterface class of the interface
  8. */
  9. public void setMapperInterface(Class mapperInterface) {
  10. this.mapperInterface = mapperInterface;
  11. }
  12. /**
  13. * If addToConfig is false the mapper will not be added to MyBatis. This means
  14. * it must have been included in mybatis-config.xml.
  15. *

  16. * If it is true, the mapper will be added to MyBatis in the case it is not already
  17. * registered.
  18. *

  19. * By default addToCofig is true.
  20. *
  21. * @param addToConfig
  22. */
  23. public void setAddToConfig(boolean addToConfig) {
  24. this.addToConfig = addToConfig;
  25. }
  26. /**
  27. * {@inheritDoc}
  28. */
  29. @Override
  30. protected void checkDaoConfig() {
  31. super.checkDaoConfig();
  32. notNull(this.mapperInterface, "Property "mapperInterface" is required");
  33. Configuration configuration = getSqlSession().getConfiguration();
  34. if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
  35. try {
  36. configuration.addMapper(this.mapperInterface);
  37. } catch (Throwable t) {
  38. logger.error("Error while adding the mapper "" + this.mapperInterface + "" to configuration.", t);
  39. throw new IllegalArgumentException(t);
  40. } finally {
  41. ErrorContext.instance().reset();
  42. }
  43. }
  44. }
  45. /**
  46. * {@inheritDoc}
  47. */
  48. public T getObject() throws Exception {
  49. return getSqlSession().getMapper(this.mapperInterface);
  50. }
  51. /**
  52. * {@inheritDoc}
  53. */
  54. public Class getObjectType() {
  55. return this.mapperInterface;
  56. }
  57. /**
  58. * {@inheritDoc}
  59. */
  60. public boolean isSingleton() {
  61. return true;
  62. }

在该类中其实现了FactoryBean接口,看过Spring源码的人,我相信对其都有很深的印象,其在Bean的实例化中起着很重要的作用。在该类中我们要关注的是getObject方法,我们之后将动态实例化的接口对象放到Spring实例化列表中,这里就是入口,也是我们的起点。不过要特别说明的是mapperInterface的值是如何被赋值的,可能会有疑问,我们再来看看上面的ClassPathMapperScanner.java我们在配置MapperFactoryBean.class的上面存在一行 definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());其在之后在Spring的PostProcessorRegistrationDelegate类的populateBean方法中进行属性配置,会将其依靠反射的方式将其注入到MapperFactoryBean.class中。

而且definition.getPropertyValues().add中添加的值是注入到MapperFactoryBean对象中去的。这一点需要说明一下。

更多内容可以关注微信公众号,或者访问AppZone网站

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67498.html

相关文章

  • Spring自定义注解不生效原因解析及解决方法

    摘要:自定义注解不生效原因解析及解决方法背景项目中,自己基于实现了一套缓存注解。但是最近出现一种情况缓存竟然没有生效,大量请求被击穿到层,导致压力过大。至此,问题得到解决。 自定义注解不生效原因解析及解决方法 背景: 项目中,自己基于spring AOP实现了一套java缓存注解。但是最近出现一种情况:缓存竟然没有生效,大量请求被击穿到db层,导致db压力过大。现在我们看一下具体代码情形(代...

    xbynet 评论0 收藏0
  • 【好好面试】学完Aop,连动态代理原理都不懂?

    摘要:总结动态代理的相关原理已经讲解完毕,接下来让我们回答以下几个思考题。 【干货点】 此处是【好好面试】系列文的第12篇文章。文章目标主要是通过原理剖析的方式解答Aop动态代理的面试热点问题,通过一步步提出问题和了解原理的方式,我们可以记得更深更牢,进而解决被面试官卡住喉咙的情况。问题如下 SpringBoot默认代理类型是什么 为什么不用静态代理 JDK动态代理原理 CGLIB动态代理...

    Keven 评论0 收藏0
  • 慕课网_《Spring入门篇》学习总结

    摘要:入门篇学习总结时间年月日星期三说明本文部分内容均来自慕课网。主要的功能是日志记录,性能统计,安全控制,事务处理,异常处理等等。 《Spring入门篇》学习总结 时间:2017年1月18日星期三说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:https://github.com/zccodere/s...个人学习源码:https://git...

    Ververica 评论0 收藏0
  • Spring AOP就是这么简单啦

    摘要:是一种特殊的增强切面切面由切点和增强通知组成,它既包括了横切逻辑的定义也包括了连接点的定义。实际上,一个的实现被拆分到多个类中在中声明切面我们知道注解很方便,但是,要想使用注解的方式使用就必须要有源码因为我们要 前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~ 之前我已经写过一篇关于AOP的文章了,那篇把比较重要的知...

    Jacendfeng 评论0 收藏0
  • 学Aop?看这篇文章就够了!!!

    摘要:又是什么其实就是一种实现动态代理的技术,利用了开源包,先将代理对象类的文件加载进来,之后通过修改其字节码并且生成子类。 在实际研发中,Spring是我们经常会使用的框架,毕竟它们太火了,也因此Spring相关的知识点也是面试必问点,今天我们就大话Aop。特地在周末推文,因为该篇文章阅读起来还是比较轻松诙谐的,当然了,更主要的是周末的我也在充电学习,希望有追求的朋友们也尽量不要放过周末时...

    boredream 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<