Spring 学习之扩展点总结之@Import(五)

Spring 源码系列

1、Spring 学习之扩展点总结之后置处理器(一)
2、Spring 学习之扩展点总结之后置处理器(二)
3、Spring 学习之扩展点总结之自定义事件(三)
4、Spring 学习之扩展点总结之内置事件(四)
5、Spring 学习之扩展点总结之@Import(五)
6、Spring 学习之AOP总结之基础介绍(六)
7、Spring 学习之AOP总结之实现代理(七)
8、SpringBoot 学习之自动配置基本原理(八)
9、SpringBoot 学习之启动原理(九)
10、ElasticSearch学习随笔之SpringBoot Starter 操作
11、图数据库 Neo4j 学习之SpringBoot整合
12、SpringBoot 学习之常用 Filter / Advice 总结
13、SpringBoot+FastJson 优雅的过滤 Response Body
14、Spring Security + Oauth2 认证授权

前言

在 Spring之扩展点总结 一、二、三、四(见文章开始处)种分别介绍了Spring的各种后置处理器,以及自定义事件,内置事件的使用,但是还没完,Spring 还提供了更加方便的注册 Bean 到上下文的途径,@Import 注解。

一、什么是 @Import 注解

网上四处看了看网友们说的,基本都差不多,官网查找内容太多,无奈之下,直接查看源码注释翻译出来。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
	//@Configuration, ImportSelector, ImportBeanDefinitionRegistrar, or regular component classes to import.
	Class<?>[] value();

}

源码注释:
Indicates one or more component classes to import — typically @Configuration classes.
Provides functionality equivalent to the element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register).
@Bean definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.
May be declared at the class level or as a meta-annotation.
If XML or other non-@Configuration bean definition resources need to be imported, use the @ImportResource annotation instead.

源码中这样解释 @Import 的:
1、表明一个或者多个组件类导入,典型的就是导入**@Configuration** 配置类(作者:在Spring Boot中体现),提供了和 Spring 的 XML 配置文件一样的功能性的导入,允许导入 @Configuration 类,ImportSelectorImportBeanDefinitionRegistrar 的实现类,还可以注册普通类到容器中。
2、在 @Configuration 配置类中用 @Bean 定义的实体应该用 @Autowired 导入。任何一个Bean本身可以自动注入,或者声明配置类声明的 Bean 的配置类实例也可以自动。
3、如果是 XML 或者是 其他非 @Configuration 配置类定义的资源需要导入时,用 @ImportResource 代替导入。

二、@Import注解四种用法

2.1 import 普通实体类

1、新建 BaseService 类,这是一个普通的类,并没有继承或者实现其他接口,也没有注解。

BaseService 类中有个 hello() 方法,为了方便测试,打印一句话。

public class BaseService {

	public void hello(){
		System.out.println("BaseService:hello");
	}
}

2、新建 MainConfig 的配置类,用 @Configuration 标注,然后用 @Import 引入 BaseService 类。

@Configuration
@Import(value = BaseService.class)
public class MainConfig {
}

3、main 测试

在 main() 方法中,实例化容器,可见 @ComponentScan 注解只扫描了 com.self.test.iocTest 包,然后我们获取普通类的实例,看能不能获取到。
如果能获取到 BaseService 实例,我们调用 hello() 方法看能不能成功。

@ComponentScan("com.self.test.iocTest")
public class MainStartApp {

	public static void main(String[] args) throws Exception {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainStartApp.class);
		BaseService baseService = applicationContext.getBean(BaseService.class);
		baseService.hello();
	}
}

测试结果:
测试结果

从测试结果中可以看出,成功拿到 BaseService 的实例并且调用了 hello() 方法。

2.2 import ImportSelector的实现类

1、依旧是需要 BaseService 这个普通类,并没有继承或者实现其他接口,也没有注解。

还是要 BaseService 类中有个 hello() 方法,为了方便测试,打印一句话。

public class BaseService {

	public void hello(){
		System.out.println("BaseService:hello");
	}
}

2、新建 ImportSelectorTest 类并且实现 ImportSelector 接口。

