During actual development, we often need to integrate various third-party libraries to improve development efficiency or extend system functionality. However, when using the Spring or Spring Boot framework, how to properly register components from these third-party libraries into the IoC container often becomes a problem that needs to be solved.
Manual Registration with @Bean
Taking the third-party utility package HuTool as an example, first we introduce the Maven dependency for HuTool:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.41</version>
</dependency>
In HuTool, if we want to use the cn.hutool.http.HttpUtil utility class, we hope to use it like this:
@Autowired
private HttpUtil httpUtil;
But obviously, writing it directly like this will result in an error:
No qualifying bean of type 'cn.hutool.http.HttpUtil' available
This is because all classes in Hutool use static methods and are not registered in the Spring container.
We can manually create a Bean in a configuration class using the @Bean annotation:
@Configuration
public class HutoolConfig {
@Bean
public HttpUtil getHttpUtil() {
return new HttpUtil();
}
}
Then we perform a unit test:
@Autowired
private HttpUtil httpUtil;
@Test
void test1(){
System.out.println(httpUtil.get("https://www.baidu.com"));
}

When creating a Bean in a configuration class, if you need to pass parameters, and if the IoC container has this parameter type, it can be auto-injected. For example:
@Configuration
public class SecurityConfig {
@Bean
public AuthenticationManager getAuthenticationManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.build();
}
}
This is equivalent to omitting:
@Autowired
HttpSecurity http;
@ComponentScan — Extending Scan Paths
If certain classes in a third-party library already use annotations like @Component, @Service, @Repository, but these classes are not under our default scan path, we can extend the scan range through @ComponentScan.
@SpringBootApplication
@ComponentScan(basePackages = {
"com.example.myapp", // Your project package path
"com.baomidou.mybatisplus.extension.plugins" // Third-party library package path
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- basePackages is an array. Once
@ComponentScanis used, the default scan path in@SpringBootApplicationbecomes ineffective, and we must configure our project’s package path in basePackages, then add the third-party library’s package path.
@Import to Directly Import Configuration Classes
Using @Import can directly import configuration classes or regular classes into the Spring container:
// Third-party configuration class
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setPassword("your-password");
return Redisson.create(config);
}
}
// Import in main configuration class
@Configuration
@Import(RedissonConfig.class)
public class ApplicationConfig {
}
Or use it directly on the startup class:
@SpringBootApplication
@Import({RedissonConfig.class, OtherConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Importing Regular Classes
@Import can also directly import regular classes without any annotations:
// Third-party regular class
public class DataConverter {
public String convert(Object data) {
return JSON.toJSONString(data);
}
}
// Import configuration
@Configuration
@Import(DataConverter.class)
public class ConverterConfig {
}
After importing, it can be directly injected and used:
@Service
public class DataService {
@Autowired
private DataConverter dataConverter;
public String processData(Object data) {
return dataConverter.convert(data);
}
}
ImportSelector for Dynamic Bean Registration
ImportSelector allows us to dynamically select which configuration classes to import based on conditions, which is very useful when we need to register Beans dynamically based on environment or configuration.
// Custom ImportSelector
public class ElasticsearchImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// Can dynamically decide which configuration classes to import based on annotation attributes, environment variables, etc.
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableElasticsearch.class.getName());
String mode = (String) attributes.get("mode");
if ("rest".equals(mode)) {
return new String[]{
"com.example.config.ElasticsearchRestClientConfig"
};
} else if ("transport".equals(mode)) {
return new String[]{
"com.example.config.ElasticsearchTransportConfig"
};
}
// Default configuration
return new String[]{
"com.example.config.ElasticsearchDefaultConfig"
};
}
}
// Configuration class
public class ElasticsearchRestClientConfig {
@Bean
public ElasticsearchClient elasticsearchClient() {
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)
).build();
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper()
);
return new ElasticsearchClient(transport);
}
}
// Use in configuration class
@Configuration
@Import(ElasticsearchImportSelector.class)
public class SearchConfig {
}
More Complex Example: Conditional Import
public class ConditionalImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
List<String> imports = new ArrayList<>();
// Check if a certain class exists in the classpath
if (ClassUtils.isPresent("com.example.ThirdPartyClass", null)) {
imports.add("com.example.config.ThirdPartyConfig");
}
// Check system properties
if ("true".equals(System.getProperty("enable.cache"))) {
imports.add("com.example.config.CacheConfig");
}
// Check environment variables
String profile = System.getenv("SPRING_PROFILES_ACTIVE");
if ("prod".equals(profile)) {
imports.add("com.example.config.ProdConfig");
}
return imports.toArray(new String[0]);
}
}
Using DeferredImportSelector
If you need to perform imports after all @Configuration classes have been processed, you can implement the DeferredImportSelector interface:
public class DeferredElasticsearchImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
// Deferred import logic
return new String[]{"com.example.config.ElasticsearchConfig"};
}
}
@EnableXxx Composite Annotations
@EnableXxx is a high-level encapsulated configuration approach that typically combines @Import with other annotations, providing users with a simple switch to enable a certain functional module.
@EnableXxx can be placed on the startup class or on a configuration class.