Spring + Your Code = ❤️ Most of the time!
The one criticism that sticks to the Spring framework (and other big web frameworks, to be honest), is that Spring does so much for you it can be hard to understand what’s really going on. Sometimes, we can just follow the doc and tutorials and watch the magic happen. But sometimes, we really need to understand how Spring, and related packages, actually work. And even more importantly, how they work with our code.
You’re already familiar with using debuggers - and Java has an excellent debugger compared to some other languages. When you use a debugger, you take an “inside-out” approach to troubleshooting. You choose a point in your code where you want to start, and then you can explore outwards from there. But while you can get a lot of detailed information that way, it’s hard to build an understanding of what’s going on overall in the codebase.
To build that kind of high-level understanding, you need more of an “outside-in” approach. Here’s a cookbook you can follow to see how your code works with Spring and other Java libraries, starting from the widest scope and narrowing in on details. To do that, I will show you how to use an open source tool called AppMap. AppMap records the runtime behavior of your code and stores it as JSON files. Then you can open AppMap Diagrams in your code editor (VSCode or IntelliJ) and view and search dependency maps and execution trace diagrams.
Here’s how you use it:
-
Install the appmap-java agent
Follow the guide for VSCode or IntelliJ .
Here's a quick checklist:- Add the appmap Java agent to your Maven or Gradle configuration - or just download the JAR file
-
Create
appmap.yml
and configure your project name and primary package names.
-
Add additional package names, such as
Here's an exampleorg.springframework.web
, toappmap.yml
appmap.yml
that I use with my fork of the Spring Pet Clinic. -
Record the AppMap of code execution
You have several choices of how to record your code:
-
Option 1: Test case(s)
If you have JUnit or TestNG tests that cover your app, run your tests with the AppMap Gradle or Maven integration enabled. -
Option 2: Record user actions and API requests
If you don't have a test that does what you need, you can use your app and get an AppMap of all the actions you perform. This is called “remote recording,” and to use it, you run your web server with the flag-javaagent:appmap-agent.jar
. If your app is an API server, run the server and send API requests. Either way, you'll get anappmap.json
file when the server exits. -
Option 3: Record the entire server run, including startup and shutdown
The AppMap Java agent supports a System propertyappmap.recording.auto
. If you set this property totrue
, the server process is recorded from start to finish, and the results are written to atimestamped appmap.json
file when the process exits.
-
Option 1: Test case(s)
-
View the diagrams
The AppMap extension for VSCode and IntelliJ enables you to open any
*.appmap.json
and explore it visually. To open AppMap Diagrams in the code editor, open the AppMap “Tool Window” (IntelliJ) or AppMap sidebar (VSCode).Click on an AppMap to view the Dependency map in your code editor window. From there, you can search, browse, and drill down into the Trace view.
Demo - Pet Clinic + org.springframework.web
As I indicated above, AppMap is a flexible tool and there are several ways to use it. Let’s start with a fairly simple, but quite useful and illustrative example.
A Spring Controller is a pretty complex mixture of methods and annotations - even the simple Pet Clinic “OwnerController” has at least 5 different annotations used in multiple different ways. Code like this is powerful, but unlike “normal” procedural or functional code, there’s no information in the code about how the functions are used, how they fit together, or which ones are used in a particular use case.
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
private VisitRepository visits;
public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
this.owners = clinicService;
this.visits = visits;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
@GetMapping("/owners/new")
public String initCreationForm(Map<String, Object> model) {
Owner owner = new Owner();
model.put("owner", owner);
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
@PostMapping("/owners/new")
public String processCreationForm(@Valid Owner owner, BindingResult result) {
if (result.hasErrors()) {
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
else {
this.owners.save(owner);
return "redirect:/owners/" + owner.getId();
}
}
@GetMapping("/owners/find")
public String initFindForm(Map<String, Object> model) {
model.put("owner", new Owner());
return "owners/findOwners";
}
}
The org.springframework.web
is a package that orchestrates these snippets of Java code based on their annotations.
To get a map of how an Owner request works, I’ll run the Pet Clinic with remote recording enabled, then use the AppMap extension for IntelliJ to create a recording of a web request. This is a bit easier to watch than to explain, so check out the video above for a walkthrough of all this.
To recreate this yourself, check out the appmap-e2e branch of Spring pet clinic
To record and review your own runtime code maps directly in your code editor, download the free AppMap plugin for JetBrains.