Auto Configuration

jim stafford

Introduction

Configuration focus thus far has been

  • under fairly static conditions

  • applied directly to a single application

Realistic Configuration Requirement

  • dynamically determined

  • modularized and not repeated

Dynamically Determined

  • dynamic based on runtime environment at startup

    • libraries present

    • properties defined

    • resources found

    • etc.

  • Examples:

    • what database will be used when in development, integration, or production?

    • what security should be enabled in development versus production areas?

Modularized and not Repeated

  • applications will ideally be broken into separate components

  • making components reusable for multiple applications is good practice

    • requires physically breaking them into separate modules

  • leaves us with repeated responsibility to configure reused components

  • there could be dozens of choices to make within a component configuration

    • application can be significantly simplified by an opinionated configuration supplied based on runtime environment

Forces At Work

If you find yourself

  • needing configurations determined dynamically at runtime

  • solving a repeated problem and bundling that into a library shared by multiple applications

Master the concepts behind Spring Boot’s Auto-configuration capability

Goals

The student will learn to:

  • Enable/disable @Configuration classes and @Bean factories based on condition(s) at startup

  • Create Auto-configuration/Starter module(s) that establish necessary dependencies and conditionally supplies beans

  • Resolve conflicts between alternate configurations

  • Locate environment and condition details to debug Auto-configuration issues

Objectives

At the conclusion of this lecture and related exercises, the student will be able to:

  1. Enable a @Component, @Configuration class, or @Bean factory method based on the result of a condition at startup

  2. Create Spring Boot Auto-configuration/Starter module(s)

  3. Bootstrap Auto-configuration classes into applications using a spring.factories metadata file

  4. Create a conditional component based on the presence of a property value

  5. Create a conditional component based on a missing component

  6. Create a conditional component based on the presence of a class

  7. Define a processing dependency order for Auto-configuration classes

  8. Access textual debug information relative to conditions using the debug property

  9. Access web-based debug information relative to conditionals and properties using the Spring Boot Actuator

Review: Configuration Class

  • @Configuration classes used to bootstrap application using Java classes

    • modern alternative to legacy XML definitions that basically do same thing — define and configure beans

  • can be the @SpringBootApplication class itself

    • appropriate for a small application

Configuration supplied within @SpringBootApplication Class
@SpringBootApplication
//==> wraps @EnableAutoConfiguration
//==> wraps @SpringBootConfiguration
//            ==> wraps @Configuration
public class SelfConfiguredApp {
    public static final void main(String...args) {
        SpringApplication.run(SelfConfiguredApp.class, args);
    }

    @Bean
    public Hello hello() {
        return new StdOutHello("Application @Bean says Hey");
    }
}

Separate @Configuration Class

  • can be broken out into separate classes

    • appropriate for larger applications with distinct areas to be configured

@Configuration(proxyBeanMethods = false)
public class AConfigurationClass {
    @Bean
    public Hello hello() {
        return new StdOutHello("...");
    }
}
@Configuration classes are commonly annotated with the proxyMethods=false attribute that tells Spring it need not create extra proxy code to enforce normal, singleton return of the created instance to be shared by all callers since @Configuration class instances are only called by Spring. The javadoc for the annotation attribute describes the extra and unnecessary work saved.

Conditional Configuration

  • can make configurations dependent on conditions found at startup

    • individual @Bean factory methods (or the @Component annotated class)

    • entire @Configuration classes

Property Condition Example
...
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class StarterConfiguredApp {
    public static final void main(String...args) {
        SpringApplication.run(StarterConfiguredApp.class, args);
    }

    @Bean
    @ConditionalOnProperty(prefix="hello", name="quiet", havingValue="true") (1)
    public Hello quietHello() {
        return new StdOutHello("(hello.quiet property condition set, Application @Bean says hi)");
    }
}
1 @ConditionalOnProperty annotation used to define a Hello bean based on the presence of the hello.quiet property equaling the value true

Property Value Condition Satisfied

Property Value Condition Satisfied Result
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar --hello.quiet=true (1)
...
(hello.quiet property condition set, Application @Bean says hi) World (2)
1matching property supplied using command line
2satisfies property condition in @SpringBootApplication
The (parentheses) is trying to indicate a whisper. hello.quiet=true property turns on this behavior.

Property Value Condition Not Satisfied

Property Value Condition Not Satisfied
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar (1)
...
***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in info.ejava.springboot.examples.app.AppCommand required a bean of type
  'info.ejava.examples.app.hello.Hello' that could not be found.