ImportSelectorTest 类只是实现了 ImportSelector 接口,并没有注解之类的东西,复写了 selectImports() 方法并且返回一个 String[] 数据,是类名的全路径,Spring 会通过返回的类的全路径来实例化这些 Bean。

public class ImportSelectorTest implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.self.test.iocTest.BaseService"};
	}

	@Override
	public Predicate<String> getExclusionFilter() {
		return null;
	}
}

3、还是需要 MainConfig 配置类,用 @Configuration 标注,这次是用 @Import 引入我们上面新建的 ImportSelectorTest 类。

@Configuration
@Import(value = ImportSelectorTest.class)
public class MainConfig {
}

4、main() 测试

main() 方法还是不变,获取 BaseService 的实例并且调用 hello() 方法。

@ComponentScan("com.self.test.iocTest")
public class MainStartApp {

	public static void main(String[] args) throws Exception {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainStartApp.class);
		BaseService baseService = applicationContext.getBean(BaseService.class);
		baseService.hello();
	}
}

测试结果:
测试结果
5、ImportSelector 接口还提供了 getExclusionFilter() 方法,此方法返回了 Predicate 预判断,不满足 Predicate 的 Spring 则不会进行实例化。

在getExclusionFilter() 方法中,判断如果类名是 “Base” 开头的,那么返回 true, 则可以注册成功,这里我用了取反,那么 Spring 应该不会实例化注册成功的。

public class ImportSelectorTest implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.self.test.iocTest.BaseService"};
	}

	@Override
	public Predicate<String> getExclusionFilter() {
		return s -> !s.startsWith("Base");
	}
}

测试结果如下:
测试结果

报错了,说明 getExclusionFilter() 起了作用,我们把代码改成这样,return s -> s.startsWith(“Base”); 在 main() 方法中获取 BaseService 成功

6、为了验证 BaseService 是否是通过 ImportSelectorTest 类导入进来的,我们把 ImportSelectorTest 类里面的 selectImports() 方法返回的类路径修改一下,再看能不能获取到 BaseService 实体了。

我们在 BaseService 类名修改成 BaseService1111。

@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
	return new String[]{"com.self.test.iocTest.BaseService1111"};
}

main() 方法测试结果如下:
测试结果

报错了,没有找到 BaseService1111 类,所以 ImportSelectorTest 起作用了。

2.3 import DeferredImportSelector的实现类

DeferredImportSelector 接口继承了 ImportSelector 接口,和 ImportSelector 的不同点就是,DeferredImportSelector 是延迟执行的,而且加入了 Group 的概念,具体实现看代码。

1、依旧是需要 BaseService 这个普通类。

public class BaseService {

	public void hello(){
		System.out.println("BaseService:hello");
	}
}

2、新建 DeferredImportSelectorTest 类 实现接口 DeferredImportSelector 并且复写 selectImports() 方法,getExclusionFilter() 的用法和 ImportSelector 一样的。

不过在 DeferredImportSelectorTest 类中,我们还复写了一个 getImportGroup() 方法,此方法返回一个 Group 类型,所以我们还需要新建一个类 SelectorTestGroup 并且实现内部接口 DeferredImportSelector.Group,然后复写 process() 方法和 selectImports() 方法。
在 process() 方法提供的参数 selector 我们可以获取到 DeferredImportSelectorTest.selectImports() 方法返回的类名,然后加入到一个组里面,再由 SelectorTestGroup.selectImports() 返回进行注册。

public class DeferredImportSelectorTest implements DeferredImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.self.test.iocTest.BaseService"};
	}

	@Override
	public Predicate<String> getExclusionFilter() {
		return null;
	}

	@Override
	public Class<? extends Group> getImportGroup() {
		return SelectorTestGroup.class;
	}

}

class SelectorTestGroup implements DeferredImportSelector.Group{

	private final List<Entry> importInstances = new ArrayList<>();

	@Override
	public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
		Arrays.stream(selector.selectImports(metadata)).forEach(className -> {
			importInstances.add(new Entry(metadata, className));
		});
	}

	@Override
	public Iterable<Entry> selectImports() {
		return importInstances;
	}
}

3、main 测试

在实例化了容器之后,我们可以从容器中 获取到 BaseService 的实例。

	public static void main(String[] args) throws Exception {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainStartApp.class);
		BaseService baseService = applicationContext.getBean(BaseService.class);
		baseService.hello();
	}

