Spring之资源定位、加载、解析、注册链路分析

前置知识

1 SpringIoC做的三件事

  • 解析配置
  • 定位与注册对象
  • 注入对象

2 Bean&BeanDefinition

  • Bean的本质就是Java对象,知识这个对象的声明周期由容器来管理,不需要为了Bean而在原来Java类上添加任何额外的限制,对Java对象的控制都体现在配置上
  • Spring会根据配置文件或者配置类来生成描述Bean的BeanDefinition,常用属性:
    • @Scope:作用域
      • singleton
      • prototype
      • request
      • session
      • globalsession
    • @lazy:懒加载,决定Bean是否延迟加载
    • @Primiary:设置为true的Bean会是优先实现的类
    • factory-bean和factory-method(@Configuration和@Bean)

factory-bean和factory-method(@Configuration和@Bean)

//实例工厂调用
public class UserFactory {

	public User getUser(){
		return new User();
	}

}
<!-- 使用实例工厂进行创建 -->
<!-- 需要先创建factoryBean对象,再通过factoryBean对象进行调用 -->
<bean id="userFactory" class="com.imooc.entity.factory.UserFactory"/>
<bean id="user3" factory-bean="userFactory" factory-method="getUser" scope="prototype" />

3 设计模式之模板方法模式

  • 围绕抽象类,实现通用逻辑,定义模板结构,部分逻辑由子类实现
  • 优点:
    • 复用代码
    • 反向控制
  • 模式涉及的方法种类
    • 模板方法
    • 具体方法
    • 钩子方法,子类依据情况实现
    • 抽象方法,必须让子类实现的方法
      在这里插入图片描述

3 容器初始化的主要工作

在这里插入图片描述

  • 将xml或者注解等配置信息读取到内存中
  • 在内存中这些配置文件会当作Resource对象
  • 之后会将Resource对象解析成BeanDefination实例
  • 最后将BeanDefination注册到容器中

1 BeanDefination家族

在这里插入图片描述

  • BeanDefinition接口: 里面的定义了获取属性值的方法
  • AbstractBeanDefinition: 提供了一些操作BeanDefination的通用方法

RootBeanDefinition

  • 它可以自己作为BeanDefination,也可以作为其它BeanDefination的父类,但是不能作为其它BeanDefination的子类
    • Spring中的继承关系不是通过extends,implements来定义的,而是通过在BeanDefination中设置Parent属性来定义的
    public void setParentName(@Nullable String parentName) {
    	if (parentName != null) {
    		throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
    	}
    }
    
  • RootBeanDefinition通常用于在运行时候接收多个BeanDefination合并起来的信息(合并具有继承关系的BeanDefination属性),一般配置文件中的属性会被解析为RootBeanDefinition,但是在Spring2.5之后使用GenericBeanDefinition取代了RootBeanDefinition和ChildBeanDefinition,但在合并的时候还是会采用RootBeanDefinition来接受
  • ChildBeanDefinition完全被GenericBeanDefinition取代,不用学习

GenericBeanDefinition

  • 由ParentName属性,能设置父类

2 Spring容器家族

2.1 BeanFactory

  • BeanFactory:所有容器的都要实现这个接口,定义了容器的最基础的方法
2.1.1 区分FactoryBean
  • FactoryBean:也是接口,用户可以通过它用比较复杂的方式生成Bean,使用getObject()方法生成Bean

    public class UserFactoryBean implements FactoryBean<User> {
    	@Override
    	public User getObject() throws Exception {
    		return new User();
    	}
    
    	@Override
    	public Class<?> getObjectType() {
    		return User.class;
    	}
    }
    
    <bean id="userFactoryBean" class="com.imooc.entity.factory.UserFactoryBean"/>
    
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring/spring-config.xml");
    User user1 = (User)context.getBean("userFactoryBean");
    System.out.println(user1);
    
  • BeanFactory定义了获取FactoryBean的方式

    // 对FactoryBean的转义定义,提供获取FactoryBean的方式,
    // 如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
    // 如果需要得到工厂本身,需要转义
    String FACTORY_BEAN_PREFIX = "&";
    
    UserFactoryBean userFactoryBean = (UserFactoryBean) applicationContext.getBean("userFactoryBean");
    

2.2 容器的分类

