Spring Boot Auto-Configuration, Event Listeners, Monitoring, and Deployment

Spring Boot Auto-Configuration: Condition Interface

Condition is a conditional configuration interface introduced in Spring 4.0. By implementing the Condition interface, you can conditionally load beans.

The @Conditional annotation is used in conjunction with a Condition implementation class.

ClassCondition Example

public class ClassCondition implements Condition {
    /**
     * @param context  The context object. Used to access the environment, IoC container, and ClassLoader.
     * @param metadata The annotation metadata object. Used to retrieve attribute values from the annotation.
     * @return true if the condition matches, false otherwise.
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // Requirement: Create a bean only if the Jedis library is on the classpath.
        // Approach: Check if the Jedis class file exists.
        boolean flag = true;
        try {
            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;
    }
}

UserConfig Using @Conditional

@Configuration
public class UserConfig {

    @Bean
    @Conditional(ClassCondition.class)
    public User user(){
        return new User();
    }
}

Test Class

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        // Start the Spring Boot application and get the Spring IoC container.
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);

        Object user = context.getBean("user");
        System.out.println(user);
    }
}

Dynamic Condition with Custom Annnotation

To make the class check dynamic, we can define a custom conditional annotation.

Custom Annotation Class

import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
    String[] value();
}

Note: @ConditionOnClass is a custom annotation.

Usage in Configuration

@Configuration
public class UserConfig {

    @Bean
    @ConditionOnClass("com.alibaba.fastjson.JSON")
    public User user(){
        return new User();
    }

    @Bean
    @ConditionalOnProperty(name = "itcast", havingValue = "itheima")
    public User user2(){
        return new User();
    }
}

Common Conditional Annotations in Spring Boot

  • @ConditionalOnProperty: Initializes a bean only if a specific property exists in the configuration file.
  • @ConditionalOnClass: Initializes a bean only if a specific class file is present in the classpath.
  • @ConditionalOnMissingBean: Initializes a bean only if no other bean of the same type exists.

Switching the Embedded Web Server

To switch from Tomcat to Jetty, you need to exclude Tomcat and include Jetty in your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <artifactId>spring-boot-starter-jetty</artifactId>
    <groupId>org.springframework.boot</groupId>
</dependency>

Understanding @Enable* Annotations

Spring Boot cannot directly access beans defined in other projects. The @Enable* annotations work by using @Import to load configuration classes dynamically.

Example: Using @EnableUser

UserConfig in springboot-enable-other project:

@Configuration
public class UserConfig {

    @Bean
    public User user() {
        return new User();
    }
}

EnableUser Annotation:

import org.springframework.context.annotation.Import;
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}

Main Application:

@EnableUser
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

Note: Without @EnableUser, @ComponentScan only scans the package of the main class and its sub-packages.

Detailed Explanation of @Import

The @Import annotation can be used in four ways:

  1. Import a bean directly: @Import(User.class)
  2. Import a configuration class: @Import(UserConfig.class)
  3. Import an ImportSelector implementation: This is used to load classes from configuration files.
  4. Import an ImportBeanDefinitionRegistrar implementation: This alows custom registration of bean definitions.

Examples

ImportSelector implementation:

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.itheima.domain.User", "com.itheima.domain.Role"};
    }
}

ImportBeanDefinitionRegistrar implementation:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        registry.registerBeanDefinition("user", beanDefinition);
    }
}

Usage in main application:

// @Import(User.class)
// @Import(UserConfig.class)
// @Import(MyImportSelector.class)
// @Import({MyImportBeanDefinitionRegistrar.class})
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
        // Retrieve beans...
    }
}

@EnableAutoConfiguration

@EnableAutoConfiguration internally uses @Import(AutoConfigurationImportSelector.class) to load configuration classes. These classes are defined in the META-INF/spring.factories file. During startup, Spring Boot automatically loads these configuration classes and initializes beans, but only those beans that match the conditions defined in the configuraton classes.

Creating a Custom Starter

Step-by-Step Guide

  1. Create a module redis-spring-boot-autoconfigure.
  2. Create a module redis-spring-boot-starter that depends on redis-spring-boot-autoconfigure.
  3. In redis-spring-boot-autoconfigure, define the beans and the META-INF/spring.factories file.
  4. In a test module, add the custom starter dependency and test it.

Implementation

1. redis-spring-boot-starter pom.xml:

<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>redis-spring-boot-autoconfigure</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

2. redis-spring-boot-autoconfigure Configuration:

RedisProperties.java:

@ConfigurationProperties(prefix = "redis")
public class RedisProperties {

    private String host = "localhost";
    private int port = 6379;

    // getters and setters
}

RedisAutoConfiguration.java:

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {

    @Bean
    public Jedis jedis(RedisProperties redisProperties) {
        return new Jedis(redisProperties.getHost(), redisProperties.getPort());
    }
}

META-INF/spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.itheima.redis.config.RedisAutoConfiguration

3. Test Module Dependency:

<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>redis-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

Test Code:

Jedis jedis = context.getBean(Jedis.class);
System.out.println(jedis);

4. Add conditional loading:

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnClass(Jedis.class)
public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "jedis")
    public Jedis jedis(RedisProperties redisProperties) {
        System.out.println("RedisAutoConfiguration....");
        return new Jedis(redisProperties.getHost(), redisProperties.getPort());
    }
}

Spring Boot Event Listeners

Spring Boot provides several listener interfaces that can be implemented to perform actions at different stages of application startup:

  • ApplicationContextInitializer
  • SpringApplicationRunListener
  • CommandLineRunner
  • ApplicationRunner

Example Implementations

MyApplicationRunner:

@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run");
        System.out.println(Arrays.asList(args.getSourceArgs()));
    }
}

MyCommandLineRunner:

@Component
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run");
        System.out.println(Arrays.asList(args));
    }
}

MyApplicationContextInitializer: To use this, add the following to META-INF/spring.factories:

org.springframework.context.ApplicationContextInitializer=com.itheima.springbootlistener.listener.MyApplicationContextInitializer
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer....initialize");
    }
}

MySpringApplicationRunListener: This requires a constructor with SpringApplication and String[] parameters.

public class MySpringApplicationRunListener implements SpringApplicationRunListener {

    public MySpringApplicationRunListener(SpringApplication application, String[] args) {
    }

    @Override
    public void starting() {
        System.out.println("starting...");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("environmentPrepared...");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("contextPrepared...");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("contextLoaded...");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("started...");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("running...");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("failed...");
    }
}

Spring Boot Startup Process Analysis

Initialization Phase

  1. Configure the bootstrap class (determine if there is a main class).
  2. Determine if it is a web environment.
  3. Retrieve initializer and listener classes.

Run Phase

  1. Start a timer.
  2. Execute listeners.
  3. Prepare the environment.
  4. Print the banner (you can customize it by placing a banner.txt in the resources folder).
  5. Create the application context.
  6. Call refreshContext(context) to actually create the beans.

Monitoring with Actuator

Basic Setup

  1. Add the actuator dependency:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. Access http://localhost:8080/actuator to see available endpoints.

Common Endpoints

  • /actuator/info: Display application info (configured in application.properties).
  • /actuator/health: Show health status. To enable detailed health info:
management.endpoint.health.show-details=always

Enable All Endpoints

management.endpoints.web.exposure.include=*

Spring Boot Admin (Graphical Monitoring)

Server Setup

  1. Create an admin-server module.
  2. Add the dependency spring-boot-admin-starter-server:
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
  1. Enable the admin server with @EnableAdminServer:
@EnableAdminServer
@SpringBootApplication
public class SpringbootAdminServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootAdminServerApplication.class, args);
    }
}

Client Setup

  1. Create an admin-client module.
  2. Add the dependency spring-boot-admin-starter-client:
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
  1. Configure the server address:
spring.boot.admin.client.url=http://localhost:9000
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=*
  1. Start both the server and client services, then access the server's web interface.

Deploying Spring Boot Applications

Spring Boot supports two deployment methods:

  1. JAR package (recommended)
  2. WAR package

WAR Deployment

  1. Change the packaging type in pom.xml to war.
  2. Modify the main class to extend SpringBootServletInitializer:
@SpringBootApplication
public class SpringbootDeployApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDeployApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(SpringbootDeployApplication.class);
    }
}
  1. Specify the final build name:
<build>
    <finalName>springboot</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Practical Use Cases

Hot Reload with DevTools

Spring Boot provides devtools for hot reloading. It uses two class loaders: one for unchanged classes (third-party jars) and another for classes that might change (Restart ClassLoader). When code changes, the old Restart ClassLoader is discarded and a new one is created, resulting in faster restart times.

Setup

  1. Enable automatic compilation in IntelliJ IDEA:

    • Go to Settings > Build, Execution, Deployment > Compiler and check Build project automatically.
  2. Enable automatic restart while running:

    • Press Ctrl+Shift+Alt+/, select Registry, and check compiler.automake.allow.when.app.running.
  3. Add the dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>
  1. Trigger hot reload: After modifying code or static resources, switch to another window or use Ctrl+F9 to trigger a recompilation.

Static Resource Serving

In Spring Boot, static resources can be placed in the following directories (under resources/):

  • static
  • public
  • resources
  • META-INF/resources

To access a file like index.html placed in static, you can use /index.html.

Modify Static Resource Path

To require a prefix like /res for static resources, add the following to application.yml:

spring:
  mvc:
    static-path-pattern: /res/**
  web:
    resources:
      static-locations:
        - classpath:/sgstatic/
        - classpath:/static/

Tags: Spring Boot auto-configuration Condition Event Listener Actuator

Posted on Fri, 04 Sep 2026 16:33:28 +0000 by poleposters