? ? ? ?IOC(Inversion of Control)即控制反转,它是一种思想。 在这过程中,对象创建后通过在对象实例上设置属性来定义他们间的依赖关系,然后IOC容器在创建bean的时候注入这些依赖。在传统应用程序中, 可以在对象中通过new创建依赖的对象,这种方式属于直接获取依赖的对象, 而IOC意味着将设计好的对象交给容器控制,因此称为控制反转。
1.@Import 能做什么
? ? ? 在Spring框架中,可以通过 XML定义Bean并加载到容器。一般情况下,每个单独的XML配置文件代表体系结构中的一个逻辑层或模块,让bean定义跨越多个XML文件会很有用,因此Spring支持通过一个或者多个<import/>元素 实现加载另一个XML文件中的bean定义。例如下面的例子
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
Spring提供的基于Java的配置允许编写注释,这可以降低配置的复杂性。像在Spring XML文件中使用< import/>元素来帮助模块化配置一样,@Import注释允许从另一个配置类加载@Bean定义。例如:
@Configuration
public class ConfigA {
@Bean
public User getUser() {
return new User();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public Customer getCustomer() {
return new Customer();
}
}
用这种方式就不需要同时指定两个配置类ConfigA和ConfigB,初始化上下文的时候只需要显示提供ConfigB就可以了 ,例如下面的代码,运行后可以同时得到User和Customer对象
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
User a = ctx.getBean(User.class);
Customer b = ctx.getBean(Customer.class);
}
}
由此可见,使用@Import 简化了容器实例化,因为只需要处理一个类,而不是要求您在构造期间记住大量的@Configuration类
2.@Import源码分析
1.@Import注解分析
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
@Import注解的全类名是org.springframework.context.annotation.Import。其只有一个默认的value属性,该属性类型为Class<?>[],表示可以传入一个或多个Class对象。例如RedisAutoConfiguration对象
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
//省略
}
通过注释可以看出,该注解有如下作用:
- 可以导入一个或多个组件类(通常是@Configuration配置类)
- 该注解的功能与Spring XML中的<import/>元素相同。可以导入@Configuration配置类、ImportSelect和ImportBeanDefinitionRegistrar的实现类。
- 从4.2版本开始,还可以引用常规组件类(普通类),该功能类似于AnnotationConfigApplicationContext.register方法。
- 该注解可以在类中声明,也可以在元注解中声明。
- 如果需要导入XML或其他非@Configuration定义的资源,可以使用@ImportResource注释。
2.@Import注解如何解析
Spring注册bean的过程中,ConfigurationClassParser对象主要用于解析配置类,其中ConfigurationClassParser #doProcessConfigurationClass方法通过读取源类中的注释、成员和方法,应用处理并构建完整的ConfigurationClass。@Import就是在此方法中被解析
部分源码如下:
//坐标:ConfigurationClassParser#doProcessConfigurationClass
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//省略
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
//省略
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}else {
this.importStack.push(configClass);
try {
//获取Import导入进来的类
for (SourceClass candidate : importCandidates) {
//1.判断该类是不是实现了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
Class<?> candidateClass = candidate.loadClass();
//实例化实现SelectImport接口的类
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
//如果实现了DeferredImportSelector
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}else {
//普通的ImportSelector实现,调用selectImports方法,返回String数组,类的全类名称,并递归解析这些class
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
//2.判断该类是否实现了ImportBeanDefinitionRegistrar接口
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
//实例化类
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}else {
//3. 如果没有实现上面两个接口,例如@Configuration类
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
//解析Configuration读取注解属性和方法
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
//省略catch ..finally
}
}
从源码上看processImports方法处理中对@Import的处理机制大致如下
1.如果Import的类实现了ImportSelector,一般情况下会调用selectImports方法,返回String数组,类的全类名称,并递归解析这些class
2.如果Import的类实现了ImportBeanDefinitionRegistrar 接口,会先实例化并把实例放入到configClass中名叫importBeanDefinitionRegistrars的Map集合中暂存
3.如果没有实现上述两个接口 (例如@Configuration),导入类将会解析并将定义的bean放入IOC容器中
??
前一篇:Spring中的事件与监听
|