2.2.1 简单容器
  • 第一个是以BeanFactory接口为主的简单容器
    在这里插入图片描述
2.2.2 高级容器 - 广泛使用
  • 以ApplicationContext为核心的高级容器
    在这里插入图片描述

2.3 简单容器 - BeanFactory

2.3.1 二级接口
  • ListableBeanFactory: 以列表的形式提供Bean的相关信息
    在这里插入图片描述

  • HierarchicalBeanFactory: 使得容器具备了层级关系,允许BeanFactory进行工厂分层

    // 返回本工厂的父工厂
    @Nullable
    BeanFactory getParentBeanFactory();
    
    // 本工厂是否包含这个Bean,只在本层查找,不去父类中查找
    boolean containsLocalBean(String name);
    
  • AutowireCapableBeanFactory: 赋予容器自动装配Bean的能力

    • 具体由AbstractAutowireCapableBeanFactory实现
2.3.2 DefaultListableBeanFactory - 重要实现类
  • 第一个可以独立运行的容器
    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
    		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {}
    
  • 存储容器中所有已经注册的BeanDefination的载体
    // 存储容器中所有已经注册的BeanDefination的载体
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    
  • 后面ApplicationContext会调用DefaultListableBeanFactory实现BeanDefination的注册
    //---------------------------------------------------------------------
    // Implementation of BeanDefinitionRegistry interface
    //---------------------------------------------------------------------
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    		throws BeanDefinitionStoreException {
    }
    

2.3 高级容器

2.3.1 ApplicationContext
  • 它已经不仅仅是BeanFactory了
public interface ApplicationContext 
			extends EnvironmentCapable, // 容器启动相关配置
					ListableBeanFactory, 
					HierarchicalBeanFactory,
					MessageSource, // 管理Message,实现国际化
					ApplicationEventPublisher, // 具备监听事件发布的能力
					ResourcePatternResolver // 可以加载资源文件
2.3.2 ApplicationContext常用容器

传统的基于xml配置的经典容器

  • FileSystemXmlApplicationContext: 从文件系统加载配置
  • ClassPathXmlApplicationContext: 从classpath加载配置
  • XmlWebApplicationContext: 用于web应用程序的容器

目前比较流行的容器

  • AnnotationConfigServletWebServerApplicationContext
  • AnnotationConfigReactiveWebServerApplicationContext
  • AnnotationConfigApplicationContext

它们的共性

  • 都会执行refresh()方法进行容器初始化
2.3.3 二级接口 - ConfigurableApplicationContext
  • ApplicationContext接口只提供了get方法,因此它肯定需要子类进行扩展
  • ConfigurableApplicationContext
    public interface ConfigurableApplicationContext 
    				extends ApplicationContext, 
    						Lifecycle, // 对生命周期的管理
    						Closeable  // 关闭容器的时候释放相关资源
    // 容器刷新
    void refresh() throws BeansException, IllegalStateException;
    
2.3.4 AbstractApplicationContext - 重要实现类
  • 实现了Application接口中简单不易动的部分,主要是容器工厂的处理,例如getBean(),refresh(),注册监听器等等
    public abstract class AbstractApplicationContext extends DefaultResourceLoader
    		implements ConfigurableApplicationContext {}
    
  • 这种设计模式其实就是模板方法模式,refresh()方法就是一个模板方法

3 Resource、ResourceLoader、容器之间的微妙关系

3.1 Resource

在这里插入图片描述

3.1.1 简介
  • EncodedResource
    • 实现对资源文件的编码处理
  • AbstractResource
    • 提供Resource接口的大部分方法的默认实现
    • 如果自定Resource,推荐继承AbstractResource
  • ClassPathResource & FileSystemResource
    public static void main(String[] args) throws IOException {
    	FileSystemResource fileSystemResource = new FileSystemResource(
    			"/Users/baidu/imooc/springframework/spring-framework-5.2.0.RELEASE/springdemo/src/main/java/com/imooc/demo/resource/test.txt"
    	);
    	File file = fileSystemResource.getFile();
    	System.out.println(file.length());
    	OutputStream outputStream =  fileSystemResource.getOutputStream();
    	BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
    	bufferedWriter.write("Hello World");
    	bufferedWriter.flush();
    	outputStream.close();
    	bufferedWriter.close();
    }
    
