Swipe to navigate through the chapters of this book
In this chapter, we’re going to explore a decent subset of Spring configuration, and we’re going to shift attention away from "Hello, World" into a simple application that will allow us to explore features and configuration. We’ll introduce the sample application first, then walk through a few different ways to configure it. There’s a lot of code here, much of it redundant on the surface, but we’ll use some base classes to help reduce the tendency to repeat ourselves.
Please log in to get access to this content
The redundancies we’re going to run into will either be required by Java as boilerplate or deliberate choices to illustrate a point. We’ll try to be clear about why such choices are made.
See all of Chapter 1 for how and why – and you thought it’d be okay to skip! … and you thought there’d be fewer footnotes as the book progressed, too, didn’t you?
Wouldn’t it have been neat for Spring to have been called “Human,” so instead of “Spring beans” we had “Human beans?”
A “mutator” is also known as a “setter,” and an “accessor” is known colloquially as a “getter.” Your Humble Authors find these terms to be rather gross, so we’re going to use the correct terms even though they make us sound all hoity-toity and like we’re too proud to drink our wine from a box.
There is no artist known as “Threadbare Loaf” at the time this was written. If you still manage to be a fan of the band, that’s… interesting.
Strictly speaking, this is not true, but artists usually do try to have unique names. The original names for the example app were intended to be “The Heebie Jeebies” and “The Screaming Meemies,” both rejected because there were actual bands with these names already. If “Threadbare Loaf” and the other example band names (“Therapy Zeppelin” and “Clancy in Silt”) used in this chapter actually exist in the real world, they were created and named rather poorly after the book was written.
Worth noting: The opinions of the ideal introductory songs, or hooks, for every real band mentioned are entirely up to the taste of the individual. You might listen to these songs and be revolted; take every suggestion with a grain of salt, unless it refers to the Canadian band Rush, in which case every suggestion is made very seriously.
We’re trying to apply the programming principle known as DRY – “Don’t Repeat Yourself.” The alternative is all WET – “Write Everything Twice.” This concept is given to us courtesy of Andy Hunt and Dave Thomas, in their book The Pragmatic Programmer.
The single class for managing both Artist and Song instances is dictated in this chapter by storing all of our data in memory. If we were using an external datastore, like a relational database accessed through JPA or JDBC, split interfaces would be the better approach. In this chapter, and for our purposes, the single interface and implementation is wiser.
The use of Resettable as an interface name matches the use of Closeable, Serializable, and other such classes in the Java API.
In getMatchingSongNamesForArtist(), the sorted() method for the Stream is probably the most expensive method call. Of course, if you really wanted to make sure, you’d crank up your friendly local profiler and check and leave assumptions to authors and other such miscreants.
If our data model was actually being held in secondary storage, i.e, in a file, or in a database, or on punch cards, persistence would have a step that had nothing to do with the references in memory, and therefore the incorrect changes would propagate only to the scope of the instance. This problem is limited to the in-memory form of the data storage.
We really tried to figure out a good way to make the application code we’ve seen so far related to Spring somehow. That’s a terrible idea, honestly; the fact that we’ve got no technical debt on Spring in our object model is a strength. However, this is a book about Spring, so it’s time to start accruing some technical debt at last. Thankfully, it’ll be beneficial, in the end.
Proof that annotated components can be used by anything managed by a given ApplicationContext can be seen in our tests. The test classes themselves are managed by the Context, and as we’ve seen (and as we’ll see, over and over again), we can wire components into our test classes with wild abandon, and it all works.
In early versions of this chapter, one of the authors wasted quite a bit of time wondering why the configuration was left unassigned. It turned out that he’d forgotten to extend AbstractTestNGSpringContextTests. It’s a silly test, but less silly than it could be.
We’re still trying to make sure we’re not repeating ourselves very often in code.
A test class can load multiple configurations, but it merges them into one context. We have not shown this in operation yet, but the explanation is that a @ContextConfiguration annotation can actually accept an array of configuration files (and other configuration sources, which we’ll learn about later in this chapter), merging them all into one context.
Seriously, run it. Unless something unfortunate’s happened to your source code or ours, the build should run without any failures when you use gradle :chapter3:test – these tests don’t really have visible output, nor should they.
It’s a good thing that every line of code is hit, even though all of these are test classes and the code doesn’t really matter, but don’t make the mistake of thinking that 100% code coverage is an “end goal.” We have 100% coverage, but we’ve not tested any edge cases, and no failure conditions. Again, 100% coverage is good, but it’s a lousy target; you should aim to fulfil all of the specification’s requirements, instead. It just so happens that our “specification” doesn’t contain any error conditions to speak of.
Of course, foo and bar are just as boring, as is the use of baz later in this very chapter. These are known as “metasyntactic variables,” a set of meaningless placeholder words used as names when we don’t want to communicate specific meaning. In this code, they’re actually being used in exactly the wrong sense, since these references do have specific meaning, but we’re using the meaningless words because they help us illustrate a concept.
Yes, we know, these are the same classes, repeated over and over again! We’re painfully aware. We just haven’t shown how to build modular configurations yet – and we actually need slightly different codebases because we’re changing the configuration that’s embedded in the code itself.
We’re programmers. We don’t have to be from Missouri, not that there’s anything wrong with that, but we’re supposed to be a suspicious bunch.
In the writing business, this is called “foreshadowing.” It’s pretty clumsy foreshadowing, honestly, but when we get to the XML configuration section, we’re going to want MusicService3 to have the mutators and accessors.
If you’re wishing we’d have introduced this tag earlier in the chapter… well, us too. However, we’re trying to introduce new pieces of information at a deliberately slow pace, to avoid overwhelming readers new to the concepts we’re discussing, so this was where we finally felt like we had the chance to introduce this one. We’ve found that people usually learn best when new concepts are introduced piecemeal, so we’ve chosen to go fairly slowly.
It might be handy to have a failing condition in this test, but that complicates the data provider greatly, by indicating if a “miss” should fail the test or not. Consider that a quick exercise for the reader.
We’re using “na” to mean “no annotations.”
- Configuration and Declaration of Beans
Joseph B. Ottinger
- Sequence number
- Chapter number
- Chapter 3