The following candidates were found but could not be injected: (2)
        - Bean method 'quietHello' in 'StarterConfiguredApp' not loaded because
    @ConditionalOnProperty (hello.quiet=true) did not find property 'quiet'

Action:
Consider revisiting the entries above or defining a bean of type
  'info.ejava.examples.app.hello.Hello' in your configuration.
1property either not specified or not specified with targeted value
2property condition within @SpringBootApplication not satisfied

Two Primary Configuration Phases

Configuration processing within Spring Boot is broken into two primary phases:

  1. User-defined configuration classes

    • processed first

    • part of the application module

    • located through the use of a @ComponentScan (wrapped by @SpringBootApplication)

    • establish the base configuration for the application

    • fill in any fine-tuning details.

  2. Auto-configuration classes

    • parsed second

    • outside the scope of the @ComponentScan

    • placed in separate modules, identified by metadata within those modules

    • enabled by application using @EnableAutoConfiguration (also wrapped by @SpringBootApplication)

    • provide defaults to fill in the reusable parts of the application

    • use User-defined configuration for details

Auto-Configuration

  • technically no different than any other @Configuration class except

    • inspected after User-defined @Configuration class(es) processing complete

    • based on being named in a META-INF/spring.factories descriptor

  • alternate identification and second pass processing allows the core application to

    • make key directional and detailed decisions

    • control conditions for the Auto-configuration class(es)

Example Auto-Configuration Class
package info.ejava.examples.app.hello; (2)
...

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
    @Bean (1)
    public Hello hello(HelloProperties helloProperties) {
        return new StdOutHello(helloProperties.getGreeting());
    }
}
1Example Auto-configuration class provides unconditional @Bean factory for Hello
2this @Configuration package is outside the default scanning scope of @SpringBootApplication

Auto-Configuration Java Packages

Auto-Configuration Packages are Separate from Application

Auto-Configuration classes are designed to be outside the scope of the @SpringBootApplication package scanning. Otherwise it would end up being a normal @Configuration class and processed within the main application JAR pre-processing.

package info.ejava.examples.app.config.auto;
@SpringBootApplication
package info.ejava.examples.app.hello; (1)

@Configuration(proxyBeanMethods = false)
public class HelloAutoConfiguration {
1app.hello is not under app.config.auto

Supporting @ConfigurationProperties

  • this particular @Bean factory defines the @ConfigurationProperties class to encapsulate the details of configuring Hello

  • supplies a default greeting

    • making it optional for the User-defined configuration to do anything

Example Auto-Configuration Properties Class
@ConfigurationProperties("hello")
@Data
@Validated
public class HelloProperties {
    @NotNull
    private String greeting = "HelloProperties default greeting says Hola!"; (1)
}
1Value used if user-configuration does not specify a property value

Locating Auto Configuration Classes

  • registered within META-INF/spring.factories file of Auto-configuration class’s JAR

  • module typically called an "auto-configuration"

Auto-configuration Module JAR
$ jar tf target/hello-starter-*-SNAPSHOT-bootexec.jar | egrep -v '/$|maven|MANIFEST.MF'
META-INF/spring.factories (1)
META-INF/spring-configuration-metadata.json (2)
info/ejava/examples/app/hello/HelloAutoConfiguration.class
info/ejava/examples/app/hello/HelloProperties.class
1"auto-configuration" dependency JAR supplies META-INF/spring.factories
2@ConfigurationProperties class metadata generated by maven plugin for use by IDEs


It is common best-practice to host Auto-configuration classes in a separate module than the beans it configures. The Hello interface and Hello implementation(s) comply with this convention and are housed in separate modules.

META-INF/spring.factories Metadata File

  • Auto-configuraton classes registered using

    • property name equaling fully qualified classname of @EnableAutoConfiguration annotation

    • value equaling fully qualified classname of Auto-configuration class(es)

  • Multiple classes can be specified separated by commas — shown later


# src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  info.ejava.examples.app.hello.HelloAutoConfiguration (1)
1Auto-configuration class metadata registration

Spring Boot 2.7 AutoConfiguration Changes

Spring Boot 2.7 has announced:

  • a new @AutoConfiguration annotation that is meant to take the place of using @Configuration on top-level classes

