勺子因利亚根据环境Docker 和本地 Spring Boot不解析 CtExecutableReference 类型和声明类型

问题描述

美好的一天。

我在我的 Java 项目中使用 Spoon 分析库时遇到了一个非常奇怪的问题。 在不同环境下使用 Spoon Launcher 运行相同代码时,我得到不同的解析结果

  • 1 环境 - 从 Intellij IDEA 本地运行的 Spring Boot 项目
  • 2 环境 - 在 Docker 容器中运行的相同 Spring Boot 项目

在本地运行的 Spring Boot 项目中 - 一切正常,但是当我在 Docker 中运行相同的代码时 - 我在 null CtExecutableReference.getType() 中得到 CtExecutableReference.getDeclaredType()

我在 GitHub 上打开了问题 - https://github.com/INRIA/spoon/issues/3926

详情如下

我的 Spoon 版本是 8.2.0。 (来自 Maven 仓库)

我正在尝试解析(构建 AST)来自 this GitHub repository代码 我在解析 this class 时遇到了麻烦 这里有以下几行

...
@Service
public class ValueServices {
    
    private ValuesRepository valuesRepository;
    
    private Queue<Values> queue;

    @Autowired
    public ValueServices(ValuesRepository valuesRepository) {
        super();
        this.valuesRepository = valuesRepository;
        this.queue = new LinkedList<Values>();
    }
    
    public List<Values> getAllValues() {
        List<Values> values = new ArrayList<>();
        this.valuesRepository.findAll().forEach(values::add);
        return values;
    }

...
}

当我运行分析并尝试为 CtExecutableReference 语句的 findAll() 方法解析 this.valuesRepository.findAll().forEach(values::add) 时,我得到 nullgetType()getDeclaredType() 值在 Docker 中运行我的项目时。 在本地运行时,getType()getDeclaredType() 都具有非空值

解析其他项目中其他类似的代码块时也会出现同样的问题。 例如here

@Service
public class BetService {

    public static final String DATE_FORMAT_Now = "yyyy-MM-dd HH";
    public static final String DATE_FORMAT_Now_WITH_HOUR_MIN = "yyyy-MM-dd HH:mm:ss";

    private BetRepository betRepository;

    @Autowired
    public BetService(BetRepository betRepository) {
        super();
        this.betRepository = betRepository;
    }

    public List<Bet> getAllBets() {
        List<Bet> bets = new ArrayList<Bet>();
        this.betRepository.findAll().forEach(bets::add);

        return bets;
    }
}

在 Docker 中运行时,带有 this.betRepository.findAll() 的语句在 getTypegetDeclaredType 中都为 null,但在本地环境中可以。

同时 following code 在两种环境中都被很好地解析

public class BetRepositoryTest {
    
    @Autowired
    private TestEntityManager entityManager;
 
    @Autowired
    private BetRepository betRepository;

    @Test
    public void test() {
        Bet bet = new Bet("2018-07-06 12:56","WIN",103333,1082,500.5);
        entityManager.persist(bet);
        entityManager.flush();
       
        List<Bet> bets = betRepository.findByCustomerId(bet.getCustomerId());       
        assertthat(bet.getCustomerId() == bets.get(0).getCustomerId());
    }
}

和语句 betRepository.findByCustomerId() 解析正常,并且在 Docker 和本地 Spring Boot 运行中都具有必要的类型信息。

我仔细检查了本地测试 - 一切正常 - 从 IDE 在测试中运行代码或从 IDE 启动 Spring Boot 项目并通过从 Web UI 调用服务初始化分析 - 一切正常,按预期工作。

但是当我构建 Docker 镜像时 - 我的类型和声明类型都为 null。

我正在使用以下代码运行 Spoon 分析

private SourceCodemetamodel buildmetamodelForFiles(Collection<File> javaFiles) {
        Launcher spoonAPI = new Launcher();
        log.debug("Spoon environment - {}",ToStringBuilder.reflectionToString(spoonAPI.getEnvironment()));
        log.debug("Spoon model builder - {}",ToStringBuilder.reflectionToString(spoonAPI.getModelBuilder()));
        Set<String> inputResources = new HashSet<>();
        for (File javaFile: javaFiles) {
            String javaDir = JavaFileUtils.getJavaFileStorageRootPath(javaFile);
            if (StringUtils.isNotBlank(javaDir) && !inputResources.contains(javaDir)) {
                spoonAPI.addInputResource(javaDir);
                inputResources.add(javaDir);
            }
            else if (StringUtils.isBlank(javaDir)) {
                spoonAPI.addInputResource(javaFile.getAbsolutePath());
            }
        }
        spoonAPI.buildModel();
        CtModel ctModel = spoonAPI.getModel();
        Collection<CtType<?>> modelTypes = ctModel.getAllTypes();
        return new SpoonSourceCodemetamodel(modelTypes,false);
    }