3.1.2 Resource的选择
  • spring能更具资源地址自动选自额正确的Resource
    • 自动是被“classpath:”、“file:”等资源地址前缀
    • 支持自动解析Ant风格代通配符的资源地址
      • Ant:路径匹配表达式,用来对URI进行匹配

3.2 ResourceLoader

  • ResourceLoader实现不同的Resource加载策略,按需返回特定类型的Resource
    在这里插入图片描述
3.1 DefaultResourceLoader - 重要实现类
  • 重点在于getResource()方法,它是获取Resource的具体实现类实例

    //获取Resource的具体实现类实例
    @Override
    public Resource getResource(String location) {
    	Assert.notNull(location, "Location must not be null");
    	//ProtocolResolver ,用户自定义协议资源解决策略
    	for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
    		Resource resource = protocolResolver.resolve(location, this);
    		if (resource != null) {
    			return resource;
    		}
    	}
    	//如果是以/开头,则构造ClassPathContextResource返回
    	if (location.startsWith("/")) {
    		return getResourceByPath(location);
    	}
    	//若以 classpath: 开头,则构造 ClassPathResource 类型资源并返回,在构造该资源时,
    	// 通过 getClassLoader()获取当前的 ClassLoader
    	else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
    		return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    	}
    	else {
    		//构造 URL ,尝试通过它进行资源定位,若没有抛出 MalformedURLException 异常,
    		// 则判断是否为 FileURL , 如果是则构造 FileUrlResource 类型资源,否则构造 UrlResource。
    		// 若在加载过程中抛出 MalformedURLException 异常,
    		// 则委派 getResourceByPath() 实现资源定位加载
    		try {
    			// Try to parse the location as a URL...
    			URL url = new URL(location);
    			return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
    		}
    		catch (MalformedURLException ex) {
    			// No URL -> resolve as resource path.
    			return getResourceByPath(location);
    		}
    	}
    }
    
3.2 PathMatchingResourcePatternResolver
  • PathMatchingResourcePatternResolver 支持路径匹配获取ResourceLoader
    • 支持 "classpath*: " 和 Ant风格
3.3 ApplicationContext
  • 可以看到ApplicationContext继承了ResourcePatternResolver
  • 其子类AbstractApplicationContext也实现了ResourcePatternResolver接口,并且还继承了DefaultREsourceLoader,这也是高级容器为什么支持统一资源加载的原因

4 BeanDefinationReader

在这里插入图片描述

4.1 BeanDefinationReader

  • BeanDefinationReader就是ResourceLoader的使用者

  • BeanDefinationReader作用

    • 读取BeanDefination
    • 借助BeanDefinationRegistry注册器接口接口注册进容器
  • 它定义了针对 单个或多个Resource资源实例 以及 单个或多个配置文件的资源加载,最终目的就是将配置文件转换为一个个的BeanDefination

    
    // 获取BeanDefinitionRegistry对象,这个类的主要作用将BeanDefinition注册到BeanDefinition的注册表中
    BeanDefinitionRegistry getRegistry();
    
    @Nullable
    ResourceLoader getResourceLoader();
    
    @Nullable
    ClassLoader getBeanClassLoader();
    
    // Bean的名字生成器,为匿名bean生成一个名字,就是id
    BeanNameGenerator getBeanNameGenerator();
    
    // 加载单个配置文件
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
    
    // 加载多个配置文件
    int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
    
    // 加载单个配置文件
    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
    
    // 加载多个配置文件
    int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
    

