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:
- Import a bean directly:
@Import(User.class) - Import a configuration class:
@Import(UserConfig.class) - Import an
ImportSelectorimplementation: This is used to load classes from configuration files. - Import an
ImportBeanDefinitionRegistrarimplementation: 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
- Create a module
redis-spring-boot-autoconfigure. - Create a module
redis-spring-boot-starterthat depends onredis-spring-boot-autoconfigure. - In
redis-spring-boot-autoconfigure, define the beans and theMETA-INF/spring.factoriesfile. - 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:
ApplicationContextInitializerSpringApplicationRunListenerCommandLineRunnerApplicationRunner
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
- Configure the bootstrap class (determine if there is a main class).
- Determine if it is a web environment.
- Retrieve initializer and listener classes.
Run Phase
- Start a timer.
- Execute listeners.
- Prepare the environment.
- Print the banner (you can customize it by placing a
banner.txtin theresourcesfolder). - Create the application context.
- Call
refreshContext(context)to actually create the beans.
Monitoring with Actuator
Basic Setup
- Add the actuator dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- Access
http://localhost:8080/actuatorto see available endpoints.
Common Endpoints
/actuator/info: Display application info (configured inapplication.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
- Create an
admin-servermodule. - Add the dependency
spring-boot-admin-starter-server:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
- Enable the admin server with
@EnableAdminServer:
@EnableAdminServer
@SpringBootApplication
public class SpringbootAdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAdminServerApplication.class, args);
}
}
Client Setup
- Create an
admin-clientmodule. - Add the dependency
spring-boot-admin-starter-client:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
- Configure the server address:
spring.boot.admin.client.url=http://localhost:9000
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=*
- Start both the server and client services, then access the server's web interface.
Deploying Spring Boot Applications
Spring Boot supports two deployment methods:
- JAR package (recommended)
- WAR package
WAR Deployment
- Change the packaging type in
pom.xmltowar. - 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);
}
}
- 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
-
Enable automatic compilation in IntelliJ IDEA:
- Go to
Settings>Build, Execution, Deployment>Compilerand checkBuild project automatically.
- Go to
-
Enable automatic restart while running:
- Press
Ctrl+Shift+Alt+/, selectRegistry, and checkcompiler.automake.allow.when.app.running.
- Press
-
Add the dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
- Trigger hot reload: After modifying code or static resources, switch to another window or use
Ctrl+F9to trigger a recompilation.
Static Resource Serving
In Spring Boot, static resources can be placed in the following directories (under resources/):
staticpublicresourcesMETA-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/