Skip to content

spring-roots/application-context-deep-dive

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 

Repository files navigation

Application Context Deep Dive

Audience

You've worked on at least a couple of Spring applications.

Objectives

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).

How to use this guide

  1. 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.
  2. 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.
  3. 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).

Part 1: Taking Stock

Objective: explore the innards of the simplest Application Context, breaking it down into its constituent parts and looking for how they work together.

  1. 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'
    }
  2. 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?
  3. Turn up JDK logging to FINEST for org.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.
  4. 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".
  5. 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 AnnotationConfigApplicationContext brings 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?

Part 2: Registering Beans

  1. 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();
  2. Explicit special bean.

    Define a class that implements ApplicationListener and 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 Peeker as an application listener using ApplicationContext#addApplicationListener().

     appCtx.addApplicationListener(new Peeker());

    Re-run the app and see the listener's output.

  3. Implicit special bean.

    Remove the explicit add from the previous step. Instead, add Peeker as 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()).

  4. By default beans are singleton.

    Replace the StaticApplicationContext with AnnotationConfigApplicationContext and register Peeker with register() instead of registerSingleton().

     AnnotationConfigApplicationContext appCtx = new AnnotationConfigApplicationContext();
     appCtx.register(Peeker.class);

    Re-run the application and note what changes (and what doesn't).

  5. 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 Peeker explicitly, 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 @Configuration a Spring bean?
    • looking at the logging output, trace through how the Peeker bean was added the Application Context:
      • which component contributes the BeanDefinition for Peeker?
      • which component actually creates an instance of Peeker?
  6. Scanned configuration.

    Remove the explicit registration of the configuration class, HelloWorldConfig. Instead, use the scanning feature of the AnnotationConfigApplicationContext:

     appCtx.scan("hello.simple");

    ... re-run the application and examine the logging output:

    • was Peeker included or ignored up by ClassPathBeanDefinitionScanner?
    • which bean was included by ClassPathBeanDefinitionScanner?
  7. Component scan.

    1. move the @Bean method from HelloWorldConfig into SimpleHelloWorld and delete the HelloWorldConfig class.
    2. specify SimpleHelloWorld as the root configuration class by specifying it as the parameter the constructor for AnnotationConfigApplicationContext.
    3. annotate SimpleHelloWorld as a @Configuration class.
    4. annotate Peeker as a @Component class.

    SimpleHelloWorld should 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();
       }
     }

    Peeker should 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 @Configuration on SimpleHelloWorld strictly required?
    • is the "hello.simple" value of @ComponentScan strictly required?

Part 3. Spring Boot-enhanced

  1. Spring Boot-supplied Application Context (minimally-activated).

    Replace the direct dependency on spring-context with 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-context as 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.

  2. Fully Activated Spring Boot Application Context.

    Replace the direct dependency on spring-boot with 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

    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:

Quiz

  1. Draw a diagram (boxes with names) of the Application Context.
    1. Include all the things!
      • can you come up with a 30 second "Elevator Pitch" for each?
    2. Color code where all the parts come from:
      • Spring Framework
      • Spring Boot
  2. What's the difference between a "Spring Bean" and a "Component"?
  3. What are the sources of properties within the Environment?

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published