Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Browser testing is essential if you have a website or web applications that users interact with. Manual testing can be very helpful to an extent, but given the multiple browsers available, not to mention versions and operating system, testing everything manually becomes time-consuming and repetitive.

To help automate this process, Selenium is a popular choice for developers, as an open-source tool with a large and active community. What's more, we can further scale our automation testing by running on theLambdaTest cloud-based testing platform.

Read more through our step-by-step tutorial on how to set up Selenium tests with Java and run them on LambdaTest:

>> Automated Browser Testing With Selenium

Partner – Orkes – NPI EA (cat=Java)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

1. Introduction

In this article, we’re going to have a look at the JavaParser library. We’ll see what it is, what we can do with it, and how to use it.

2. What Is JavaParser?

JavaParser is an open-source library for working with Java sources. It allows us to parse Java source code into an abstract syntax tree (AST). Once we’ve done this, we can analyze the parsed code, manipulate it, and even write new code.

Using JavaParser, we can parse source code written in Java up to Java 18. This includes all stable language features but may not include any preview features.

3. Dependencies

Before we can use JavaParser, we need to include the latest version in our build, which is 3.25.10 at the time of writing.

The main dependency that we need to include is javaparser-core. If we’re using Maven, we can include this dependency in our pom.xml file:

<dependency>
    <groupId>com.github.javaparser</groupId>
    <artifactId>javaparser-core</artifactId>
    <version>3.25.10</version>
</dependency>

Or if we’re using Gradle, we can include it in our build.gradle file:

implementation("com.github.javaparser:javaparser-core:3.25.10")

At this point, we’re ready to start using it in our application.

Two additional dependencies are available as well. The dependency com.github.javaparser:javaparser-symbol-solver-core provides a means to analyze the parsed AST to find the relationships between Java elements and their declarations. The dependency com.github.javaparser:javaparser-core-serialization provides a means to serialize the parsed AST to and from JSON.

4. Parsing Java Code

Once we have the dependencies set up in our application, we’re ready to go. Parsing of Java code always starts with the StaticJavaParser class. This gives us several different mechanisms for parsing code, depending on what we’re parsing and where it’s coming from.

4.1. Parsing Source Files

The first thing we’ll look at parsing is the entire source files. We can do this with the StaticJavaParser.parse() method. Several overloaded alternatives allow us to provide the source code in different ways – directly as a string, as a File on the local filesystem, or as an InputStream or Reader for some resource. All of these work the same way and are simply convenient ways to provide the code to be parsed.

Let’s see it in action. Here, we’ll attempt to parse the provided source code and generate a CompilationUnit as a result:

CompilationUnit parsed = StaticJavaParser.parse("class TestClass {}");

This represents our AST and lets us inspect and manipulate the parsed code.

4.2. Parsing Statements

Individual statements are at the other end of the spectrum of the code that we can parse. We do this using the StaticJavaParser.parseStatement() method. Unlike with source files, there’s only a single version of this that takes a single string containing the statement to parse.

This method returns a Statement object that represents the parsed statement:

Statement parsed = StaticJavaParser.parseStatement("final int answer = 42;");

4.3. Parsing Other Constructs

JavaParser can also parse many other constructs, covering the entire Java language up to Java 18. Each construct has a separate, dedicated parse method and returns an appropriate type representing the parsed code. For example, we can use parseAnnotation() for parsing annotations, parseImport() for import statements, parseBlock() for parsing blocks of statements, and many more.

Internally, JavaParser will use the exact same code for parsing the various parts of our code. For example, when parsing a block using parseBlock(), JavaParser will ultimately end up in the same code as is called directly by parseStatement(). This means we can rely on these different parsing methods working the same for the same subsets of code.

We do need to know exactly what type of code we’re parsing in order to select the correct parsing method. For example, using the parseStatement() method to parse a class definition will fail.

4.4. Malformed Code

If parsing fails, the JavaParser will throw a ParseProblemException indicating exactly what was wrong with the code. For example, if we attempt to parse a malformed class definition, then we’ll get something like:

ParseProblemException parseProblemException = assertThrows(ParseProblemException.class,
    () -> StaticJavaParser.parse("class TestClass"));

assertEquals(1, parseProblemException.getProblems().size());
assertEquals("Parse error. Found <EOF>, expected one of  \"<\" \"extends\" \"implements\" \"permits\" \"{\"", 
    parseProblemException.getProblems().get(0).getMessage());

