稀土掘金技术社区 02月27日
Spring中@Import原理和使用
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

@Import注解是Spring框架中一个强大的工具,用于将普通类、组件类以及ImportSelector和ImportBeanDefinitionRegistrar接口的实现类引入到Spring容器中,从而实现配置的模块化,使代码结构更清晰,易于维护。Spring集成许多框架正是通过@Import实现的。文章详细介绍了@Import的三种用法:导入普通类、与ImportSelector接口结合使用、与ImportBeanDefinitionRegistrar接口结合使用,并深入解析了其原理,最后通过分析Spring源码中@EnableAspectJAutoProxy和@EnableAsync的实现,展示了@Import在模块化功能选项中的应用。

💡 @Import注解允许将普通类导入Spring容器,即使这些类没有使用@Component注解。通过`@Import(OrderService.class)`可以直接将`OrderService`类型的bean添加到容器中,但需要注意,bean的名称是类的全类名。

⚙️ ImportSelector接口提供了动态选择导入配置类的能力。通过实现`selectImports`方法,可以返回一个全类名数组,这些类将被添加到Spring容器中。`getExclusionFilter`方法则允许排除`selectImports`方法返回值中的某些类,提供了更灵活的配置导入方式。

📝 ImportBeanDefinitionRegistrar接口允许通过编程方式动态注册BeanDefinition。通过实现`registerBeanDefinitions`方法,可以编程式地向容器中注册beanDefinition,Spring容器将根据这些定义创建对应的bean,这为动态配置bean提供了强大的支持。

🧩 Spring通过ConfigurationClassPostProcessor类扫描并处理@Configuration类中的@Import注解。解析@Import注解时,会根据导入类的类型采取不同的处理方式,最终将所有导入的类注册为BeanDefinition,由Spring容器进行管理。

原创 程序员侠客行 2025-02-27 08:30 重庆

点击关注公众号,“技术干货”及时达!

点击关注公众号,“技术干货” 及时达!

@Import注解是 Spring 框架中一个非常强大的工具,它允许你将普通类、组件类、ImportSelector实现类和ImportBeanDefinitionRegistrar实现类引入到容器中。通过@Import,你可以实现配置的模块化,使得代码更加清晰和易于维护。

Spring 集成很多框架时,就是通过 @Import 来实现的。

本文中源码来自 Spring 5.3.x 分支,github 源码地址:github.com/spring-proj…[1]

一 如何使用 @Import

该注解只有一个 value 属性,取值为需要导入的类的 class 对象。

有三种用法,介绍如下。

1.1 @Import 导入普通类

对于普通类(「没有被声明为 Component」),通过 @Import 也可以添加到 Spring 容器中。

例如,使用 @Import(OrderService.class) ,就能向容器中添加 OrderService 类型的 bean 「。」

import com.xiakexing.service.OrderService;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(OrderService.class)
public class AppConfig {
}
// 没有使用@Component的普通类
public class OrderService {
public void test() {
System.out.println("执行OrderService.test");
}
}
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
OrderService orderService = context.getBean("orderService", OrderService.class);
orderService.test();
}
}

当执行上面代码时,会出现报错:No bean named 'orderService' available.

「注意,通过 @Import 直接导入的 bean,名称是该类的全类名。」

因此应这样获取 context.getBean(OrderService.class.getName(), OrderService.class)

1.2 @Import 与 ImportSelector 接口

ImportSelector接口,允许你自定义条件动态选择要导入的配置类,有两个方法:

    selectImports:返回一个全类名的数组,这些类将被添加到 spring 容器中。注意,该方法可以返回空数组,但是不能返回 null!

    getExclusionFilter:返回一个 Predicate,用于排除 selectImports 方法返回值中的某些类。

来看示例,OrderService 与上面相同,增加 UserService 类。

public class UserService {
public void test() {
System.out.println("执行UserService.test");
}
}

