You've worked on at least a couple of Spring applications.
Achieve fluency in constructing, configuring, and executing a Spring Application Context:
- essential of the parts of an Application Context;
- the role of each part;
- which parts are contributed by which flavor of Application Context;
- how Spring Beans are added to the Bean Factory;
- how properties are loaded (and how/why various sources are ordered).
- Enjoy!. Think of Spring Framework as a tinker toy: let poke and explore just for the fun of it! You'll be amazed at how much more you'll retain when you're actually enjoying the experience.
- GO SLOW. The clearer your understanding of these fundamental bits, the stronger your foundation. The stronger your foundation, the more solidly you'll be able to grow your knowledge of more advanced concepts.
- Use Your Tools. The IDE's debugger and code exploration features are powerful ways to bring shape to the code you are reading; it's worth taking the time to learn them.
- IntelliJ:
- "Type Hierarchy" (Ctrl+H) — pick a core interface (e.g.
ApplicationContext) and explore the tree of implementors. - "Download sources..." when opening a class from a dependency, it's always a good idea to grab the source code. Spring source code is pleasant to read.
- "Quick Documentation" (F1) — when viewing a class or method, hit F1 to see a rendering of its JavaDoc.
- out of the box, IntelliJ displays this view as a kind of "pop-up". You might it useful to: make it a tool (click the pin in the top-right corner of the window), and and dock it on the bottom (or side).
- "Type Hierarchy" (Ctrl+H) — pick a core interface (e.g.
- IntelliJ:
Objective: explore the innards of the simplest Application Context, breaking it down into its constituent parts and looking for how they work together.
-
Create a Java program that instantiates the simplest concrete type of Application Context available (i.e.
StaticApplicationContext);StaticApplicationContext simplestAppCtx = new StaticApplicationContext(); simplestAppCtx.refresh(); simplestAppCtx.close();
If you're using Gradle, here's your minimal
build.gradle:apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'org.springframework:spring-context:4.3.7.RELEASE' }
-
Run it in the debugger, set a breakpoint after the
refresh()call and explore the contents of the ApplicationContext object.Among all the many objects within the Application Context, let's find and examine each:
- there are six (6) singleton beans in this plain-vanilla application
context. Can you find them within the Application Context?
- hint: you're looking for a Map between the bean names and their actual instances.
- what are they?
- what does each of them do?
- Does that square with the JavaDoc for
ApplicationContext?
- Does that square with the JavaDoc for
- there are six (6) singleton beans in this plain-vanilla application
context. Can you find them within the Application Context?
-
Turn up JDK logging to
FINESTfororg.springframework(enabling logging).- You might want to decrease the font size of the console and/or increase the line spacing to make the output more readable.
-
Let's use the logging output to tell the story of how an Application Context initializes.
- Run the application again.
- Start from the top of the log output, for each line attempt to translate to "english".
-
Let's add an Application Context for one that knows how to detail with annotation-based config,
AnnotationConfigApplicationContext:StaticApplicationContext simplestAppCtx = new StaticApplicationContext(); simplestAppCtx.refresh(); AnnotationConfigApplicationContext annotationAppCtx = new AnnotationConfigApplicationContext(SimpleHelloWorld.class); annotationAppCtx.refresh(); simplestAppCtx.close(); annotationAppCtx.close();
Let's see what more the "Annotation Config" part of the
AnnotationConfigApplicationContextbrings in:- now there are 12 singleton beans (twice as many!)... what are the new ones?
- does their bean name match their type?
- of each of these new singletons, what do they do?
- of the beans you saw in the prior step (
environment,messageSource, ...), do any change in their type or configuration? - what additional steps does this flavor of Application Context show up in the logs?
- now there are 12 singleton beans (twice as many!)... what are the new ones?
-
Either revert back to the first step of Part 1, above, or create a new Java program that instantiates a
StaticApplicationContext, calls some lifecycle events and closes:StaticApplicationContext appCtx = new StaticApplicationContext(); appCtx.refresh(); appCtx.close();
-
Explicit special bean.
Define a class that implements
ApplicationListenerand outputs events that are triggered:class Peeker implements ApplicationListener<ApplicationEvent> { private static final Logger log = Logger.getLogger(Peeker.class.getName()); @Override public void onApplicationEvent(ApplicationEvent event) { log.info("Received Event: " + event); } }
Explicitly add
Peekeras an application listener usingApplicationContext#addApplicationListener().appCtx.addApplicationListener(new Peeker());
Re-run the app and see the listener's output.
-
Implicit special bean.
Remove the explicit add from the previous step. Instead, add
Peekeras a singleton:appCtx.registerSingleton("peeker", Peeker.class);
Compare the output from the previous step. What other kinds of beans are specially handled? (hint: look for invocations of
AbstractApplicationContext#getBeanNamesForType()). -
By default beans are singleton.
Replace the
StaticApplicationContextwithAnnotationConfigApplicationContextand registerPeekerwithregister()instead ofregisterSingleton().AnnotationConfigApplicationContext appCtx = new AnnotationConfigApplicationContext(); appCtx.register(Peeker.class);
Re-run the application and note what changes (and what doesn't).
-
Explicit configuration (declaring beans).
Add another class to house the configuration:
@Configuration class HelloWorldConfig { @Bean public static Peeker peeker() { return new Peeker(); } }
... and instead of registering
Peekerexplicitly, register this configuration class, instead:appCtx.register(HelloWorldConfig.class);
Run the application in the debugger again.
- note what new singletons appear in the BeanFactory.
- is a class annotated with
@Configurationa Spring bean? - looking at the logging output, trace through how the
Peekerbean was added the Application Context:- which component contributes the BeanDefinition for
Peeker? - which component actually creates an instance of
Peeker?
- which component contributes the BeanDefinition for
-
Scanned configuration.
Remove the explicit registration of the configuration class,
HelloWorldConfig. Instead, use the scanning feature of theAnnotationConfigApplicationContext:appCtx.scan("hello.simple");
... re-run the application and examine the logging output:
- was
Peekerincluded or ignored up byClassPathBeanDefinitionScanner? - which bean was included by
ClassPathBeanDefinitionScanner?
- was
-
Component scan.
- move the
@Beanmethod fromHelloWorldConfigintoSimpleHelloWorldand delete theHelloWorldConfigclass. - specify
SimpleHelloWorldas the root configuration class by specifying it as the parameter the constructor forAnnotationConfigApplicationContext. - annotate
SimpleHelloWorldas a@Configurationclass. - annotate
Peekeras a@Componentclass.
SimpleHelloWorldshould look something like this:@Configuration @ComponentScan("hello.simple") public class SimpleHelloWorld { public static void main(String[] args) { AnnotationConfigApplicationContext appCtx = new AnnotationConfigApplicationContext(SimpleHelloWorld.class); appCtx.refresh(); appCtx.close(); } }
Peekershould look something like this:@Component class Peeker implements ApplicationListener<ApplicationEvent> { private static final Logger log = Logger.getLogger(Peeker.class.getName()); @Override public void onApplicationEvent(ApplicationEvent event) { log.info("Received Event: " + event); } }
... and rerun the application. The intent, here, is that this is equivalent to the previous state.
- is
@ConfigurationonSimpleHelloWorldstrictly required? - is the
"hello.simple"value of@ComponentScanstrictly required?
- move the
-
Spring Boot-supplied Application Context (minimally-activated).
Replace the direct dependency on
spring-contextwith the Spring Boot base artifact (notice: not the starter).dependencies { compile 'org.springframework.boot:spring-boot:1.5.2.RELEASE' }Either explore the dependencies using the Gradle plugin or run gradle on the command-line to view them:
$ gradle dependencies
Notice that this artifact depends on
spring-contextas we have been thus far and adds the minimal set of Spring Boot.Replace instantiating the Application Context directly with the convenience method from Spring Boot:
ConfigurableApplicationContext appCtx = SpringApplication.run(SimpleHelloWorld.class);
and re-run the application and/or debug it.
- what exact concrete type of Application Context does Spring Boot use?
- does
SpringApplication.run()refresh the context?- how does that square with the behaviors described in the
SpringApplicationJavaDoc?
- how does that square with the behaviors described in the
- there are now 18 singletons in the bean factory
- what are the three (3) new ones?
- what does each contribute?
- Spring Boot registers a
ConfigFileApplicationListener.- what does this component (as an
ApplicationListener) do?- how does that square with the behavior described in Spring Boot Reference — 24.3 Application property files?
- it defines an inner class of type
BeanFactoryPostProcessorthat it registers when theApplicationPreparedEventis triggered (see Spring Boot Reference — 23.5 Application events and listeners).- what does that inner class do?
- How does that square with the order of precedence of config sources described in the Spring Boot docs?
- what does this component (as an
- Spring Boot also includes a number of other
ApplicationListeners; what do these do?
-
Fully Activated Spring Boot Application Context.
Replace the direct dependency on
spring-bootwith the Spring Boot Starter.dependencies { compile 'org.springframework.boot:spring-boot-starter:1.5.2.RELEASE' }Either explore the dependencies using the Gradle plugin or run gradle on the command-line to view them:
$ gradle dependencies
- What new dependencies are included? Here is some helpful background to determine what these do:
Add a couple of arguments to the initialization:
ConfigurableApplicationContext appCtx = SpringApplication.run(SimpleHelloWorld.class, "--some-opt-arg=happy", "some-non-opt-arg");
re-run the application and/or debug it:
- did you notice the logging output change?
- What mechanism in Spring Boot was turned on by merely including the starter?
- Which logging system is active now?
- there are now 20 singletons in the bean factory
- what are the two (2) new ones?
- what does each contribute? Here are some references for background:
- Draw a diagram (boxes with names) of the Application Context.
- Include all the things!
- can you come up with a 30 second "Elevator Pitch" for each?
- Color code where all the parts come from:
- Spring Framework
- Spring Boot
- Include all the things!
- What's the difference between a "Spring Bean" and a "Component"?
- What are the sources of properties within the Environment?