We can see from this error message that the problem is that the class definition is wrong. In Java, such a statement must be followed by either a “<“ – for a generic definition, the extends or implements keyword, or else a “{“ to start the actual body of the class.

5. Analyzing Parsed Code

Once we’ve parsed some code, we can start analyzing it to learn from it. This is similar to reflection within a running application, only on the parsed source code instead of the currently running code.

5.1. Accessing Parsed Elements

Once we’ve parsed some source code, we can query the AST to access individual elements. Exactly how we do this varies depending on the elements we want to access and what we’ve parsed.

For example, if we’ve parsed a source file into a CompilationUnit, then we can access a class that we expect to be present using getClassByName():

Optional<ClassOrInterfaceDeclaration> cls = compilationUnit.getClassByName("TestClass");

Note that this returns an Optional<ClassOrInterfaceDeclaration>. Optional is used because we can’t guarantee the type is present in this compilation unit. In other cases, we might be able to guarantee the presence of elements. For example, a class will always have a name, so ClassOrInterfaceDeclaration.getName() doesn’t need to return an Optional.

At every stage, we can only directly access elements that are at the outermost level of what we’re currently working with. For example, if we’ve got a CompilationUnit from parsing a source file, then we can access the package declaration, the import statements, and the top-level types, but we can’t access the members within those types. However, once we access one of these types, we can then access the members within it.

5.2. Iterating Parsed Elements

In some cases, we may not know exactly what elements are present in our parsed code, or else we simply want to work with all of a certain type of element instead of only one.

Each of our AST types can access an entire range of appropriate nested elements. Exactly how this works depends on what we want to work with. For example, we can extract all of the import statements out of a CompilationUnit using:

NodeList<ImportDeclaration> imports = compilationUnit.getImports();

No Optional is needed, as this is guaranteed to return a result. However, if no imports were present, this result might be an empty list.

Once we’ve done this, we can treat this as any collection. The NodeList type implements java.util.List correctly so we can work with it exactly as any other list.

5.3. Iterating the Entire AST

In addition to extracting exactly one type of element from our parsed code, we can also iterate over the entire parsed tree. All AST types from JavaParser implement the visitor pattern, allowing us to visit every element in our parsed source code with a custom visitor:

compilationUnit.accept(visitor, arg);

There are then two standard types of visitors that we can use with this. Both of these have a visit() method for each possible AST type, which takes a state argument that’s passed into the accept() call.

The simplest of these is VoidVisitor<A>. This has one method for every AST type and no return values. We then have an adapter type – VoidVisitorAdapter – that gives us a standard implementation to help ensure that the entire tree is correctly called.

We then only need to implement the methods that we’re interested in – for example:

compilationUnit.accept(new VoidVisitorAdapter<Object>() {
    @Override
    public void visit(MethodDeclaration n, Object arg) {
        super.visit(n, arg);

        System.out.println("Method: " + n.getName());
    }
}, null);

This will output a log message for every method name in the source file, regardless of where they are. The fact that this recurses over the entire tree structure means that these methods can be in top-level classes, inner classes, or even anonymous classes within other methods.

The alternative is GenericVisitor<R, A>. This works similarly to VoidVisitor, except that its visit() methods have a return value. We also have adapter classes here, depending on how we want to collect the return values from each method. For example, GenericListVisitorAdaptor will force us to have our return type from each method be a List<R> instead and merge all of these lists together:

List<String> allMethods = compilationUnit.accept(new GenericListVisitorAdapter<String, Object>() {
    @Override
    public List<String> visit(MethodDeclaration n, Object arg) {
        List<String> result = super.visit(n, arg);
        result.add(n.getName().asString());
        return result;
    }
}, null);

This will return a list that contains the names of every method in the entire tree.

6. Outputting Parsed Code

In addition to parsing and analyzing our code, we can also output it again as a string. This can be useful for many reasons – for example, if we want to extract and output only specific sections of the code.

The easiest way to achieve this is to simply use the standard toString() method. All of our AST types correctly implement this and will produce formatted code. Note that this might not be formatted exactly as it was when we parsed the code, but it’ll still follow relatively standard conventions.

For example, if we parse the following code:

package com.baeldung.javaparser;
import java.util.List;
class TestClass {
private List<String> doSomething()  {}
private class Inner {
private String other() {}
}
}

When we format it, we’ll get this as the output:

package com.baeldung.javaparser;

import java.util.List;

class TestClass {

    private List<String> doSomething() {
    }

    private class Inner {

        private String other() {
        }
    }
}

The other method we can use for formatting code is using the DefaultPrettyPrinterVisitor. This is a standard visitor class that will handle formatting. This gives us the advantage of configuring some aspects of how the output is formatted. For example, if we wanted to indent with two spaces instead of four, we could write:

DefaultPrinterConfiguration printerConfiguration = new DefaultPrinterConfiguration();
printerConfiguration.addOption(new DefaultConfigurationOption(DefaultPrinterConfiguration.ConfigOption.INDENTATION,
    new Indentation(Indentation.IndentType.SPACES, 2)));
DefaultPrettyPrinterVisitor visitor = new DefaultPrettyPrinterVisitor(printerConfiguration);

compilationUnit.accept(visitor, null);
String formatted = visitor.toString();

7. Manipulating Parsed Code

Once we’ve parsed some code into an AST, we’re also able to make changes to it. Since this is now just a Java object model, we can treat it as any other object model, and JavaParser gives us the ability to change most aspects of it freely.

Combining this with the ability to output our AST back as working source code means that we can then manipulate parsed code, make changes to it, and provide the output in some form. This could be useful for IDE plugins, code compilation steps, and much more.

This can be used in any way that we have access to the appropriate AST elements – whether that’s from directly accessing them, iterating with a visitor, or whatever makes sense.

For example, if we wanted to uppercase every single method name in a piece of code, then we could do something like:

compilationUnit.accept(new VoidVisitorAdapter<Object>() {
    @Override
    public void visit(MethodDeclaration n, Object arg) {
        super.visit(n, arg);
        
        String oldName = n.getName().asString();
        n.setName(oldName.toUpperCase());
    }
}, null);

This uses a simple visitor to visit every method declaration in our source tree and uses the setName() method to give each method a new name. The new name is then simply the old name in uppercase.

Once this is done, the AST is updated in place. We can then format it however we wish, and the newly formatted code will reflect our changes.

8. Summary

We’ve seen here a quick introduction to JavaParser. We’ve shown how to get started with it and some of the things we can achieve using it. Next time you need to manipulate some Java code, why not try it out?

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)