4.2 AbstractBeanDefinitionReader

  • 实现了BeanDefinitionReader中的公共处理逻辑
  • 重点关注实现的loadBeanDefinitions方法
    • 根据用户提供的资源加载器类型类来判断是加载多个资源还是加载单个资源
    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    	//获取资源加载器,主要的功能就是根据路径和类加载器获取Resource对象
    	ResourceLoader resourceLoader = getResourceLoader();
    	//判断资源加载器是否为空
    	if (resourceLoader == null) {
    		throw new BeanDefinitionStoreException(
    				"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
    	}
    	//ResourcePatternResolver 用于加载多个文件或者能够加载Ant风格路径的文件资源
    	if (resourceLoader instanceof ResourcePatternResolver) {
    		// Resource pattern matching available.
    		try {
    			Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
    			int count = loadBeanDefinitions(resources);
    			if (actualResources != null) {
    				Collections.addAll(actualResources, resources);
    			}
    			if (logger.isTraceEnabled()) {
    				logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
    			}
    			return count;
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException(
    					"Could not resolve bean definition resource pattern [" + location + "]", ex);
    		}
    	}
    	else {
    		//加载单个文件资源
    		// 直接使用ResouceLoader加载
    		Resource resource = resourceLoader.getResource(location);
    		int count = loadBeanDefinitions(resource);
    		if (actualResources != null) {
    			actualResources.add(resource);
    		}
    		if (logger.isTraceEnabled()) {
    			logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
    		}
    		return count;
    	}
    }
    

4.3 XmlBeanDefinationReader - 重要

  • 断点设置
    在这里插入图片描述
4.3.1 通过Document对象解析出BeanDefination
  • 执行到XmlPathDefinationReader

    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    	return loadBeanDefinitions(new EncodedResource(resource));
    }
    
    • 我们看到这里直接传入了Resource对象,而不是配置文件路径,其实这是由其父类AnstractBeanDefinationReade调用它的loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)方法r将路径转为Resource对象了
  • 调用同类方法doLoadBeanDefinitions(inputSource, encodedResource.getResource());进一步解析

    • 创建Document对象,XML的文档对象,就是dom树,解析xml配置文件
    • 解析dom树,即解析出一个个属性,将其保存到BeanDefinition当中
    //创建Document对象,XML的文档对象,就是dom树
    // 使用这个Document可以获取XML文件中的节点并且创建节点
    // SAX XML
    Document doc = doLoadDocument(inputSource, resource);
    //解析dom树,即解析出一个个属性,将其保存到BeanDefinition当中
    //并向容器注册BeanDefinition
    int count = registerBeanDefinitions(doc, resource);
    
  • 注册BeanDefination: registerBeanDefinitions(Document doc, Resource resource)

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    	//创建BeanDefinitionDocumentReader,这个是实际从XML的DOM树中读取BeanDefiniton
    	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    	//获取注册表beanDefinitionMap的在本次加载前的BeanDefinition数量
    	int countBefore = getRegistry().getBeanDefinitionCount();
    	//加载并注册
    	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    	//本次加载注册后容器里BeanDefinition的数量减去先前的,即本次加载的BeanDefinition数量
    	return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    
  • 最终进入DefaultBeanDefinitionDocumentReader执行具体的加载注册逻辑doRegisterBeanDefinitions(Element root)

  • 调用本类方法parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)进一步处理

    • 该方法对每个标签进行遍历
  • 调用本类方法parseDefaultElement进一步处理,判断各种标签类型

  • 调用本类方法processBeanDefinition进一步处理

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    	// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
    	//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
    	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    	if (bdHolder != null) {
    		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    		try {
    			//向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
    			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    		}
    		catch (BeanDefinitionStoreException ex) {
    			getReaderContext().error("Failed to register bean definition with name '" +
    					bdHolder.getBeanName() + "'", ele, ex);
    		}
    		// 在完成BeanDefinition注册之后,往容器发送注册完成的事件
    		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    	}
    }
    
  • 调用BeanDefinitionParserDelegate中的parseBeanDefinitionElement方法最终解析出BeanDefination

    //根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition
    //为载入Bean定义信息做准备
    AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
  • 调用BeanDefinitionReaderUtils最终创建

    protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
    			throws ClassNotFoundException {
    
    	return BeanDefinitionReaderUtils.createBeanDefinition(
    			parentName, className, this.readerContext.getBeanClassLoader());
    }
    
