GVKun编程网logo

ioc 解析 import 标签(ioc源码解析)

30

本文的目的是介绍ioc解析import标签的详细情况,特别关注ioc源码解析的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解ioc解析import标签的机会,同时也不

本文的目的是介绍ioc 解析 import 标签的详细情况,特别关注ioc源码解析的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解ioc 解析 import 标签的机会,同时也不会遗漏关于**@Import({AutoConfigurationImportSelector.class})、10. 组件注册-@Import-使用ImportBeanDefinitionRegistrar、10、组件注册-@Import-使用ImportBeanDefinitionRegistrar、4.2 spring-import 标签的解析;的知识。

本文目录一览:

ioc 解析 import 标签(ioc源码解析)

ioc 解析 import 标签(ioc源码解析)

importBeanDefinitionResource 方法

	/**
	 * Parse an "import" element and load the bean definitions
	 * from the given resource into the bean factory.
	 * 用来解析spring配置文件中的import标签
	 * 如<import resource="spring-dao.xml"/>
	 *   <import resource="spring-consumer.xml"/>
	 * 过程如下:
	 * 		获取 source 属性的值,该值表示资源的路径
	 * 		解析路径中的系统属性,如”${user.dir}”
	 * 		判断资源路径 location 是绝对路径还是相对路径
	 * 		如果是绝对路径,则调递归调用 Bean 的解析过程,进行另一次的解析
	 * 		如果是相对路径,则先计算出绝对路径得到 Resource,然后进行解析
	 * 		通知监听器,完成解析
	 */
	protected void importBeanDefinitionResource(Element ele) {
		// 获取 resource 的属性值
		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		// 为空直接退出
		if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}

		// Resolve system properties: e.g. "${user.dir}"
		// 解析系统属性 格式如:"${user.dir}"
		location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

		Set<Resource> actualResources = new LinkedHashSet<>(4);

		// Discover whether the location is an absolute or relative URI
		// 判断 location 是绝对路径还是相对路径
		boolean absoluteLocation = false;
		try {
			// 以classpath*: 或者 classpath:开头,能够通过localtion构建出java.net.URL,判断为绝对路径
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
			// cannot convert to an URI, considering the location relative
			// unless it is the well-known Spring prefix "classpath*:"
		}

		// Absolute or relative?
		// 绝对路径
		if (absoluteLocation) {
			try {
				// 直接根据地址加载相应的配置文件
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
				}
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error(
						"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
			}
		}
		else {
			// No URL -> considering resource location as relative to the current file.
			// 相对路径,根据相应的地址计算出绝对路径
			try {
				int importCount;
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				if (relativeResource.exists()) {
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				else {
					String baseLocation = getReaderContext().getResource().getURL().toString();
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
				}
			}
			catch (IOException ex) {
				getReaderContext().error("Failed to resolve current resource location", ele, ex);
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
						ele, ex);
			}
		}
		// 解析成功后,进行监听器激活处理
		Resource[] actResArray = actualResources.toArray(new Resource[0]);
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}

**@Import({AutoConfigurationImportSelector.class})

**@Import({AutoConfigurationImportSelector.class})