SimpleImportSelector 只导入 UserService 类。

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.util.function.Predicate;
public class SimpleImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{OrderService.class.getName(), UserService.class.getName()};
}
// 排除全类名中有Order的类
@Override
public Predicate<String> getExclusionFilter() {
return new Predicate<String>() {
@Override
public boolean test(String name) {
return name.contains("Order");
}
};
}
}
import com.xiakexing.service.SimpleImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(SimpleImportSelector.class)
public class AppConfig {
}
import com.xiakexing.service.OrderService;
import com.xiakexing.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class.getName(), UserService.class);
userService.test();
OrderService orderService = context.getBean(OrderService.class.getName(), OrderService.class);
orderService.test();
}
}

结果就是 userService.test() 执行成功,而获取 orderService 时报错。

1.3 @Import 与 ImportBeanDefinitionRegistrar 接口

ImportBeanDefinitionRegistrar 接口允许通过编程方式动态注册 BeanDefinition。

来看示例,定义 User 类。

public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
" + name + ''' +
", age=" + age +
'}';
}
}

自定义 ImportBeanDefinitionRegistrar 接口实现,编程式向容器中注册 User 类的 beanDefinition,Spring 容器将创建对应的 bean。

import com.xiakexing.entity.User;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class SimpleBeanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
definition.setBeanClass(User.class);
ConstructorArgumentValues values = new ConstructorArgumentValues();
values.addIndexedArgumentValue(0, "Tom");
values.addIndexedArgumentValue(1, 29);
definition.setConstructorArgumentValues(values);
definition.setScope("singleton");
registry.registerBeanDefinition("user", definition);
}
}
import com.xiakexing.service.SimpleBeanRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(SimpleBeanRegistrar.class)
public class AppConfig {
}

二 @Import 的原理

Spring 中ConfigurationClassPostProcessor类,会扫描所有的@Configuration类,并处理其中的@Import注解。

    扫描配置类:Spring 容器启动时,会扫描所有的@Configuration类。

    解析 @Import 注解:对于每个配置类,Spring 会解析其中的@Import注解,获取需要导入的类。

    处理导入的类:根据导入的类的类型(普通类、@Configuration类、ImportSelector实现类、ImportBeanDefinitionRegistrar实现类),Spring 会采取不同的处理方式。

    注册 BeanDefinition:最终,Spring 会将所有导入的类注册为 BeanDefinition,让 Spring 容器管理。

源码中ConfigurationClassParser类负责解析@Configuration类,其中 doProcessConfigurationClass 方法逻辑如下:

    先递归地收集配置类或某个注解上的 @Import 的 value 值;

    然后调用processImports方法来处理@Import注解,分 3 种情况处理导入的类。

    例如,对于 ImportBeanDefinitionRegistrar 接口实现,先收集到 Map 中缓存起来,然后遍历 Map,逐个调用 registerBeanDefinitions 方法

三 源码中使用

Spring 源码中,模块化、插拔式的功能选项,就是通过 @Import 实现的。如 aop、

3.1 @EnableAspectJAutoProxy 实现

当在配置类上添加 @EnableAspectJAutoProxy 时,就启用了 Spring aop 功能。 「源码中,EnableAspectJAutoProxy 注解上通过 Import 引入了 AspectJAutoProxyRegistrar」,该类 ImportBeanDefinitionRegistrar 接口的实现,在 registerBeanDefinitions 方法中向容器注册了 AnnotationAwareAspectJAutoProxyCreator。

AnnotationAwareAspectJAutoProxyCreator 类是 BeanPostProcessor 接口的一个实现,会解析容器中 @Aspect 注解的 bean,封装 @Before 等方法为 Advisor 对象;在 bean 的初始化后阶段,为切面命中的 bean 创建代理对象。

3.2 @EnableAsync 实现

@EnableAsync 注解提供了开箱即用的异步解决方案,我们只需在想要异步执行的方法上加 @Async 即可。 「源码中,EnableAsync 注解上通过 Import 引入了 AsyncConfigurationSelector 类,」 该类是 ImportSelector 接口实现,在 selectImports 方法中引入了 ProxyAsyncConfiguration 类(默认使用 JDK 动态代理)。在 ProxyAsyncConfiguration 中,又声明了 AsyncAnnotationBeanPostProcessor 的 bean。后者是 BeanPostProcessor 接口实现,在 bean 的初始化后阶段,为使用了 @Async 的 bean 创建代理对象。

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

@Import Spring框架 配置模块化 ImportSelector ImportBeanDefinitionRegistrar
相关文章