
Lesson Description
The "Java Development Best Practices" Lesson is part of the full, Enterprise Java with Spring Boot course featured in this preview video. Here's what you'd learn in this lesson:
Josh discusses some general Java/Spring best practices. These include RAII (resource acquisition is initialization), dependency injection, portable service abstraction, and aspect-oriented programming. These topics are not Spring-specific, but create better-architected Spring applications.
Transcript from the "Java Development Best Practices" Lesson
[00:00:00]
>> Josh Long: Now, Spring is a technology that was created, like I said back in early 2000s, 2001 was when the first author tag was written, right, the first date for contribution in Java. And the core idea behind it is that it supports this thing called dependency injection. What does that mean?
[00:00:19]
Well, it's commonplace now. Even Angular has it, even the JavaScript people understand dependency injection. But it was a big deal 25 years ago. And the reason it was such a big deal is because compared to what was prevalent in the Java ecosystem at the time, which was hideous, this is a breath of fresh air.
[00:00:37]
I think that's one of the reasons why the technology is called Spring is because it's just so much cleaner and nicer. And the dinosaurs that roamed the Earth at the time were something called J2EE, which has changed its name three different times since then. So it's now, there was Java EE, and then Jakarta EE.
[00:00:53]
Jakarta EE and Java EE are far, far better than J2EE, which we can all agree was abhorrent. J2EE at the time was written, I think, because remember, some of this stuff started coming together in the late 90s. And one of the patterns that you would have seen as a C programmer in the late 90s or a C programmer is RAI.
[00:01:15]
Resource acquisition is initialization. How many of you have ever heard of this pattern or anti pattern in Java? The idea is in C you want to allocate some memory, do something with it, and then free it as quickly as possible. And so you have code where you have these duplicative allocations of resources, and then the freeing up of that resource so that it doesn't leak, which is again, I know smart pointers and the like from Boost and now in the C++ standard have come a long way since then.
[00:01:43]
But at the time, this is not the case, okay? And so you see a lot of code from that time that was written in terms of RAII in Java and it doesn't make any sense because Java has object orient. It has a garbage collection, rather. So let's look at this code here, right?
[00:01:55]
I've got some sample code. You can just follow along. I'm not going to type anything here. It's in the beans folder there. We're going to go pretty quickly here, so let's make sure we're all here on the same page. Here's some code you should not write. This is a customer service implementation and I've got an embedded database and I'm creating the database.
[00:02:14]
But imagine me creating a database connection to a data source right in place Imagine me initializing it and putting the username and the password in the class where I'm using the data source. That's not a good idea. And then imagine me writing code using the lowest level abstraction for talking to a SQL database in Java using JDBC directly.
[00:02:34]
This code is an embarrassment of awful, right? It's just so many things that could be better. So first of all, what if I have more than one object that needs a pointer to the data source? Why would I create a new data source per component? I wouldn't want to do that.
[00:02:49]
It's a waste of memory. Especially when data sources typically have thread pools, which in turn have, well, threads, which means that you can't really scale that up efficiently. And then same thing over here. What is the business logic for this? I'm getting the ID and the name from a table called Customer and I am mapping each result into an object called Customer.
[00:03:08]
That's it. Everything else is just boilerplate. Allocating a statement, allocating a connection, clearing it, handling exception. I'm not even handling exceptions, but I should be. So this is bad code, but this is RAII, Resource Acquisition is Initialization. I'm doing everything in place and I'm not letting the natural sort of elegance of object oriented programming shine through.
[00:03:28]
So then you get to, let me see, Readme here. Then you get to dependency injection, which is just, you know, just means writing code with constructors. It's not a big idea, right? So here I take that awful code that we had before and it's still verbose. I'm still doing the absolute least pleasant thing I can do without the benefit of any technology at all, basically, except for the core jdbc, spi, JDBC being the way that Java talks to databases, right?
[00:03:53]
Like I don't know what's the equivalent in most of the languages, right? It's just, yeah, imagine using straight ADL, or straight, what is the Python equivalent? It'll come to me. Anyway, the point is I'm injecting a data source here. So at least the data source is now being defined somewhere else and I can reuse it so I can pass around the pointer to the data source to multiple places and you can see that that code is testable here in this.
[00:04:23]
So, so I've got the data source extracted out, I'm initializing the schema, I've got this little handy utility thing here that will load schema SQL and data SQL and it'll run the SQL data source initializer. And then finally, once that's confirmed, I actually have a Valid object that I can then use, right?
[00:04:41]
If I go back to my test over here, the object, the database, is correct. And now I can create my customer service passing in a pointer to the data source. And then I can confirm that there are two records in the database by reading them from the database like that.
[00:04:55]
So that works fine. It's a little bit better, but it's still very, very verbose, right? I'm still writing a lot of JDBC code, so what else can we do? Well, what's another thing that Spring introduced at the time, right? So we had this, we used to talk about the Spring triangle.
[00:05:08]
So you've got dependency injection, which works against, undoes the nastiness of things like raii, right? But then you also have portable service abstractions. Portable service abstractions are the second leg of the three legs of the Spring triangle, right? There's three sides. And portable service abstractions are basically utility abstractions that make working with these tedious lower level APIs more pleasant, okay?
[00:05:34]
And at the time we had something called the JDBC template, which that was like a gateway for a lot of people who wanted to get into using Spring, but. But they could only bring, they could only treat it like a utility library. Maybe they didn't have the ability to use it as a full on framework yet, but they had some utilities they wanted to use, so portable.
[00:05:51]
I could take that code now that we just wrote and refactor it now to use the JDBC client. So here's the exact same code, right? But if I look at the where's my customer service here? This is using the JDBC client, right? This is a newer. There's a JDBC template which is from 20 years ago.
[00:06:07]
There's the JDBC client, which is newer, but the same idea, right? I've refactored that code to express the essence of what I care about. So I'm injecting not the jdbc, I'm not injecting the database itself, I'm injecting a JDBC client which in turn has the database. I do that here.
[00:06:22]
So I create that and then I pass that into the customer service and look at the code, right? Select all from id, select id name from customer. And then the essence is for each row, map it to this object. I'm not writing boilerplate code anymore. This is just an object that will return the records from the database.
[00:06:38]
Pretty straightforward, okay? I'm not dealing with allocating statements or connections or any of that stuff anymore. It's just much cleaner. So already I'm stripping Away noise. And I'm leaving behind essential business logic. But still, there are some concerns that are common to all my objects. And normally in object oriented programming, you enforce these common things by having common roots.
[00:07:01]
But there are things that are sometimes important to all objects, even if they're not in a hierarchical sense the parent of that thing, right? You wouldn't say they don't have that relationship. But these concerns need to be cared for. So, for example, transaction demarcation or auditing, logging, right, things like that.
[00:07:23]
So how would I do that? What if I wanted to do logging? What if I wanted to have timings around each method invocation? Whenever somebody calls a service, I want to emit telemetry to another thing. Well, that's the thing I wanna do across all my code. Do I want to have a base class that does this?
[00:07:39]
Do I want to make everybody call that superclass method, right? I don't want to do that. That seems very ugly. What if I had some way to transparently using object orientation and information hiding and polymorphism. What if I had some way to add behavior to a type in such a way that the consumer of that type doesn't know that they've been.
[00:08:00]
They've in effect been duped, they've had something swap out. So let's look at that example again. I've got my customer service, but I'm using this thing I wrote called the loggable proxymaker proxy. And I'm passing this customer service. This is still customer service. The contract is still customer service.
[00:08:20]
So anybody that was expecting a customer service will still get a customer service. But they're not actually getting a customer service. They're getting a proxy. They're getting a subclass of customer service that adds extra behavior. And that subclass was written programmatically. That is to say we did it at runtime.
[00:08:36]
We created the subclass at runtime, extending the class that we provided to add this extra behavior because of polymorphism. Anybody that was expecting customer service before will still be able to say they got a customer service. It's just a subclass. So let's look at this code here. This is a little low level.
[00:08:53]
This is not stuff most people should have to write, but it's not hard to do either. This is a capability. Well, this is a spring object that is using two facilities, one of which was introduced in Java 1.4 in 2001. So it's not a hard thing to understand or whatever.
[00:09:10]
Basically we are programmatically, we're saying to Java, create an implementation of this interface or of this type the target. And then, whenever somebody calls a method, here's an event listener. When the method gets called, call this event listener and give me a pointer to the on like as the method is being invoked on the target.
[00:09:32]
When somebody calls, get customers or whatever. Give me a chance to look at the invocation of the method and then I can start some clock, I can capture the current time and the end time in the finally block block and I get the result there and I can then log it out or I can send it to Prometheus or something.
[00:09:54]
Then that's it. So I'm using this thing. It's automatically adding this capability to every method in that object that I pass in. But it's giving me back something that is a customer service. But if you look at the actual type at runtime, it's not customer service itself, it's actually customer service dollar sign and then some inscrutable string of characters.
[00:10:15]
It's a dynamically generated subclass at runtime. We are in effect compiling new code when the program starts up. This is why it's very weird for people who have never seen Java before. They don't realize that it's just as dynamic as JavaScript, or Python, or whatever. You can do eval in effect in Java in a roundabout circumlocutus way.
[00:10:37]
But you can. That's a good thing, as we'll see later when we talk about Graalvm a bad thing, okay? So let's see, if I actually run this code, okay? Let's run this code up to this point and we'll put our cursor and look at the actual generated class here.
[00:10:53]
Customer service. You see that? That's not just customer service, that's customer service. No, this one over here. Customer service $spring CGLIB blah blah blah blah. The class name was clearly synthetic. It's not a real class, right? So run this. Okay, so that AOP is the third leg. So you've got Aspect Oriented programming.
[00:11:16]
This idea of introducing aspects of behavior that influence the form of a lot of different objects. That's called Aspect Oriented programming. There's actually a much higher level abstraction in Spring for dealing with this kind of stuff. You don't ever have to do something like this. But the point is it's there, it's pervasive, and it's one of the ways that Spring is able to do magic tricks for you.
[00:11:34]
You create just a random class and you add an annotation and suddenly you've got an HTTP endpoint. How it's because Spring is able to add behavior to these objects, and it's able to do a bait and switch. You say, hey, I'm advertising a customer service. Somebody else says, hey, I want to inject a customer service.
[00:11:52]
In between, the framework sits there and it can do a swap. It takes your regular customer service and adds this logging ability and then gives that to the thing that depends upon it. That swap is the that indirection is so key to so much of what Spring does.
[00:12:07]
Okay.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops