📢 This article was translated by gemini-2.5-flash
Configuration
You can configure things using YAML, YML, or properties files. Java system properties and command-line arguments also work.
Priority: Command Line Args > Java System Properties > Properties Files > YML > YAML
For command line usage, first run the Maven package command, then execute from the command line:
1
2
3
4
5
| java -jar path_to_jar.jar
# Java System Property, e.g., for port
java -Dserver.port=9000 -jar path_to_jar.jar
# Command Line Argument, e.g., for port
java -jar path_to_jar.jar --server.port=9000
|
When packaging a SpringBoot project, you need the spring-boot-maven-plugin. (If you create a project from the official skeleton, this plugin is automatically included).
Bean Management
Getting Beans
For default singleton, non-lazy-loaded beans, Spring creates them all during startup and puts them in the IOC container. (If you use @Lazy, they’ll be instantiated on first use).
If you want to explicitly get these beans, you can do it like this:
- Get by name:
1
| Object getBean(String name)
|
- Get by type:
1
| <T> T getBean(Class<T> requiredType)
|
- Get by name and type (type conversion):
1
| <T> T getBean(String name, Class<T> requiredType)
|
To use this method, you first need to get the IOC container object:
1
2
3
4
5
6
7
8
9
10
11
12
13
| @Autowired
private ApplicationContext applicationContext; //IOC container object
public void testGetBean(){
// Get by bean name
DeptController beanl = (DeptController) applicationContext.getBean("deptController");
// Get by bean type
DeptController bean2 = applicationContext.getBean(DeptController.class);
// Get by bean name and type
DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
}
|
Bean Scopes
Spring supports five scopes. The last three are only active in a web environment.
| Scope | Description |
|---|
| singleton | Only one instance of the bean exists per container (singleton) |
| prototype | A new instance is created each time the bean is used (non-singleton) |
| request | A new instance is created for each request |
| session | A new instance is created for each session |
| application | A new instance is created for each application |
Use the @Scope annotation to set the scope:
1
2
3
4
5
6
| // Set to non-singleton
@Scope("prototype")
@RestController
public class xxxController{
}
|
In real-world development, most beans are singletons, meaning most beans don’t need the scope attribute configured.
Third-Party Beans
If the bean you want to manage comes from a third party (not custom-defined), you can’t use @Component or its derivatives. Instead, you’ll need the @Bean annotation. A good example is dom4j for parsing XML files.
1
2
3
4
5
| <dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
|
Dependency as above.
1
2
3
4
5
6
7
| @SpringBootApplication
public class xxxApplication{
@Bean // Hand the method's return value to the IOC container to become an IOC bean
public SAXReader saxReader(){
return new SAXReader();
}
}
|
However, for third-party beans, it’s a good practice to centralize their configuration. You can declare a configuration class using the @Configuration annotation.
1
2
3
4
5
6
7
| @Configuration
public class CommonConfig {
@Bean
public SAXReader saxReader(){
return new SAXReader();
}
}
|
You can specify a bean’s name using the name or value attribute of the @Bean annotation. If not specified, the method name becomes the bean name by default. If a third-party bean depends on other beans, just declare them as method parameters in the bean definition method, and the container will autowire them by type.
1
2
3
4
5
6
7
| @Configuration
public class CommonConfig {
@Bean
public SAXReader saxReader(XxService xxService){
return new SAXReader();
}
}
|
Starter Dependencies
When developing directly with Spring, you need to pull in a bunch of dependencies and ensure version compatibility. With SpringBoot, you just need to include a ‘starter’ dependency. The magic behind it is Maven’s transitive dependencies; all other required dependencies are automatically pulled in.
Auto-Configuration
SpringBoot’s auto-configuration means that when the Spring container starts, various configuration classes and bean objects are automatically added to the IOC container. This eliminates manual declarations, simplifying development and cutting down on tedious configuration.
At its core, a configuration class (@Configuration) is a @Component and thus also a bean in the container.
So, after pulling in a dependency, how do the configuration classes and beans defined within that JAR get loaded into the Spring IOC container?
@ComponentScan
You can use @ComponentScan to specify which packages to scan. For example, if you imported com.example.
1
2
| @SpringBootApplication
@ComponentScan({"net.yexca","com.example"})
|
However, if you’re pulling in many third-party dependencies, you’d have to list a lot of packages above, and large-scale scanning can impact performance.
@Import
You can import regular classes, configuration classes, and ImportSelector interface implementations.
Regular Classes
1
2
| @Import(TokenParser.class) // Import a regular class
@SpringBootApplication
|
Configuration Classes
Configuration class content:
1
2
3
4
5
6
7
8
9
10
11
12
| @Configuration
public class HeaderConfig {
@Bean
public HeaderParser headerParser(){
return new HeaderParser();
}
@Bean
public HeaderGenerator headerGenerator(){
return new HeaderGenerator();
}
}
|
Startup class:
1
2
| @Import(HeaderConfig.class) // Import a configuration class
@SpringBootApplication
|
ImportSelector Implementations
ImportSelector interface implementation content:
1
2
3
4
5
6
| public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// Returns a string array (array containing fully qualified class names)
return new String[]{"com.example.HeaderConfig"};
}
}
|
Startup class:
1
2
| @Import(MyImportSelector.class) // Import an ImportSelector implementation class
@SpringBootApplication
|
@EnableXxxxx
The @Import approach above requires you to first know exactly which config classes or beans are in the third-party dependency. Instead, third-party dependencies can offer @EnableXxxxx annotations. These encapsulate @Import to provide common beans, so you just need to use @EnableXxxxx.
For instance, let’s wrap the previous @Import config class with an @EnableHeaderConfig annotation:
1
2
3
4
5
| @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)// Specify which bean objects or config classes to import
public @interface EnableHeaderConfig {
}
|
Then, simply add the @EnableHeaderConfig annotation to your startup class to import the relevant beans.
1
2
| @EnableHeaderConfig // Use the Enable-prefixed annotation provided by the third-party dependency
@SpringBootApplication
|
This is also the approach SpringBoot itself uses.
SpringBoot’s Auto-Configuration
Inside the @SpringBootApplication annotation, you’ll find @EnableAutoConfiguration. This, in turn, @Imports AutoConfigurationImportSelector.class, an implementation of the ImportSelector interface.
The selectImports() method is overridden in this implementation class:
1
2
3
4
5
6
7
8
9
| public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// Get the collection of auto-configuration class info
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
|
It calls getAutoConfigurationEntry() to fetch the collection of auto-configuration class info:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// Get the collection of all auto-configuration classes configured in the properties file
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
|
Specifically, the getCandidateConfigurations(annotationMetadata, attributes) method retrieves all auto-configuration classes configured in the properties files:
1
2
3
4
5
6
| protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
|
As you can see, it fetches the collection of configuration classes from META-INF/spring.factories and META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports files.
These two files are typically found within the starter dependencies you include.
So, when a SpringBoot application starts, it loads the configuration classes defined in these files. These configuration class details (fully qualified names) are then encapsulated into a String array, and finally, all these configuration classes are loaded into Spring’s IOC container via the @Import annotation, managed by the IOC container.
@Conditional
With so many configuration classes, will every single bean be registered in the IOC container? Nope. The @Conditional annotation allows bean objects to be wired based on specific conditions.
@Conditional is a parent annotation with many sub-annotations.
@ConditionalOnClass
Registers the bean into the IOC container ONLY if the corresponding bytecode file exists in the environment.
1
2
3
4
5
6
7
8
9
10
11
| @Configuration
public class HeaderConfig {
@Bean
// The bean is added to the IOC container only if the specified class exists in the environment
@ConditionalOnClass(name="io.jsonwebtoken.Jwts")
public HeaderParser headerParser(){
return new HeaderParser();
}
}
|
The bean above will only be injected into the IOC container if the JWT token dependency is present.
1
2
3
4
5
6
| <!--JWT Token-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
|
Testing:
1
2
3
4
5
6
7
8
9
10
11
| @SpringBootTest
public class AutoConfigurationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
public void testHeaderParser(){
System.out.println(applicationContext.getBean(HeaderParser.class));
}
}
|
@ConditionalOnMissingBean
Registers the bean into the IOC container ONLY if there’s no corresponding bean (by type or name) in the environment.
1
2
3
4
5
6
7
8
9
10
| @Configuration
public class HeaderConfig {
@Bean
@ConditionalOnMissingBean // The bean is added to the IOC container only if a bean of this type does not exist
public HeaderParser headerParser(){
return new HeaderParser();
}
}
|
The above will create the bean only if there isn’t already a HeaderConfig type bean in the IOC container.
You can also specify another bean’s name in the annotation:
1
2
3
4
5
6
7
8
9
10
11
| @Configuration
public class HeaderConfig {
@Bean
// The bean is added to the IOC container only if a bean with the specified name does not exist
@ConditionalOnMissingBean(name="deptController2")
public HeaderParser headerParser(){
return new HeaderParser();
}
}
|
In the example above, the HeaderConfig object will only be registered into the IOC if a bean named deptController2 doesn’t exist.
You can also specify a type:
1
2
3
4
5
6
7
8
9
10
11
| @Configuration
public class HeaderConfig {
@Bean
// The bean is added to the IOC container only if a bean of the specified type does not exist
@ConditionalOnMissingBean(HeaderConfig.class)
public HeaderParser headerParser(){
return new HeaderParser();
}
}
|
Running the example above and trying to call this bean would throw a NoSuchBeanDefinitionException. Since @Configuration implies @Component, a HeaderConfig bean is automatically created, preventing the HeaderParser bean from being created by this condition.
@ConditionalOnProperty
Registers the bean into the IOC container ONLY if the specified property and value exist in the configuration file.
Configuration File:
Configuration Class:
1
2
3
4
5
6
7
8
9
10
11
| @Configuration
public class HeaderConfig {
@Bean
// The bean is added to the IOC container only if the specified property name and value exist in the configuration file
@ConditionalOnProperty(name ="name",havingValue = "header")
public HeaderParser headerParser(){
return new HeaderParser();
}
}
|
Custom Starter Dependencies
Let’s say you want to build a custom starter dependency for Alibaba Cloud OSS.
First, naming conventions: Official SpringBoot starters are named spring-boot-starter-xxx, while third-party ones are xxx-spring-boot-starter.
Next, modules: You’ll typically define two modules following a standard pattern:
- The
starter module: This handles dependency management, bundling all necessary development dependencies into the starter. - The
autoconfigure module: This is where auto-configuration logic resides.
Once these two modules are set up, other projects just need to include the starter dependency, and the autoconfigure module will be transitively pulled in.
Module POM Files
aliyun-oss-spring-boot-starter Module:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- Introduce autoconfigure module -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
|
aliyun-oss-spring-boot-autoconfigure Module:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Introduce web starter dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--Alibaba Cloud OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
</dependencies>
</project>
|
Auto-Configuration
AliOSSAutoConfiguration Class:
1
2
3
4
5
6
7
8
9
10
11
12
13
| @Configuration
// Import AliOSSProperties class and hand it to Spring IOC for management
@EnableConfigurationProperties(AliOSSProperties.class)
public class AliOSSAutoConfiguration {
// Create AliOSSUtils object and hand it to Spring IOC container
@Bean
public AliOSSUtils aliOSSUtils(AliOSSProperties aliOSSProperties){
AliOSSUtils aliOSSUtils = new AliOSSUtils();
aliOSSUtils.setAliOSSProperties(aliOSSProperties);
return aliOSSUtils;
}
}
|
AliOSSProperties Class:
1
2
3
4
5
6
7
8
9
10
11
12
13
| /* Alibaba Cloud OSS related configuration */
@Data
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
// Region
private String endpoint;
// Access Key ID
private String accessKeyId ;
// Access Key Secret
private String accessKeySecret ;
// Bucket Name
private String bucketName;
}
|
AliOSSUtils Class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| @Data
public class AliOSSUtils {
private AliOSSProperties aliOSSProperties;
/**
* Implements uploading images to OSS
*/
public String upload(MultipartFile multipartFile) throws IOException {
// Get the input stream of the uploaded file
InputStream inputStream = multipartFile.getInputStream();
// Prevent file overwrites
String originalFilename = multipartFile.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
// Upload file to OSS
OSS ossClient = new OSSClientBuilder().build(aliOSSProperties.getEndpoint(),
aliOSSProperties.getAccessKeyId(), aliOSSProperties.getAccessKeySecret());
ossClient.putObject(aliOSSProperties.getBucketName(), fileName, inputStream);
// File access path
String url =aliOSSProperties.getEndpoint().split("//")[0] + "//" + aliOSSProperties.getBucketName() + "." + aliOSSProperties.getEndpoint().split("//")[1] + "/" + fileName;
// Close ossClient
ossClient.shutdown();
return url;// Return the path uploaded to OSS
}
}
|
Create the auto-configuration file META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
1
| com.aliyun.oss.AliOSSAutoConfiguration
|
Usage
Add Dependency:
1
2
3
4
5
6
| <!-- Introduce Alibaba Cloud OSS starter dependency -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
|
In the example above, the Alibaba Cloud OSS related configurations need to be read from the configuration file:
1
2
3
4
5
6
7
| # Configure Alibaba Cloud OSS parameters
aliyun:
oss:
endpoint: your_oss_region
accessKeyId: your_key_id
accessKeySecret: your_key_secret
bucketName: your_bucker_name
|
Testing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| @RestController
public class UploadController {
@Autowired
private AliOSSUtils aliOSSUtils;
@PostMapping("/upload")
public String upload(MultipartFile image) throws Exception {
// Upload file to Alibaba Cloud OSS
String url = aliOSSUtils.upload(image);
return url;
}
}
|