在运行 Launcher 之前,我尝试打印它的设置。这是我得到的

2021-05-14 13:26:12.329 DEBUG 1 --- [         task-1] c.s.s.r.i.s.SpoonJavaSourceCodeAnalyzer  : Spoon environment - spoon.support.StandardEnvironment@4626a7ce[errorCount=0,processingStopped=false,prettyPrintingMode=FULLYQUALIFIED,warningCount=0,sourceClasspath=<null>,preserveLineNumbers=false,copyResources=true,enableComments=true,level=ERROR,shouldCompile=false,skipSelfChecks=false,complianceLevel=8,previewFeaturesEnabled=false,outputType=classes,noclasspath=true,compressionType=GZIP,sniperMode=false,ignoreDuplicateDeclarations=false,prettyPrinterCreator=<null>,useTabulations=false,tabulationSize=4,binaryOutputDirectory=/spooned-classes]

2021-05-14 13:26:12.333 DEBUG 1 --- [         task-1] c.s.s.r.i.s.SpoonJavaSourceCodeAnalyzer  : Spoon model builder - spoon.support.compiler.jdt.JDTBasedSpoonCompiler@2df98092[environment=<null>,probs=[],requestor=spoon.support.compiler.jdt.TreeBuilderRequestor@57050733,factory=spoon.reflect.factory.FactoryImpl@237d7ffa,javaCompliance=7,sources=<virtual folder>: spoon.support.compiler.VirtualFolder@42618617,templates=<virtual folder>: spoon.support.compiler.VirtualFolder@7fb32ea2,templateClasspath={},compilationunitFilters=[],sortList=true]

我正在 Java8 上运行项目 - 在两种环境中(详情如下)。 要构建 docker,我使用以下命令

FROM java:8
copY maven /maven/
ENTRYPOINT java -Xverify:none -XX:TieredStopAtLevel=1 -XX:+TieredCompilation -XX:+UseSerialGC -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=${PROFILE:-docker-dev} -jar /maven/skillcounters-sca-service-1.0-SNAPSHOT.jar

我尝试切换到不同的 Docker 基础镜像(openjdk \ alpine 等),但没有任何帮助。 我试图排除上面列出的所有 Java 运行选项(例如 -XXblabla) - 也没有帮助。

为了了解可能出现的问题,我在应用程序启动时打印了所有环境(包括 Java)数据。

这里打印的是本地环境

Apple_PubSub_Socket_Render : /private/tmp/com.apple.launchd.xCrhs0tTMM/Render
COMMAND_MODE : unix2003
HOME : /Users/sk
JAVA_MAIN_CLASS_66239 : org.codehaus.classworlds.Launcher
JAVA_MAIN_CLASS_66249 : com.skillcounters.sca.SCAServiceApplication
LANG : ru_RU.UTF-8
LC_CTYPE : ru_RU.UTF-8
LOGNAME : sk
PATH : /Users/sk/Develop/d20/db/liquibase:/Users/sk/anaconda/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PID : 66249
PWD : /Users/sk/Develop/d20/d20-git-repo/skillcounters-sca-service/skillcounters-sca-service-impl
SecuritySESSIONID : 186a9
SHELL : /bin/bash
SSH_AUTH_SOCK : /private/tmp/com.apple.launchd.ItaWSltKcA/Listeners
TMPDIR : /var/folders/lz/gjd4j2t12_39qs0hpdjqd3sh0000gn/T/
USER : sk
xpc_FLAGS : 0x0
xpc_SERVICE_NAME : com.apple.xpc.launchd.oneshot.0x10000002.idea
__CF_USER_TEXT_ENCODING : 0x1F5:0x0:0x0
awt.toolkit : sun.lwawt.macosx.LWCToolkit
file.encoding : UTF-8
file.encoding.pkg : sun.io
file.separator : /
ftp.nonProxyHosts : local|*.local|169.254/16|*.169.254/16
gopherProxySet : false
http.nonProxyHosts : local|*.local|169.254/16|*.169.254/16
java.awt.graphicsenv : sun.awt.CGraphicsEnvironment
java.awt.headless : true
java.awt.printerjob : sun.lwawt.macosx.CPrinterJob
java.class.path : {all dependent jars go here - excluded them not to pollute issue...}
java.class.version : 52.0
java.endorsed.dirs : /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/endorsed
java.ext.dirs : /Users/sk/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
java.home : /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre
java.io.tmpdir : /var/folders/lz/gjd4j2t12_39qs0hpdjqd3sh0000gn/T/
java.library.path : /Users/sk/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
java.runtime.name : Java(TM) SE Runtime Environment
java.runtime.version : 1.8.0_131-b11
java.specification.name : Java Platform API Specification
java.specification.vendor : Oracle Corporation
java.specification.version : 1.8
java.vendor : Oracle Corporation
java.vendor.url : http://java.oracle.com/
java.vendor.url.bug : http://bugreport.sun.com/bugreport/
java.version : 1.8.0_131
java.vm.info : mixed mode
java.vm.name : Java HotSpot(TM) 64-Bit Server VM
java.vm.specification.name : Java Virtual Machine Specification
java.vm.specification.vendor : Oracle Corporation
java.vm.specification.version : 1.8
java.vm.vendor : Oracle Corporation
java.vm.version : 25.131-b11