  • the deprecation of META-INF/spring.factories in favor of META-INF/spring/ org.springframework.boot. autoconfigure.AutoConfiguration.imports

For backwards compatibility, entries in spring.factories will still be honored.
Spring Boot 2.7.0 M2 Release Notes -- Changes to Auto-configuration
— Spring.io

Example Auto-Configuration Module Source Tree

Example Auto-Configuration Module Structure
pom.xml
src
`-- main
    |-- java
    |   `-- info
    |       `-- ejava
    |           `-- examples
    |               `-- app
    |                   `-- hello
    |                       |-- HelloAutoConfiguration.java
    |                       `-- HelloProperties.java
    `-- resources
        `-- META-INF
            `-- spring.factories

Auto-Configuration / Starter Roles/Relationships

Modules designed as starters can have varying designs with the following roles carried out:

  • Auto-configuration classes that conditionally wire the application

  • An opinionated starter with dependencies that trigger the Auto-configuration rules

autoconfig modules

Example Starter Module pom.xml

The module is commonly termed a starter and will have dependencies on

  • spring-boot-starter

  • the service interface

  • one or more service implementation(s) and their implementation dependencies

    <groupId>info.ejava.examples.app</groupId>
    <artifactId>hello-starter</artifactId>

    <dependencies>
        <dependency> (1)
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- commonly declares dependency on interface module -->
        <dependency> (2)
            <groupId>${project.groupId}</groupId>
            <artifactId>hello-service-api</artifactId>
            <version>${project.version}</version>
        </dependency> (2)
        <!-- hello implementation dependency -->
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>hello-service-stdout</artifactId>
            <version>${project.version}</version>
        </dependency>
1dependency on spring-boot-starter define classes pertinent to Auto-configuration
2starter modules commonly define dependencies on interface and implementation modules

Example Starter Implementation Dependencies

  • remaining dependencies support the specific example module implementation

Example Starter pom.xml Implementation Dependencies
        <dependency> (1)
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency> (1)
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>

        <!-- creates a JSON metadata file describing @ConfigurationProperties -->
        <dependency> (1)
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
1these dependencies are part of optional implementation detail having nothing to do with Auto-configuration topic

Application Starter Dependency

The application module declares dependency on the starter module containing or having a dependency on the Auto-configuration artifacts.

Application Module Dependency on Starter Module
<!-- takes care of initializing Hello Service for us to inject -->
<dependency>
    <groupId>${project.groupId}</groupId> (1)
    <artifactId>hello-starter</artifactId>
    <version>${project.version}</version> (1)
</dependency>
1For this example, the application and starter modules share the same groupId and version and leverage a ${project} variable to simplify the expression. That will likely not be the case with most starter module dependencies and will need to be spelled out.

Starter Brings in Pertinent Dependencies

  • starter dependency brings in

    • Hello Service interface

    • targeted implementation(s)

    • some implementation dependencies

Application Module Transitive Dependencies from Starter
$ mvn dependency:tree
...
[INFO] +- info.ejava.examples.app:hello-starter:jar:6.0.1-SNAPSHOT:compile
[INFO] |  +- info.ejava.examples.app:hello-service-api:jar:6.0.1-SNAPSHOT:compile
[INFO] |  +- info.ejava.examples.app:hello-service-stdout:jar:6.0.1-SNAPSHOT:compile
[INFO] |  +- org.projectlombok:lombok:jar:1.18.10:provided
[INFO] |  \- org.springframework.boot:spring-boot-starter-validation:jar:2.7.0:compile
...

Configured Application

  • example application contains component that requests the greeter implementation of type Hello to say hello to "World"

Injection Point for Auto-configuration Bean
import lombok.RequiredArgsConstructor;
...
@Component
@RequiredArgsConstructor (1)
public class AppCommand implements CommandLineRunner {
    private final Hello greeter;

    public void run(String... args) throws Exception {
        greeter.sayHello("World");
    }
}
1lombok is being used to provide the constructor injection

Review: Unconditional Auto-Configuration Class

This starter dependency is bringing in a @Bean factory to construct an implementation of Hello.

Example Auto-Configuration Class
package info.ejava.examples.app.hello;
...

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
    @Bean
    public Hello hello(HelloProperties helloProperties) { (1)
        return new StdOutHello(helloProperties.getGreeting());
    }
}
1Example Auto-configuration configured by HelloProperties

Review: Starter Module Default

  • starter dependency brings in an Auto-configuration class that instantiates a StdOutHello implementation configured by a HelloProperties class


