首页 > 其他分享 >MapStruct+Maven+Lombok问题NoSuchBeanDefinitionException、does not have an accessible empty constructo排查

MapStruct+Maven+Lombok问题NoSuchBeanDefinitionException、does not have an accessible empty constructo排查

时间:2024-01-24 10:44:07浏览次数:26  
标签:accessible rbac java MapStruct constructo mapstruct 报错 org processor

概述

先直接说我遇到的问题吧,Spring Boot应用启动失败:

ERROR | org.springframework.boot.web.embedded.tomcat.TomcatStarter | onStartup | 61 | - Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. 
Message: Error creating bean with name 'jwtAuthorizationTokenFilter' defined in file [/Users/johnny/code/backend/rbac/rbac-provider/target/classes/com/aaaaa/rbac/modules/security/security/JwtAuthorizationTokenFilter.class]: Unsatisfied dependency expressed through constructor parameter 0; 
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jwtUserDetailsService': Injection of resource dependencies failed; 
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jwtPermissionService': Injection of resource dependencies failed; 
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'roleServiceImpl': Injection of resource dependencies failed; 
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.aaaaa.rbac.modules.system.service.mapper.RoleMapper' available: 
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup="", name="", description="", authenticationType=CONTAINER, type=java.lang.Object.class, mappedName="")}

PS:上面的报错日志经过硬换行(hard wrap)处理,方便阅读;IDEA打印的日志是经过软换行(soft wrap)显示的:
在这里插入图片描述

排查

Spring Boot启动失败不要太常见。

分析

分析一下这个启动报错日志:

  1. Spring启动时想注入jwtAuthorizationTokenFilter这个Bean,发现jwtAuthorizationTokenFilter依赖于jwtUserDetailsService
  2. 那就注入jwtUserDetailsService吧,发现jwtUserDetailsService又依赖于jwtPermissionService
  3. jwtPermissionService依赖于roleServiceImpl
  4. roleServiceImpl又依赖于RoleMapper
  5. RoleMapper依赖于RoleMapper

一开始以为是@Resource和@Autowired的差异导致应用启动失败。
在这里插入图片描述
于是把上面报错提到的Spring Bean的@Resource注解换成@Autowired注解,还是同样的报错;

添加@Qualifier注解,还是同样的报错;

@Autowired
@Qualifier("roleServiceImpl")
private RoleService roleService;

经过上面这一番瞎折腾,发现解决不了问题,才静下心来分析Mapper类,此时才发现端倪:

import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;

