tiant好心情 2023-10-24 11:50 采纳率: 44.4%
浏览 10

spring boot 3.1.3整合mybatisplus 3.5.3后报错

将spring boot 升级到3.1.3和mybatis-plus-boot-starter升级到3.5.3后,项目启动执行删除的mapper会报
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field protected java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.h accessible: module java.base does not "opens java.lang.reflect" to unnamed module @62e7f11d
这是为什么呢

  • 写回答

1条回答 默认 最新

  • CSDN-Ada助手 CSDN-AI 官方账号 2023-10-24 15:35
    关注

    【相关推荐】



    • 帮你找了个相似的问题, 你可以看下: https://ask.csdn.net/questions/7638268
    • 这篇博客你也可以参考下:spring boot+mybatis报错mapper无法注入
    • 除此之外, 这篇博客: mybatis与spring boot的集成中的 2.4.mapper的创建 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:

      通过前一阶段,所有的mapper信息都已经注册到了容器,下一步,就是根据注册的信息实例化mapper了。也就是执行spring加载bean流程的org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization这个方法,这个方法会调org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons这个方法,对容器中的所有单例的bean进行创建,创建的bean分两种情况,一种是FactoryBean类型的bean,一种是普通bean,因为mapper的代理类MapperFactoryBean实现了FactoryBean接口,所以是FactoryBean类型的bean,核心源码如下:

              // Trigger initialization of all non-lazy singleton beans...
              for (String beanName : beanNames) {
                  RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                  if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                      if (isFactoryBean(beanName)) {
                          // getBean(beanName)获取的是FactoryBean创建的bean实例
                          // getBean("&"+beanName)获取FactoryBean本身,所以这里获取的是FactoryBean本身
                          Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                          if (bean instanceof FactoryBean) {
                              final FactoryBean<?> factory = (FactoryBean<?>) bean;
                              boolean isEagerInit;
                              // 判断是否有代码运行权限
                              if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                                  isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                                                  ((SmartFactoryBean<?>) factory)::isEagerInit,
                                          getAccessControlContext());
                              }
                              else {
                                  // 默认情况 FactoryBean 延迟创建bean
                                  // 但如果是 SmartFactoryBean,而且设置其 eagerInit 值为 true
                                  // 那么就进行提前创建
                                  isEagerInit = (factory instanceof SmartFactoryBean &&
                                          ((SmartFactoryBean<?>) factory).isEagerInit());
                              }
                              if (isEagerInit) {
                                  // 进行提前创建
                                  getBean(beanName);
                              }
                          }
                      }
                      else {
                          getBean(beanName);
                      }
                  }
              }

      如果是mapper的话,根据代码逻辑,这里不会提前创建,但是FactoryBean本身是会创建的,也就是mapper的代理类MapperFactoryBean是会创建的。创建逻辑就在org.springframework.beans.factory.support.AbstractBeanFactory#getBean这个方法里,最终一路下去会调到org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,这个是创建bean的核心逻辑,代码比较多,和我们有关就是这一段:

      				// Create bean instance.
      				if (mbd.isSingleton()) {
      					sharedInstance = getSingleton(beanName, () -> {
      						try {
      							return createBean(beanName, mbd, args);
      						}
      						catch (BeansException ex) {
      							// Explicitly remove instance from singleton cache: It might have been put there
      							// eagerly by the creation process, to allow for circular reference resolution.
      							// Also remove any beans that received a temporary reference to the bean.
      							destroySingleton(beanName);
      							throw ex;
      						}
      					});
      					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      				}

      最核心的是createBean(beanName, mbd, args)是这个方法,bean的实例化、属性填充和初始化等等都是在这里处理的。

      实例化:通过有参的构造函数实例化MapperFactoryBean,参数就是mapper接口

      属性填充:就是将依赖注入,这里会将sqlSessionTemplate和sqlSessionFactory注入,@MapperScan的情况下,指定了名称,会根据名称注入,默认自动配置的情况下,会根据类型注入

      初始化:MapperFactoryBean有自己的逻辑,下面详细说下

      看下MapperFactoryBean的继承体系。

       MapperFactoryBean的父类DaoSupport实现了InitializingBean接口,那么在MapperFactoryBean创建后会,在初始化阶段会调用org.springframework.dao.support.DaoSupport#afterPropertiesSet这个方法。

          public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
              this.checkDaoConfig();
      
              try {
                  this.initDao();
              } catch (Exception var2) {
                  throw new BeanInitializationException("Initialization of DAO failed", var2);
              }
          }

      checkDaoConfig()是一个抽象方法,MapperFactoryBean做了实现:

        @Override
        protected void checkDaoConfig() {
          //检查sqlSessionTemplate不能为空,这里肯定不会为空,在属性填充阶段已经注入了
          super.checkDaoConfig();
          //检查mapper接口不能为空,这里肯定不会为空,在实例化阶段已经通过有参构造函数传进来了
          notNull(this.mapperInterface, "Property 'mapperInterface' is required");
      
          Configuration configuration = getSqlSession().getConfiguration();
          if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
            try {
              configuration.addMapper(this.mapperInterface);
            } catch (Exception e) {
              logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
              throw new IllegalArgumentException(e);
            } finally {
              ErrorContext.instance().reset();
            }
          }
        }

      主要看下configuration.addMapper(this.mapperInterface)这个方法:

        public <T> void addMapper(Class<T> type) {
          if (type.isInterface()) {
            if (hasMapper(type)) {
              throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
              //将mapper接口包装成MapperProxyFactory对象保存至map缓存起来,后面mapper注入service时,会从这里获取
              knownMappers.put(type, new MapperProxyFactory<>(type));
              // It's important that the type is added before the parser is run
              // otherwise the binding may automatically be attempted by the
              // mapper parser. If the type is already known, it won't try.
              MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
              parser.parse();
              loadCompleted = true;
            } finally {
              if (!loadCompleted) {
                knownMappers.remove(type);
              }
            }
          }
        }

      再继续看parser.parse()这个方法:

        public void parse() {
          String resource = type.toString();
          if (!configuration.isResourceLoaded(resource)) {
            // 先加载xml资源,如:UserMapper.xml,parse方法先解析配置文件,然后再解析接口里的注解配置,且注解里的配置会覆盖配置文件里的配置,也就是说注解的优先级高于配置文件
            loadXmlResource();
            // 添加解析过的映射,下次判断有就不在解析
            configuration.addLoadedResource(resource);
            // 命名空间
            assistant.setCurrentNamespace(type.getName());
            // 二级缓存的处理,处理注解@CacheNamespace与@CacheNamespaceRef
            parseCache();
            parseCacheRef();
            //获取mapper的所有方法
            Method[] methods = type.getMethods();
            for (Method method : methods) {
              try {
                // issue #237
                if (!method.isBridge()) {
                  // 解析方法,对方法上各种注解的解析,并把解析完的信息,添加到Configuration的mappedStatements中去,在后面执行sql语句时根据方法名取出来调用
                  parseStatement(method);
                }
              } catch (IncompleteElementException e) {
                configuration.addIncompleteMethod(new MethodResolver(this, method));
              }
            }
          }
          parsePendingMethods();
        }

      至此,MapperFactoryBean的初始化也完事了。回到org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean这个方法,在这个方法里的创建单例bean后,会调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getObjectForBeanInstance,接着调用org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance,这个方法会决定返回MapperFactoryBean本身还是它创建的bean。

        protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
      
          // Don't let calling code try to dereference the factory if the bean isn't a factory.
          if (BeanFactoryUtils.isFactoryDereference(name)) {//是否是FactoryBean名字的前缀&
            if (beanInstance instanceof NullBean) {
              return beanInstance;
            }
            if (!(beanInstance instanceof FactoryBean)) {//不是FactoryBean的话名字有&会报异常
              throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
            }
            if (mbd != null) {
              mbd.isFactoryBean = true;
            }
            //要找的就是FactoryBean自身,直接返回
            return beanInstance;
          }
      
          // Now we have the bean instance, which may be a normal bean or a FactoryBean.
          // If it's a FactoryBean, we use it to create a bean instance, unless the
          // caller actually wants a reference to the factory.
          //不是FactoryBean就直接返回,也就是普通的bean
          if (!(beanInstance instanceof FactoryBean)) {
            return beanInstance;
          }
      
          //除了以上逻辑,那就剩下最后一种情况了,就是获取的是FactoryBean创建的bean
          Object object = null;
          if (mbd != null) {
            mbd.isFactoryBean = true;
          }
          else {
            //从FactoryBean的缓存中获取
            object = getCachedObjectForFactoryBean(beanName);
          }
          if (object == null) {
            // Return bean instance from factory.
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            //mbd没定义,但是FactoryBean是有定义的,获取mbd
            if (mbd == null && containsBeanDefinition(beanName)) {
              mbd = getMergedLocalBeanDefinition(beanName);
            }
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
          }
          return object;
        }

      第一段是处理FactoryBean的情况,也就是创建MapperFactoryBean走的逻辑,中间是普通bean的情况,最后一段是获取FactoryBean创建的bean,也就是MapperFactoryBean创建的bean,这个是在需要注入mapper时触发的,下一节说。


    如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^
    评论

报告相同问题?

问题事件

  • 创建了问题 10月24日

悬赏问题

  • ¥15 C++为什么这个代码没报错运行不出来啊
  • ¥15 第一行输入n,第二行输入a b c输出的字符向后平移n个位置,当移动到z时,重新返回a开始
  • ¥15 为什么跑这个代码,文件显示不在呀
  • ¥15 一道ban了很多东西的pyjail题
  • ¥15 关于#r语言#的问题:如何将生成的四幅图排在一起,且对变量的赋值进行更改,让组合的图漂亮、美观@(相关搜索:森林图)
  • ¥15 C++识别堆叠物体异常
  • ¥15 微软硬件驱动认证账号申请
  • ¥15 GPT写作提示指令词
  • ¥20 根据动态演化博弈支付矩阵完成复制动态方程求解和演化相图分析等
  • ¥15 华为超融合部署环境下RedHat虚拟机分区扩容问题