Review: Auto-configuration class` Configuration Properties
@ConfigurationProperties("hello")
@Data
@Validated
public class HelloProperties {
    @NotNull
    private String greeting = "HelloProperties default greeting says Hola!"; (1)
}
1hello.greeting default defined in @ConfigurationProperties class of starter/autoconfigure module

Produced Default Starter Greeting

Example Application Execution without Satisfying Property Condition
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar
...
HelloProperties default greeting says Hola! World

User-Application Supplies Property Details

  • application can supply properties to express details of greeting

application.properties
#appconfig-autoconfig-example application.properties
#uncomment to use this greeting
hello.greeting: application.properties Says - Hey


Runtime Output with hello.greeting Property Defined
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar
...
application.properties Says - Hey World (1)
1auto-configured implementation using user-defined property

Auto-Configuration Conflict

Review: Conditional @Bean Factory

We saw how we could make a @Bean factory in the User-defined application module conditional (on the value of a property).

Conditional @Bean Factory
@SpringBootApplication
public class StarterConfiguredApp {
...
    @Bean
    @ConditionalOnProperty(prefix = "hello", name = "quiet", havingValue = "true")
    public Hello quietHello() {
        return new StdOutHello("(hello.quiet property condition set, Application @Bean says hi)");
    }
}

Potential Conflict

  • have condition where our two @Bean factory methods cause ambiguity error

    • @Bean factory in User-defined application module conditional (on property value)

    • @Bean factory in Auto-configuration class brought in by starter module

Example Output with Bean Factory Ambiguity
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar --hello.quiet=true (1)
...
***************************
APPLICATION FAILED TO START
***************************
Description:

Parameter 0 of constructor in info.ejava.examples.app.config.auto.AppCommand
   required a single bean, but 2 were found:
        - quietHello: defined by method 'quietHello' in
         info.ejava.examples.app.config.auto.StarterConfiguredApp
        - hello: defined by method 'hello' in class path resource
         [info/ejava/examples/app/hello/HelloAutoConfiguration.class]

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans,
or using @Qualifier to identify the bean that should be consumed
1Supplying the hello.quiet=true property value causes two @Bean factories to chose from

How Can Conflict Be Solved?

Conditional

Which Condition?

If User-definition did not supply a bean

@ConditionalOnMissingBean

@ConditionOnMissingBean Auto-Configuration Example
...
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean (1)
    public Hello hello(HelloProperties helloProperties) {
        return new StdOutHello(helloProperties.getGreeting());
    }
}
1@ConditionOnMissingBean causes Auto-configured @Bean method to be inactive when Hello bean already exists


  • @ConditionalOnMissingBean and sibling @ConditionalOnBean are special

    • meant to be used with Auto-configuration classes in the autoconfigure modules

  • Auto-configuration classes are processed after the User-defined classes

    • clear point to determine whether User-defined @Bean factory does or does not exist

  • any other use of these two annotations requires careful ordering and is not recommended

Bean Conditional Example Output

  • with @ConditionalOnMissingBean defined on Auto-configuration class and the property condition satisfied

    • we get bean injected from User-defined @Bean factory

Runtime with Property Condition Satisfied
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar --hello.quiet=true
...
(hello.quiet property condition set, Application @Bean says hi) World
  • with property condition not satisfied, we get bean injected from Auto-configuration @Bean factory

Runtime with Property Condition Not Satisfied
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar
...
application.properties Says - Hey World

Resource Conditional and Ordering

Example Condition on File Present and Evaluation Ordering
...
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnResource;

@ConditionalOnResource(resources = "file:./hello.properties") (1)
@AutoConfigureBefore(HelloAutoConfiguration.class) (2)
public class HelloResourceAutoConfiguration {
    @Bean
    public Hello resourceHello() {
        return new StdOutHello("hello.properties exists says hello");
    }
}
1Auto-configured class satisfied only when file hello.properties present
2This Auto-configuration class is processed prior to HelloAutoConfiguration

Registering Second Auto-Configuration Class

hello-starter spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  info.ejava.examples.app.hello.HelloAutoConfiguration, \ (1)
  info.ejava.examples.app.hello.HelloResourceAutoConfiguration
1comma separated

Resource Conditional Example Output

  • with hello.properties present

    • @Bean factory from HelloAutoConfiguration skipped — bean already exists

    • bean created using HelloResourceAutoConfiguration @Bean factory — evaluated before HelloAutoConfiguration

Resource Condition Satisfied
$ echo hello.greeting: hello.properties exists says hello World > hello.properties
$ cat hello.properties
hello.greeting: hello.properties exists says hello World

$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar
...
hello.properties exists says hello World
  • when property file is not present

    • @Bean factory from HelloAutoConfiguration used since neither property or resource-based conditions satisfied

Resource Condition Not Satisfied
$ rm hello.properties
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar
...
application.properties Says - Hey World

@Primary

  • familiar situation — ambiguous match

    • if we supply a hello.properties file and hello.quiet=true property value

Example Ambiguous Conditional Match
$ touch hello.properties
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar --hello.quiet=true
...
***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in info.ejava.examples.app.config.auto.AppCommand required a single bean,
  but 2 were found:
        - quietHello: defined by method 'quietHello' in info.ejava.examples.app.config.auto.StarterConfiguredApp
        - resourceHello: defined by method 'resourceHello' in class path resource
      [info/ejava/examples/app/hello/HelloResourceAutoConfiguration.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans,
or using @Qualifier to identify the bean that should be consumed
  • this time — to correct — we add the @Primary annotation to our highest priority @Bean factory

    • if there is a conflict — this one will be used

...
import org.springframework.context.annotation.Primary;

@ConditionalOnResource(resources = "file:./hello.properties")
@AutoConfigureBefore(HelloAutoConfiguration.class)
public class HelloResourceAutoConfiguration {
    @Bean
    @Primary //chosen when there is a conflict
    public Hello resourceHello() {
        return new StdOutHello("hello.properties exists says hello");
    }
}

@Primary Example Output

  • avoided conflict error with one of the @Bean factories listed as @Primary

Ambiguous Choice Resolved thru @Primary
$ cat hello.properties
hello.greeting: hello.properties exists says hello World
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar --hello.quiet=true (1)
...
hello.properties exists says hello World
1@Primary condition satisfied overrides application @Bean condition

Class Conditions

  • important difference between conditions applied to @Configuration class or methods

    • class conditional annotations prevent entire class from loading when not satisfied

    • @Bean factory conditional annotations allow class to load but prevent method from being called when not satisfied

  • this works for missing classes too!

    • Spring Boot parses conditional class using ASM to detect and evaluate conditions prior to allowing class to be loaded into JVM

    • otherwise we would get ClassNotFoundException for the import of class we are trying to base condition on

Class Conditional Example

  • adding @ConditionalOnClass annotation to prevent @Configuration class from being loaded if implementation class does not exist on classpath

...
import info.ejava.examples.app.hello.stdout.StdOutHello; (2)
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(StdOutHello.class) (2)
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Hello hello(HelloProperties helloProperties) {
        return new StdOutHello(helloProperties.getGreeting()); (1)
    }
}
1StdOutHello is the implementation instantiated by the @Bean factory method
2HelloAutoConfiguration.class will not get loaded if StdOutHello.class does not exist


  • @ConditionOnClass accepts either class or string expression of fully qualified classname

  • @ConditionalOnMissingClass accepts only string form of classname

Spring Boot Autoconfigure module contains many examples of real Auto-configuration classes

Excluding Auto Configurations

We can turn off certain Auto-configured classes using the

@SpringBootApplication(exclude = {})
// ==> wraps @EnableAutoConfiguration(exclude={})
public class StarterConfiguredApp {
    ...
}

Debugging Auto Configurations

  • many conditional User-defined and Auto-configurations going on

  • easy to get lost or make a mistake

  • two primary tools can expose details of conditional configuration decisions

    • Conditions Evaluation Report

    • Spring Boot Actuator

Conditions Evaluation Report

  • simplistic textual report of positive and negative condition evaluation matches

  • add a debug property to the configuration

    • --debug or -Ddebug to the command line

Conditions Evaluation Report Example

Conditions Evaluation Report Snippet
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar --debug | less
...
============================
CONDITIONS EVALUATION REPORT
============================

Positive matches: (1)
-----------------
   HelloAutoConfiguration matched:
      - @ConditionalOnClass found required class 'info.ejava.examples.app.hello.stdout.StdOutHello' (OnClassCondition)

   HelloAutoConfiguration#hello matched:
      - @ConditionalOnBean (types: info.ejava.examples.app.hello.Hello; SearchStrategy: all) did not find any beans (OnBeanCondition)

Negative matches: (2)
-----------------
   HelloResourceAutoConfiguration:
      Did not match:
         - @ConditionalOnResource did not find resource 'file:./hello.properties' (OnResourceCondition)

   StarterConfiguredApp#quietHello:
      Did not match:
         - @ConditionalOnProperty (hello.quiet=true) did not find property 'quiet' (OnPropertyCondition)
1Positive matches show which conditionals are activated and why
2Negative matches show which conditionals are not activated and why

Condition Evaluation Report Results

The report shows us that

  • HelloAutoConfiguration class was enabled because StdOutHello class was present

  • hello @Bean factory method of HelloAutoConfiguration class was enabled because no other beans were located

  • entire HelloResourceAutoConfiguration class was not loaded because file hello.properties was not present

  • quietHello @Bean factory method of application class was not activated because hello.quiet property was not found

Actuator Conditions

  • can also look at conditionals while Web applications are running using the Spring Boot Actuator

  • requires example transitioned from a command to a Web application

    • can be done technically by simply changing our starter in pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
<!--            <artifactId>spring-boot-starter</artifactId>-->
        </dependency>
  • also need to add a dependency on the spring-boot-starter-actuator module

        <!-- added to inspect env -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

Activating Actuator Conditions

  • Actuator, by default, will not expose any information without being configured to do so

  • can show a JSON version of the Conditions Evaluation Report by adding management.endpoints.web.exposure.include equal to value “conditions”

    • performed here using command line

    • could be in a profile-specific properties file appropriate for exposing this information

Enable Actuator Conditions Report to be Exposed
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar \
  --management.endpoints.web.exposure.include=conditions


Example Actuator Conditions Report
{
"contexts": {
  "application": {
    "positiveMatches": {
        "HelloAutoConfiguration": [{
            "condition": "OnClassCondition",
            "message": "@ConditionalOnClass found required class 'info.ejava.examples.app.hello.stdout.StdOutHello'"
            }],
        "HelloAutoConfiguration#hello": [{
            "condition": "OnBeanCondition",
            "message": "@ConditionalOnBean (types: info.ejava.examples.app.hello.Hello; SearchStrategy: all) did not find any beans"
            }],
...
,
    "negativeMatches": {
        "StarterConfiguredApp#quietHello": {
            "notMatched": [{
            "condition": "OnPropertyCondition",
            "message": "@ConditionalOnProperty (hello.quiet=true) did not find property 'quiet'"
            }],
            "matched": []
            },
        "HelloResourceAutoConfiguration": {
            "notMatched": [{
            "condition": "OnResourceCondition",
            "message": "@ConditionalOnResource did not find resource 'file:./hello.properties'"
            }],
            "matched": []
            },
...

Actuator Environment

  • also helpful to inspect the environment

    • determine value of properties

    • determine which source of properties is being used

  • add env to the exposure.include property to see information

Enable Actuator Conditions Report and Environment to be Exposed
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar \
   --management.endpoints.web.exposure.include=conditions,env

Actuator Environment Report

{
activeProfiles: [ ],
propertySources: [{
        name: "server.ports",
        properties: {
            local.server.port: {
                value: 8080
                }
            }
    },
    {
        name: "commandLineArgs",
        properties: {
            management.endpoints.web.exposure.include: {
                value: "conditions,env"
                }
            }
    },
...

Actuator Specific Property Source

Example Actuator Environment Report for Specific Property
{
  property: {
  source: "applicationConfig: [classpath:/application.properties]",
  value: "application.properties Says - Hey"
},
...

More Actuator

  • can explore some of the other Actuator endpoints by

    • changing the include property to *

    • revisiting the main actuator endpoint

  • Actuator Documentation is available on the web

Expose All Actuator Endpoints
$ java -jar target/appconfig-autoconfig-*-SNAPSHOT-bootexec.jar \
   --management.endpoints.web.exposure.include="*" (1)
1double quotes ("") being used to escape * special character on command line

Summary

In this module we:

  • Defined conditions for @Configuration classes and @Bean factory methods that are evaluated at runtime startup

  • Placed User-defined conditions, which are evaluated first, in with with application module

  • Placed Auto-configuration classes in separate starter module to automatically bootstrap applications with specific capabilities

  • Added conflict resolution and ordering to conditions to avoid ambiguous matches

  • Discovered how class conditions can help prevent entire @Configuration classes from being loaded and disrupt the application because an optional class is missing

  • Learned how to debug conditions and visualize the runtime environment through use of the debug property or by using the Actuator for web applications