@Mapper(componentModel = "spring", uses = {PermissionMapper.class, MenuMapper.class, DeptMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface RoleMapper extends EntityMapper<RoleDTO, Role> {
}

RoleMapper使用的@Mapper注解并不是MyBatis提供的注解,而是MapStruct提供的注解。

另外附上EntityMapper源码:

public interface EntityMapper<D, E> {
    E toEntity(D dto);

    D toDto(E entity);

    List<E> toEntity(List<D> dtoList);

    List<D> toDto(List<E> entityList);
}

关于MapStruct的基础,可参考Java对象拷贝MapStruct

看一下启动类:

@SpringBootApplication
@EnableTransactionManagement
@EnableDiscoveryClient
@EnableLdapRepositories("com.aaaaa.rbac.modules.ldap.repository")
@EnableSwagger2
public class AppRun {
    public static void main(String[] args) {
        SpringApplication.run(AppRun.class, args);
    }
}

并没有配置扫描路径,那就在@SpringBootApplication注解里增加扫描路径:

@SpringBootApplication(scanBasePackages = {"com.aaaaa.rbac"})

并没有解决问题,还是同样的报错。

没看到aaaImpl.class文件

上面提到xxxMapper类使用MapStruct提供的@Mapper注解,并且显式指定生成Spring Component类:componentModel = "spring"

也就是说再执行mvn clean compile后,应该可以在classpath路径下看到RoleMapperImpl.class文件,pom文件里也引入过依赖:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.2.0.Final</version>
</dependency>

可是我在执行mvn clean compile命令后,并没有看到aaaImpl.class文件。

试过升级版本号,还是没有看到aaaImpl.class文件。

于是又去瞎捣鼓,瞎排查,去排查编译问题。

理论上,应该生成Impl.class文件,有这些文件,应用就可以启动成功。

尝试配置IDEA:
在这里插入图片描述
尝试过安装插件,并重启IDEA:
在这里插入图片描述
都没有看到编译生成的Impl.class文件,应用启动失败,还是同样的报错日志。

前前后后各种排查启动失败的问题,花费3~4个小时吧。

此时才想起来从头审视这个应用启动报错。

缘起

生产环境某个应用大量爆出如下ERROR级别日志:session ip change too many
在这里插入图片描述
查看到如下具体的报错信息,原来报错来源自alibaba druid这个开源组件:
在这里插入图片描述
等等!merchant服务代码我熟悉得很,并没有在pom.xml文件里引入过druid啊。

查看Git提交日志,发现最近merchant服务引入另一个服务提供的Feign接口,即引入rbac-client。借助于Maven Helper插件提供的Dependency Analyzer面板,查看到druid组件确实是在引入rbac-client包后引入的。

maven module

rbac服务之前只有2个module:

<modules>
    <module>rbac-common</module>
    <module>rbac-provider</module>
</modules>

加上工程父pom.xml文件,一共3个pom.xml文件。

新增业务场景需要,其他应用(也就是上面报错的merchant应用)需要用到rbac里已有的某个模块功能,于是rbac服务新增一个rbac-client模块,即maven module,即新增一个pom.xml文件。

理论上工程根pom.xml文件应该很轻量化:

<project>
    <parent>
    </parent>
    <groupId>com.aaa.rbac</groupId>
    <artifactId>rbac</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>
    <modules>
        <module>rbac-common</module>
    </modules>

    <name>rbac</name>

    <properties>
        <user.version>1.0.0-SNAPSHOT</user.version>
    </properties>
</project>

但是rbac这个服务却不是这样,在工程根pom.xml文件里引入一大堆乱七八糟的依赖,rbac-client作为其中的一个module,自然也就引入工程根pom.xml文件里引入的一大堆依赖,这其中就包括引发session ip change too many的druid组件。

真相

为了解决merchant服务爆出的druid错误日志。并没有考虑在merchant服务里的pom.xml文件里,通过增加exclusions标签来排除druid依赖,而是考虑直接优化rbac服务的根pom.xml文件。

也就是说,上面的应用启动报错日志,包括没有看到编译生成的aaImpl.class文件,都是在我改动rbac服务的几个pom.xml文件之后产生的。


ok,fine.
Actually, I'm not fine.


代码改动还在本地,没有提交。git stash将所有改动暂存下来,即恢复到没有任何改动的clean状态。执行mvn clean compile命令,终于看到生成如下aaMapperImpl.class文件
在这里插入图片描述
编译器生成的类文件如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RoleMapperImpl implements RoleMapper {
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private DeptMapper deptMapper;
    // 省略一大堆DTO和PO类转换方法
}

启动应用,当然可以启动成功,毕竟rbac服务在生产跑着呢。

这也印证之前的猜想,应用启动失败是因为没有找到aaMapperImpl.class文件。

maven

知道原因后,rbac的pom文件还是得优化一下。执行git unstash,或借助于IDEA提供的Git->Unstash Changes功能。

问题出在mapstruct-processor核心依赖:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <scope>provided</scope>
</dependency>

rbac-provider module依赖于rbac-common module。

如果把mapstruct-processor放在rbac-common module下的pom.xml文件里,虽然rbac-provider有声明引入rbac-common,也就自然而然会引入mapstruct-processor依赖,事实上通过Maven Helper插件也能看到rbac-provider module确实引入mapstruct-processor依赖,但是应用启动就是失败。

如果把mapstruct-processor依赖直接放在rbac-provider module的pom.xml文件里,则应用启动成功。

What The Fuck???

所以问题是我搞不懂Maven啊?????

does not have an accessible empty constructor.

另外,在优化工程的pom.xml文件时,也就是把之前放在根pom.xml文件里的依赖分散到各个module下时。还出现一个编译问题:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project rbac-provider: Compilation failure
[ERROR] com.aaaaa.rbac.modules.system.service.dto.JobDTO does not have an accessible empty constructor.

也就是说编译都通不过,更谈何启动应用呢?

作为另一个比MapStruct更出名,使用率更高的编译器插件,Lombok远比MapStruct大名鼎鼎吧。rbac服务当然也在使用lombok。

如果在rbac-common里引入lombok,在rbac-provider里引入mapstruct-processor就会出现上面这个编译失败的问题。也就是说,lombok

关于Maven的迷思:
看来我是真的不会用Maven,不懂Maven啊???

java: Internal error in the mapping processor: java.lang.NullPointerException

mvn clean compile执行成功,但是应用启动报错:

java: Internal error in the mapping processor: java.lang.NullPointerException
at org.mapstruct.ap.internal.processor.DefaultVersionInformation.createManifestUrl(DefaultVersionInformation.java:180)
at org.mapstruct.ap.internal.processor.DefaultVersionInformation.openManifest(DefaultVersionInformation.java:151)
at org.mapstruct.ap.internal.processor.DefaultVersionInformation.getLibraryName(DefaultVersionInformation.java:127)
at org.mapstruct.ap.internal.processor.DefaultVersionInformation.getCompiler(DefaultVersionInformation.java:120)
at org.mapstruct.ap.internal.processor.DefaultVersionInformation.fromProcessingEnvironment(DefaultVersionInformation.java:98)
at org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext.<init>(DefaultModelElementProcessorContext.java:59)
at org.mapstruct.ap.MappingProcessor.processMapperElements(MappingProcessor.java:222)
at org.mapstruct.ap.MappingProcessor.process(MappingProcessor.java:162)
at org.jetbrains.jps.javac.APIWrappers$ProcessorWrapper.process(APIWrappers.java:157)
at org.jetbrains.jps.javac.JavacMain.compile(JavacMain.java:238)

如上报错日志有删减及美化换行处理,另外这个启动报错日志并不是出现在IDEA的console里,而是出现在Build Output里:
在这里插入图片描述
并且有个WARN提示:

java: You aren't using a compiler supported by lombok, so lombok will not work and has been disabled.
  Your processor is: com.sun.proxy.$Proxy25
  Lombok supports: sun/apple javac 1.6, ECJ

解决WARN的方法是:使用较新的lombok版本。

解决ERROR报错的方法是:使用较新的MapStruct版本。

参考intellij-idea-mapstruct-java-internal-error-in-the-mapping-processor-java-lang

java: Couldn't retrieve @Mapper annotation

报错同样出现在Build Output里:
在这里插入图片描述
解决方法:移除springfox-swagger2里的mapstruct相关依赖。

参考Couldn't retrieve @Mapper annotation

反思

  1. 遇到问题一定不能慌乱,不能瞎百度 ,瞎Google,瞎尝试。时间就在尝试中浪费,注意力就在分析这个研究那个中分散,可能差一点就摸到门了。
  2. 一定要先分析,排除和问题无关的因素。
  3. 说起来容易,排查起问题来思路肯定容易混乱,尤其是前前后后遇到上文提到的5类报错。
  4. 真的不懂Maven啊。
  5. 问题是和MapStruct、Lombok、Maven有关。解决问题不难,难的是彻底搞透原理。

标签:accessible,rbac,java,MapStruct,constructo,mapstruct,报错,org,processor
From: https://www.cnblogs.com/johnny-wong/p/17984112

相关文章

  • vite 生产打包后报错 xx is not a constructor
    版本vite:"^5.0.11"解决方法添加optimizeDeps.disabled=false和build.commonjsOptions.include=[]。vite.config.tsimport{defineConfig}from'vite'importreactfrom'@vitejs/plugin-react'exportdefaultdefineConfig({plu......
  • constructor中的super
    1.ES6要求,子类的构造函数必须执行一次super函数。这是必须的,否则JavaScript引擎会报错。在执行super函数时,其实就是在创建子类的this,然后将父类的实例和方法放置在这个this对象中,子类在调用super之前是没有this的,所有的this操作都要在super()关键字后执行。由于super指向父类的......
  • 简化属性拷贝插件 MapStructs 使用指北
    MapStruct使用指南1、安装与介绍what?mapstruct是一个代码生成器,可以简化实现javabean之间的转换的配置方法生成的代码使用传统的方法实现getset属性,比起反射更快、更简单、更安全,易于理解why?基于多层的应用经常需要映射不同的对象模型如VO->TDO等;属性转换的代码......
  • MapStruct+Maven+Lombok问题NoSuchBeanDefinitionException、does not have an access
    概述先直接说我遇到的问题吧,SpringBoot应用启动失败:ERROR|org.springframework.boot.web.embedded.tomcat.TomcatStarter|onStartup|61|-ErrorstartingTomcatcontext.Exception:org.springframework.beans.factory.UnsatisfiedDependencyException.Message:Error......
  • 使用MapStruct进行对象的转换
    在Java系统工程开发过程中,都会有各个层之间的对象转换,比如VO、DTO、PO、VO等,而如果都是手动get、set又太浪费时间,还可能操作错误,选择一个自动化工具会更加方便。目前市面上有大概12种类型转换的操作,如下:MapStruct可以直接在编译期间生成getset方法,非常方便快捷,且不耗费性......
  • mapstruct报错 No property named "XXXX" exists in source parameter(s). Type "XXXX
    1、问题现象java:Nopropertynamed"XXXX"existsinsourceparameter(s).Type"XXXX"hasnoproperties.2、相关环境依赖版本jdk:17maven:3.8.8springboot:3.1.4lombok:1.18.30mapstruct:1.5.53、解决办法在pom.xml中加入如下配置<annotationProcessor......
  • class sun.reflect.GeneratedConstructorAccessor2 cannot access its superclass sun
    在启动JFinal程序时报错classsun.reflect.GeneratedConstructorAccessor2cannotaccessitssuperclasssun.reflect.Constructor问题所在因为这个项目的原作者是使用eclipse编写的,idea和eclipse的启动机制不一样,由于eclipse并没有自动实现热加载机制,因此这里我们需要加上......
  • nodejs使用sequelize vscode报错:Type 'Model<any, any, any>' is not a constructor f
    我的模型定义如下:import{Model,DataTypes}from"sequelize";//定义资源模型classRuleextendsModel{}问题:vscdoe报错:Type'Model<any,any,any>'isnotaconstructorfunctiontype.解决:这个问题可能是由于TypeScript类型定义的问题导致的。Model 是Seq......
  • MapStruct使用指南以及原理解析
    使用指南:https://juejin.cn/post/6956190395319451679原理解析:https://blog.csdn.net/begefefsef/article/details/1264349501.MapStruct原理是一个Java注解处理器,它基于编译时代码生成的原理,用于自动化Javabean类型之间的映射工作。以下是MapStruct的工作原理的详细解读:注......
  • mapstruct 高级用法自定义转换规则
    https://svip888.blog.csdn.net/article/details/115706803?spm=1001.2101.3001.6650.15&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-15-115706803-blog-117566307.235%5Ev39%5Epc_relevant_3m_sort_dl_base3&depth_1-utm_sourc......