这是在 Docker 环境中打印的内容

CA_CERTIFICATES_JAVA_VERSION : 20140324
HOME : /root
HOSTNAME : e297584466e8
JAVA_DEBIAN_VERSION : 8u111-b14-2~bpo8+1
JAVA_HOME : /usr/lib/jvm/java-8-openjdk-amd64
JAVA_VERSION : 8u111
LANG : C.UTF-8
PATH : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PID : 8
PROFILE : prod
PWD : /
awt.toolkit : sun.awt.X11.XToolkit
file.encoding : UTF-8
file.encoding.pkg : sun.io
file.separator : /
java.awt.graphicsenv : sun.awt.X11GraphicsEnvironment
java.awt.headless : true
java.awt.printerjob : sun.print.PSPrinterJob
java.class.path : /maven/skillcounters-skill-service-1.0-SNAPSHOT.jar
java.class.version : 52.0
java.endorsed.dirs : /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/endorsed
java.ext.dirs : /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext:/usr/java/packages/lib/ext
java.home : /usr/lib/jvm/java-8-openjdk-amd64/jre
java.io.tmpdir : /tmp
java.library.path : /usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
java.protocol.handler.pkgs : org.springframework.boot.loader
java.runtime.name : OpenJDK Runtime Environment
java.runtime.version : 1.8.0_111-8u111-b14-2~bpo8+1-b14
java.security.egd : file:/dev/./urandom
java.specification.name : Java Platform API Specification
java.specification.vendor : Oracle Corporation
java.specification.version : 1.8
java.vendor : Oracle Corporation
java.vendor.url : http://java.oracle.com/
java.vendor.url.bug : http://bugreport.sun.com/bugreport/
java.version : 1.8.0_111
java.vm.info : mixed mode
java.vm.name : OpenJDK 64-Bit Server VM
java.vm.specification.name : Java Virtual Machine Specification
java.vm.specification.vendor : Oracle Corporation
java.vm.specification.version : 1.8
java.vm.vendor : Oracle Corporation
java.vm.version : 25.111-b14

任何帮助将不胜感激

解决方法

我在启用 Spoon Launcher 日志记录时找到了一些线索,将其设置为“全部”

这是我在 Docker 环境中得到的

...
The method findAll() is undefined for the type ValuesRepository at /opt/skillcounters/filestorage/github/vipinjo/assignment-consumer/work-tree/d4e050cf3566981ba386e336c5889f8d107abfbb/src/main/java/com/vipinjoseph/assignmentconsumer/service/ValueServices.java:32

并且在本地环境中这些警告不会显示...

存在一些类似于 https://www.programmersought.com/article/47106442813/

的类路径问题

经过将近一天的努力,我找到了解决方案

  1. 使用 Spoon 9.0.0 或更高版本并带有 this 修复
  2. 使用 Spoon Launcher 的父类加载器创建类加载器
  3. 将每个 *.jar 文件直接包含到类加载器 URL 类路径中(不是包含 jar 的目录,而是传递给类加载器的单个 URL 中的每个 jar)
  4. 将此类加载器设置为 InputClassLoader
                URLClassLoader urlClassLoader = URLClassLoader.newInstance(
                                                                jarUrls.toArray(new URL[0]),spoonAPI.getClass().getClassLoader()
                                                );
                spoonAPI.getEnvironment().setInputClassLoader(urlClassLoader);