4.3.2 注册BeanDefination
  • DefaultBeanDefinitionDocumentReader中的processBeanDefinition方法中调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());进行BeanDefination的创建

    //向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
    // 参数:BeanDefiantion的包装类
    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    
  • 方法分析

    public static void registerBeanDefinition(
    		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    		throws BeanDefinitionStoreException {
    
    	// 将beandefinition及其名字注册到容器里
    	String beanName = definitionHolder.getBeanName();
    	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
    	// 如果存在别名则逐个注册进容器
    	String[] aliases = definitionHolder.getAliases();
    	if (aliases != null) {
    		for (String alias : aliases) {
    			registry.registerAlias(beanName, alias);
    		}
    	}
    }
    
  • 该方法最终执行DefaultListableBeanFactory的registerBeanDefinition()方法对BeanDefination进行注册

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		//看看beanName是否已经存在容器里,存在则表明已经被注册过
		if (existingDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			// 如果允许覆盖,就覆盖旧值
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			// 判断Bean是否开始创建
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}
		//检查是否有同名的BeanDefinition已经在IOC容器中注册
		if (existingDefinition != null || containsSingleton(beanName)) {
			//尝试重置所有已经注册过的BeanDefinition的缓存,包括BeanDefinition
			//的父类以及合并的beanDefinition的缓存,所谓的合并BeanDefinition
			//指的的有parent属性的beandefinition,该BeanDefinition会把parent的
			//BeanDefinition属性合并在一块
			resetBeanDefinition(beanName);
		}
	}

4.4 BeanDefinationRegistry接口

public interface BeanDefinitionRegistry extends AliasRegistry {

	 // 往注册表中注册一个新的 BeanDefinition 实例
	 void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;

	 // 移除注册表中已注册的 BeanDefinition 实例
	 void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	 // 从注册中取得指定的 BeanDefinition 实例
	 BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

	 // 判断 BeanDefinition 实例是否在注册表中(是否注册)
	 boolean containsBeanDefinition(String beanName);

	// 取得注册表中所有 BeanDefinition 实例的 beanName(标识
	String[] getBeanDefinitionNames();

	 // 返回注册表中 BeanDefinition 实例的数量
	 int getBeanDefinitionCount();


 	// beanName(标识)是否被占用
	boolean isBeanNameInUse(String beanName);

}

5 xml配置的资源定位、加载、解析、注册链路分析

1 断点设置

  • 断点设置 - DefaultListableBeanFactory 的registerBeanDefination方法
    在这里插入图片描述

2 栈针执行流程分析

  • 1、程序入口

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring/spring-config.xml");
    
  • 2、调用构造方法

    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
    	this(new String[] {configLocation}, true, null);
    }
    
    • 真正干活的构造函数
      public ClassPathXmlApplicationContext(
      		String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      		throws BeansException {
      
      	super(parent);
      	// configLocations是一个数组,那么就用PathMatchingResourcePatternResolver进行加载
      	// 因为它支持加载多个配置文件
      	setConfigLocations(configLocations);
      	if (refresh) {
      		// 调用AbstractApplicationContext的refresh()方法进行容器初始化
      		refresh();
      	}
      }
      
  • 3、调用AbstractApplicationContext的refresh()方法进行容器初始化

    //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
    //子类的refreshBeanFactory()方法启动,里面有抽象方法
    //针对xml配置,最终创建内部容器,该容器负责 Bean 的创建与管理,此步会进行BeanDefinition的注册
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
  • 5、在refresh()方法中调用obtainFreshBeanFactory()方法

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    	refreshBeanFactory();
    	return getBeanFactory();
    }
    
  • 6、在obtainFreshBeanFactory()方法中调用了refreshBeanFactory() ,它是AbstractRefreshableApplicationContext中的方法

    //创建DefaultListableBeanFactory容器
    DefaultListableBeanFactory beanFactory = createBeanFactory();
    beanFactory.setSerializationId(getId());
    // 对容器进行定制化,默认主要设置是否允许BeanDefinition的重复注册,
    // Bean之间是否允许循环引用等,此外还可以设置启动参数,开启注解的自动装配等
    customizeBeanFactory(beanFactory);
    //加载BeanDefinition,for xml,使用了委派模式
    loadBeanDefinitions(beanFactory);
    
  • 7、refreshBeanFactory()方法中调用了AbstractXmlApplicationContext中的loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法

    • 创建了一个XmlBeanDefintaionReader
    • 并进一步调用本类中的loadBeanDefinitions(XmlBeanDefinitionReader reader)方法
  • 8、loadBeanDefinitions(XmlBeanDefinitionReader reader)调用本类方法loadBeanDefinitions(String… locations)进行进一步加载,根据locations逐个加载

    @Override
    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    	Assert.notNull(locations, "Location array must not be null");
    	int count = 0;
    	for (String location : locations) {
    		count += loadBeanDefinitions(location);
    	}
    	return count;
    }
    
  • 9、逐个加载流程,调用本类方法loadBeanDefinitions(String location)

    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    	return loadBeanDefinitions(location, null);
    }
    
  • 10、进一步调用loadBeanDefinitions(String location, @Nullable Set actualResources)

    • 发现使用ResourcePatternResolver来加载多个资源
  • 11、往前两步,调用了**XmlBeanDefinationReader**的loadBeanDefinitions(Resource resource)方法

    • 使用了EncodedResource来包装资源实例
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    	return loadBeanDefinitions(new EncodedResource(resource));
    }
    
  • 12、继续调用同类方法loadBeanDefinitions(EncodedResource encodedResource)

    //调用同类的方法继续解析
    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    
  • 13、继续调用同类方法doLoadBeanDefinitions(InputSource inputSource, Resource resource)进行解析

    //创建Document对象,XML的文档对象,就是dom树
    // 使用这个Document可以获取XML文件中的节点并且创建节点
    // SAX XML
    Document doc = doLoadDocument(inputSource, resource);
    //解析dom树,即解析出一个个属性,将其保存到BeanDefinition当中
    //并向容器注册BeanDefinition
    int count = registerBeanDefinitions(doc, resource);
    
  • 14、调用DefaultBeanDefinitionDocumentReader的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    	this.readerContext = readerContext;
    	doRegisterBeanDefinitions(doc.getDocumentElement());
    }
    
  • 15、进一步调用同类方法 doRegisterBeanDefinitions(Element root)

    //调用这个方法,解析
    parseBeanDefinitions(root, this.delegate);
    
  • 16、进一步调用同类方法parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)

    • 会进行递归解析Document中的标签
    • 这里是真正解析出BeanDefination实例的地方
    //若是则按照spring原有的逻辑进行解析
    parseDefaultElement(ele, delegate);
    
  • 17、调用同类方法processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)进行处理

    • 这里是向容器注册BeanDefination的入口
    //向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
    // 参数:BeanDefiantion的包装类
    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    
  • 18、调用BeanDefinitionReaderUtils中的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)进行处理

    // 将beandefinition及其名字注册到容器里
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
  • 19、调用DefaultListableBeanFactory中的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)进行最终注册

    • 如果已经注册过BeanDefination了,就会清除先前的BeanDefination,如果Bean是单例的,也会清除先前创建出来的Bean实例

6 注解配置的资源定位、加载、解析、注册链路分析

6.1 断点设置

  • 断点设置 - DefaultListableBeanFactory 的registerBeanDefination方法
    在这里插入图片描述

6.2 栈帧执行流程分析

  • 1、调用AnnotationConfigApplicationContext的构造函数
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
	// 调用默认无参构造器,主要初始化AnnotatedBeanDefinitionReader
	// 以及路径扫描器ClassPathBeanDefinitionScanner
	this();
	//把传入的Class进行注册,Class既可以有@Configuration注解,也可以没有@Configuration注解
	//如何注册委托给了 org.springframework.context.annotation.AnnotatedBeanDefinitionReader.register 方法进行注册
	// 包装传入的Class 生成 BeanDefinition , 注册到BeanDefinitionRegistry
	register(componentClasses);
	refresh();
}
  • 2、真正调用的是无参构造

    public AnnotationConfigApplicationContext() {
    	this.reader = new AnnotatedBeanDefinitionReader(this);
    	this.scanner = new ClassPathBeanDefinitionScanner(this);
    }
    
    • 可以看到this中包含一个DefaultListableBeanFactory,在ClassPathXmlApplicationContext中是在refresh中才创建DefaultListableBeanFactory
      • 这是因为系统中有一些内置的BeanDefination,需要提前提供注册的容器
        在这里插入图片描述
    • 而DefaultListableBeanFactory中的registerBeanDefinition()方法会被循环调用因为要先注册系统的beanDefination
  • 多循环调用几次就会开始处理注解标注的Bean,可以自己看

    • @Configuration在构造函数中调用register(componentClasses)处理
    • @Autowired等注解在refresh()刷新容器的时候处理

7 总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熠熠98

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值