IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Springframework的@Import注解分析 -> 正文阅读

[Java知识库]Springframework的@Import注解分析


? ? ? ?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中的事件与监听

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-16 22:07:36  更:2022-03-16 22:12:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:42:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码