:**将**AutoConfigurationImportSelector**这个类导入到spring容器中,**AutoConfigurationImportSelector**可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中 继续研究**AutoConfigurationImportSelector**这个类,通过源码分析这个类中是通过selectImports这个方法告诉springboot都需要导入那些组件: ![image-20200119172227144](./images/image-20200119172227144.png) **深入研究loadMetadata方法** ![image-20200119172325325](./images/image-20200119172325325.png) **深入getCandidateConfigurations方法** 个方法中有一个重要方法loadFactoryNames,这个方法是让SpringFactoryLoader去加载一些组件的名字。 ![image-20200119172418984](./images/image-20200119172418984.png) 继续点开loadFactory方法 ```java public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) { //获取出入的键 String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map> loadSpringFactories( @Nullable ClassLoader classLoader) { MultiValueMap result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { 这些内容,是从拉勾教育的《Java工程师高薪训练营》里学到的,课程内容非常全面,还有拉勾的内推大厂服务,推荐你也看看。

10. 组件注册-@Import-使用ImportBeanDefinitionRegistrar

10. 组件注册-@Import-使用ImportBeanDefinitionRegistrar

用户自定义的类实现ImportBeanDefinitionRegistrar接口,可以在registerBeanDefinitions函数中手动的将一个组件注册到容器中,代码如下:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param importingClassMetadata 当前类的注解信息
     * @param registry BeanDefinition注册类
     *         把所需要添加到容器中的bean,调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean definition = registry.containsBeanDefinition("io.scnb.bean.Red");
        boolean definition2 = registry.containsBeanDefinition("io.scnb.bean.Blue");
        if (definition && definition2) {
            // 指定bean定义信息(Bean的类型,scope等)
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }

    }
}

在使用时,需要在@Import注解的值中指明使用该实现了ImportBeanDefinitionRegistrar接口的类:

@Configuration
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {}

测试代码:

@Test
public void testImport() {
    printBeans(context);
}

public void printBeans(AnnotationConfigApplicationContext context) {
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
        System.out.println(name);
    }
}

测试代码输出结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig2
io.scnb.bean.Color
io.scnb.bean.Red
io.scnb.bean.Blue
io.scnb.bean.Yellow
person
steve
rainBow

10、组件注册-@Import-使用ImportBeanDefinitionRegistrar

10、组件注册-@Import-使用ImportBeanDefinitionRegistrar

10、组件注册-@Import-使用ImportBeanDefinitionRegistrar

public interface ImportBeanDefinitionRegistrar {

	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 */
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

10.1 实现ImportBeanDefinitionRegistrar 并重写 registerBeanDefinitionsfan方法

package com.hw.springannotation.conditional;

import com.hw.springannotation.beans.RainBow;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @Description TODO
 * @Author hw
 * @Date 2018/11/28 16:16
 * @Version 1.0
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * @param importingClassMetadata annotation metadata of the importing class 当前类的注解信息
     * @param registry               current bean definition registry   定义的注册类
     *                               所有需要添加到容器中的bean BeanDefinitionRegistry.registerBeanDefinition 手动注册进来
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean red = registry.containsBeanDefinition("com.hw.springannotation.beans.Red");
        boolean blue = registry.containsBeanDefinition("com.hw.springannotation.beans.Blue");

        if (red && blue) {
            // 指定Bean的定义信息,Bean的类型,作用域等
            BeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            // 注册一个Bean,指定bean名
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }
    }
}

10.2 使用

新建RainBow类,并在MainConfig中导入MyImportBeanDefinitionRegistrar.class

@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})

10.3 测试

4.2 spring-import 标签的解析;

4.2 spring-import 标签的解析;

  对于 spring 配置文件的编写,我想,对于经历过庞大项目的人,都有那种恐惧的心理,太多的配置文件。不过,分模块都是大多数人能想到的方法,但是,怎么分模块,那就是仁者见仁,智者见智了。我的策略是使用 import。
基本代码格式如下
 web.xml

<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

    <import resource="CTIContext.xml" />
    <import resource="customerContext.xml" />
    <import resource="customerServingContext.xml" />
    <import resource="manageContext.xml" />
    <import resource="routineContext.xml" />
    <import resource="systemContext.xml" />
</beans>
  applicationContext.xml 文件中使用 import 的方式导入有模块配置文件,以后若有新模块的加入,那就可以简单修改这个文件了,这样大大的简化了配置文件后期的复杂程度;
 
  Spring 的解析代码如下;
 1 /**
 2      * Parse an "import" element and load the bean definitions from the given resource
 3      * into the bean factory.
 4      */
 5     protected void importBeanDefinitionResource(Element ele) {
 6         // 获取resource 属性
 7         String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
 8         // 如果不存在resource ,则不做任何处理
 9         if (!StringUtils.hasText(location)) {
10             getReaderContext().error("Resource location must not be empty", ele);
11             return;
12         }
13 
14         // Resolve system properties: e.g. "${user.dir}"
15         // 解析系统属性.格式如 : ${user.dir}
16         location = environment.resolveRequiredPlaceholders(location);
17 
18         Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
19 
20         // Discover whether the location is an absolute or relative URI
21         // 判断是绝对地址还是相对地址
22         boolean absoluteLocation = false;
23         try {
24             absoluteLocation = ResourcePatternUtils.isUrl(location)
25                     || ResourceUtils.toURI(location).isAbsolute();
26         }
27         catch (URISyntaxException ex) {
28             // cannot convert to an URI, considering the location relative
29             // unless it is the well-known Spring prefix "classpath*:"
30         }
31 
32         // Absolute or relative?
33         // 如果是绝对地址,则直接根据地址加载对应的配置文件
34         if (absoluteLocation) {
35             try {
36                 int importCount = getReaderContext().getReader().loadBeanDefinitions(
37                         location, actualResources);
38                 if (logger.isDebugEnabled()) {
39                     logger.debug("Imported " + importCount
40                             + " bean definitions from URL location [" + location + "]");
41                 }
42             }
43             catch (BeanDefinitionStoreException ex) {
44                 getReaderContext().error(
45                         "Failed to import bean definitions from URL location ["
46                                 + location + "]", ele, ex);
47             }
48         }
49         else {
50             // No URL -> considering resource location as relative to the current file.
51             // 如果是相对地址,则计算出绝对地址
52             try {
53                 int importCount;
54                 // Resource 的多个子类 ,如 VfsResource,FileSystemResource,ClassPathResource
55                 // 而每个Resource的createRelative 方法都不太一样所以这里先使用子类的方法尝试解析,
56                 Resource relativeResource = getReaderContext().getResource().createRelative(
57                         location);
58                 if (relativeResource.exists()) {
59                     importCount = getReaderContext().getReader().loadBeanDefinitions(
60                             relativeResource);
61                     actualResources.add(relativeResource);
62                 }
63                 else {
64                     // 如果解析不成功,则使用默认解析器ResourcePatternResolver 进行解析
65                     String baseLocation = getReaderContext().getResource().getURL().toString();
66                     importCount = getReaderContext().getReader().loadBeanDefinitions(
67                             StringUtils.applyRelativePath(baseLocation, location),
68                             actualResources);
69                 }
70                 if (logger.isDebugEnabled()) {
71                     logger.debug("Imported " + importCount
72                             + " bean definitions from relative location [" + location
73                             + "]");
74                 }
75             }
76             catch (IOException ex) {
77                 getReaderContext().error("Failed to resolve current resource location",
78                         ele, ex);
79             }
80             catch (BeanDefinitionStoreException ex) {
81                 getReaderContext().error(
82                         "Failed to import bean definitions from relative location ["
83                                 + location + "]", ele, ex);
84             }
85         }
86         // 解析后进行监听器激活处理
87         Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
88         getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
89     }

 

关于ioc 解析 import 标签ioc源码解析的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于**@Import({AutoConfigurationImportSelector.class})、10. 组件注册-@Import-使用ImportBeanDefinitionRegistrar、10、组件注册-@Import-使用ImportBeanDefinitionRegistrar、4.2 spring-import 标签的解析;等相关内容,可以在本站寻找。

本文标签: