You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于JavaParser实现Spring Boot Maven项目类依赖分析与代码生成

Great question! Building a standalone command-line tool for dependency tracing and index building using JavaParser (instead of Eclipse JDT which is tied to plugin development) is totally doable. Here's a step-by-step guide to implement this for your Spring Boot Maven project:

Implementing a Spring Boot Maven Dependency Tracing Tool with JavaParser

1. Project Setup & Dependencies

First, create a new Maven project and add these dependencies to your pom.xml—they cover JavaParser's core/symbol-solving capabilities plus Maven dependency resolution tools:

<dependencies>
    <!-- JavaParser core for parsing source code -->
    <dependency>
        <groupId>com.github.javaparser</groupId>
        <artifactId>javaparser-core</artifactId>
        <version>3.25.8</version>
    </dependency>
    <!-- JavaParser Symbol Solver for resolving type references -->
    <dependency>
        <groupId>com.github.javaparser</groupId>
        <artifactId>javaparser-symbol-solver-core</artifactId>
        <version>3.25.8</version>
    </dependency>
    <!-- Maven tools to parse pom.xml and resolve dependencies -->
    <dependency>
        <groupId>org.apache.maven</groupId>
        <artifactId>maven-model</artifactId>
        <version>3.9.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.maven.resolver</groupId>
        <artifactId>maven-resolver-api</artifactId>
        <version>1.9.13</version>
    </dependency>
    <dependency>
        <groupId>org.apache.maven.resolver</groupId>
        <artifactId>maven-resolver-impl</artifactId>
        <version>1.9.13</version>
    </dependency>
</dependencies>

2. Resolve Maven Dependencies & Build Classpath

Before analyzing code, you need to fetch all jar paths from the target Spring Boot project's dependencies (including local Maven repo jars):

// Parse the target project's pom.xml
MavenXpp3Reader pomReader = new MavenXpp3Reader();
Model mavenModel = pomReader.read(new FileInputStream("/path/to/target/project/pom.xml"));

// Initialize Maven Resolver to fetch dependency paths
RepositorySystem repoSystem = RepositorySystemUtils.newRepositorySystem();
RepositorySystemSession repoSession = MavenRepositorySystemUtils.newSession();
LocalRepository localRepo = new LocalRepository(System.getProperty("user.home") + "/.m2/repository");
repoSession.setLocalRepositoryManager(repoSystem.newLocalRepositoryManager(repoSession, localRepo));

// Add Maven Central (or your private repos)
List<RemoteRepository> remoteRepos = List.of(
    new RemoteRepository.Builder("central", "default", "https://repo1.maven.org/maven2/").build()
);

// Resolve full dependency tree
DependencyRequest depRequest = new DependencyRequest();
depRequest.setRoot(new org.apache.maven.model.Dependency(
    mavenModel.getGroupId(), mavenModel.getArtifactId(), mavenModel.getVersion()
));
depRequest.setRepositories(remoteRepos);

DependencyResult depResult = repoSystem.resolveDependencies(repoSession, depRequest);
List<String> jarPaths = depResult.getArtifactResults().stream()
    .map(ArtifactResult::getArtifact)
    .map(Artifact::getFile)
    .map(File::getAbsolutePath)
    .filter(path -> path.endsWith(".jar"))
    .collect(Collectors.toList());

3. Configure JavaParser with Symbol Solver

The Symbol Solver is key to linking method calls (like StringUtils.rpad()) to their actual class definitions in jars:

// Combine type solvers for project source + dependency jars
CombinedTypeSolver typeSolver = new CombinedTypeSolver();
// Add target project's source code
typeSolver.add(new JavaParserTypeSolver(new File("/path/to/target/project/src/main/java")));
// Add all dependency jars
for (String jarPath : jarPaths) {
    typeSolver.add(new JarTypeSolver(new File(jarPath)));
}

// Initialize JavaParser with symbol resolution enabled
ParserConfiguration parserConfig = new ParserConfiguration();
parserConfig.setSymbolResolver(new SymbolSolver(typeSolver));
JavaParser javaParser = new JavaParser(parserConfig);

4. Trace Method Calls to Their Jar Origins

Now you can scan the target project's source code, find method calls, and map them to their containing jars:

// Walk through all Java files in the target project
Files.walk(Paths.get("/path/to/target/project/src/main/java"))
    .filter(path -> path.toString().endsWith(".java"))
    .forEach(path -> {
        try {
            CompilationUnit compilationUnit = javaParser.parse(path.toFile()).getResult().orElseThrow();
            // Find all method call expressions
            compilationUnit.findAll(MethodCallExpr.class).forEach(methodCall -> {
                methodCall.resolve().ifPresent(resolvedMethod -> {
                    ResolvedTypeDeclaration declaringClass = resolvedMethod.declaringType();
                    String className = declaringClass.getQualifiedName();
                    // Find which jar contains this class
                    Optional<JarTypeSolver> jarSource = typeSolver.solvers().stream()
                        .filter(s -> s instanceof JarTypeSolver)
                        .map(s -> (JarTypeSolver)s)
                        .filter(s -> s.tryToSolveType(className).isPresent())
                        .findFirst();
                    
                    jarSource.ifPresent(solver -> {
                        System.out.printf("Call to %s.%s() found in jar: %s%n",
                            className, resolvedMethod.getName(), solver.getJarFile().getAbsolutePath());
                    });
                });
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    });

For example, when this code encounters StringUtils.rpad(...), it will output the full path to the Spring jar containing that StringUtils class.

5. Build a Class Index for Refactoring Support

To enable refactoring operations, store parsed class/method data in an index for quick lookup:

// Simple class to hold class metadata
class ClassMetadata {
    private String qualifiedName;
    private String jarPath;
    private List<String> methodNames;

    // Constructor, getters, setters omitted for brevity
}

// Build the index
Map<String, ClassMetadata> classIndex = new HashMap<>();
for (String jarPath : jarPaths) {
    JarTypeSolver jarSolver = new JarTypeSolver(new File(jarPath));
    jarSolver.getAllTypeNames().forEach(className -> {
        jarSolver.tryToSolveType(className).ifPresent(type -> {
            List<String> methods = type.getDeclaredMethods().stream()
                .map(ResolvedMethodDeclaration::getName)
                .collect(Collectors.toList());
            classIndex.put(className, new ClassMetadata(className, jarPath, methods));
        });
    });
}

You can use this index to quickly find all references to a class/method, or batch-update method calls during refactoring.

6. Package as a Standalone Command-Line Tool

Use Maven's Assembly Plugin to package your tool into an executable jar with all dependencies:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.6.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.yourpackage.Main</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>build-executable-jar</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Run it from the command line like this:

java -jar your-dependency-trace-tool.jar --project /path/to/your/spring-boot-project

Key Notes

  • Spring Boot Fat Jars: If your target project uses a fat jar, you'll need to either extract it first or use a library like spring-boot-loader to access internal jars.
  • Performance: For large projects, parallelize file scanning and cache the class index to speed up repeated runs.
  • Complex Types: JavaParser's Symbol Solver handles most cases, but you may need to add custom logic for edge cases like nested classes or generic method references.

内容的提问来源于stack exchange,提问作者user1539343

火山引擎 最新活动