测试结果:
测试结果

从测试结果中看,我们从容器中获取到了 BaseService 实例,调用了 hello() 方法,打印了这句话。

三、总结

@Import 使用起来十分方便,是一个Spring很重要的一个扩展点,后面的 SpringBoot 的自动配置封装就是通过这个注解引入了SpringBoot 的 各种 starter 组件中的配置类实现了主动装配。
DeferredImportSelector 的和 ImportSelector 感觉差不太多,具体底层原理和各种高级用法,请看下篇分解。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: @Import注解是Spring框架的一种注解,可以用来快速导入多个组件,包括类、包或者配置类。 使用@Import注解可以在一个配置类中快速导入其他组件,而不必通过@Bean或@ComponentScan注解来导入。这样可以方便地将功能模块化,并且使用起来也更加方便。 使用方法如下: 1. 在配置类上使用@Import注解,并指定要导入的组件的类型数组。 例如: ``` @Import({MyConfiguration.class, MyBean.class}) public class AppConfig { // ... } ``` 2. 也可以使用ImportSelector接口和ImportBeanDefinitionRegistrar接口来动态选择和注册组件。 例如: ``` @Import(MyImportSelector.class) public class AppConfig { // ... } public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 在这里可以动态的返回需要导入的组件 return new String[] {MyBean.class.getName()}; } } ``` 使用@Import注解导入组件的好处是可以方便地将组件模块化,可以将一个功能模块分成若干个配置类,然后使用@Import注解导入。这样可以让代码更加清晰,更加方便维护。 ### 回答2: @Import注解是Spring框架中的一种注解,在使用该注解时,可以将其他配置类或者Bean定义类导入到当前配置类中。通过@Import注解,可以在一个配置类中同时引入多个不同的配置类或者Bean定义类,实现了配置的模块化和复用。 @Import注解可以实现多种功能,具体包括以下几点: 1. 导入配置类:通过@Import注解可以将其他的配置类导入到当前的配置类中,这样可以将多个配置类组合在一起,实现配置的分离和复用,提高代码的可维护性和可读性。 2. 导入Bean定义类:除了配置类之外,@Import注解也可以将其他的Bean定义类引入到当前配置类中,这样可以将多个不同的Bean定义类组合在一起,实现Bean的组装和扩展。 3. 导入自动配置类:Spring Boot框架中的自动配置就是通过@Import注解来实现的,通过将相应的自动配置类导入到配置类中,可以实现对应功能的自动配置和初始化,减少了开发人员的工作量。 4. 导入条件配置:通过@Import注解可以根据不同的条件来选择性地导入不同的配置类或者Bean定义类,根据具体的条件来进行动态的选择和配置,实现更加灵活和可配置化的开发。 总结来说,@Import注解在Spring框架中起到了组合和扩展配置的作用,可以将多个配置类或者Bean定义类导入到当前配置类中,实现了配置的模块化和复用,同时也提供了条件导入的功能,使得配置的选择更加灵活和可配置化。 ### 回答3: @Import注解是Spring框架中的一个注解,作用是用于导入其他的配置类或者Bean。通过@Import注解,我们可以将其他的配置类或者Bean引入到当前的配置类中,从而实现配置类之间的解耦。 @Import注解可以用于导入其他的@Configuration配置类,这样可以方便地将多个配置类合并到一个总的配置类中。通过@Import注解,我们可以将不同的配置类按照逻辑进行划分,分别编写配置类,然后使用@Import注解将它们引入到一个总的配置类中。这样做的好处是,能够更好地组织和管理配置类,提高代码的可读性和可维护性。 除了导入其他的配置类,@Import注解还可以用于导入其他的普通的Bean。这对于一些无法通过@Configuration注解进行配置的Bean来说非常有用。通过@Import注解,我们可以将这些Bean引入到当前的配置类中,然后使用@Autowired注解进行注入。 总之,@Import注解的作用是使得配置类之间可以进行解耦,并且可以方便地引入其他的配置类或者Bean。它在Spring框架中的应用非常广泛,能够有效地提高代码的可读性、可维护性和复用性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值