手写Spring:第6章-资源加载器解析文件注册对象
文章目录
- 一、目标:资源加载器解析文件注册对象
- 二、设计:资源加载器解析文件注册对象
- 三、实现:资源加载器解析文件注册对象
- 3.1 工程结构
- 3.2 资源加载器解析文件注册对象类图
- 3.3 类工具类
- 3.4 资源加载接口定义和实现
- 3.4.1 定义资源加载接口
- 3.4.2 类路径资源实现接口
- 3.4.3 文件资源实现类
- 3.4.4 URL资源实现类
- 3.5 包装资源加载器
- 3.5.1 资源加载器接口
- 3.5.2 默认实现的资源加载器
- 3.6 Bean定义读取接口及实现类
- 3.6.1 定义Bean读取接口
- 3.6.2 定义Bean读取抽象类
- 3.6.3 解析XML处理Bean注册
- 3.7 Bean工厂接口完善
- 3.7.1 Bean工厂接口
- 3.7.2 扩展Bean工厂子接口
- 3.7.3 扩展Bean工厂的层次子接口
- 3.7.4 自动化处理Bean工厂配置接口
- 3.7.5 Bean工厂配置化接口
- 3.7.6 Bean工厂预先实例化的操作接口
- 3.8 Bean接口及其实现类完善
- 3.8.1 Bean工厂抽象类
- 3.8.2 修改Bean注册接口
- 3.8.3 默认的Bean工厂实现类
- 四、测试:资源加载器解析文件注册对象
- 4.1 添加配置文件
- 4.1.1 properties配置文件
- 4.1.2 xml配置文件
- 4.2 单元测试
- 4.2.1 xml配置测试
- 4.2.2 加载类路径测试
- 4.2.3 加载文件路径测试
- 4.2.4 加载URL路径测试
- 五、总结:资源加载器解析文件注册对象
一、目标:资源加载器解析文件注册对象
💡 如何通过 Spring 配置文件的方式将 Bean 对象实例化?
- 现在是通过单元测试手动操作 Beab 对象的定义、注册和属性填充,以及最终获取对象调用方法。
- 但是这里有个问题?实际使用 Spring 框架,是不太可能让用户通过手动方式创建的,而是最好能通过配置文件的方式简化创建过程。

- 如图中所示:把 2、3、4 整合到 Spring 框架中,通过 Spring 配置文件的方式将 Bean 对象实例化。
- 接下来就需要在现有的 Spring 框架中,添加能解决 Spring 配置的读取、解析、注册 Bean 操作。
二、设计:资源加载器解析文件注册对象
💡 技术设计:资源加载器解析文件注册对象
- 需要在现有的 Spring 框架雏形中添加一个资源解析器,也就是能读取
classpath、本地文件和云文件的配置内容。- 这些配置内容就是像使用 Spring 时配置的
spring.xml一样,里面会包括 Bean 对象的描述和属性信息。 - 在读取配置文件信息后,接下来就是对配置文件中的 Bean 描述信息解析后进行注册操作,把 Bean 对象注册到 Spring 容器中。
- 这些配置内容就是像使用 Spring 时配置的

- 资源加载器属于相对独立的部分,它位于 Spring 框架核心包下的
I/O实现内容,主要用于处理 Class、本地和云环境的文件信息。 - 当资源可以加载后,接下来就是解析和注册 Bean 到 Spring 中的操作,这部分实现需要和 DefaultListableBeanFactory 核心类结合起来。
- 因为你所有的解析后的注册动作,都会把 Bean 定义信息放入到这个类中。
- 在实现时就设计好接口的实现层级关系,包括我们需要定义出 Bean 定义的读取接口
BeanDefinitionReader以及做好对应的实现类,在实现类中完成对 Bean 对象的解析和注册。
三、实现:资源加载器解析文件注册对象
3.1 工程结构
spring-step-05
|-src|-main| |-java| |-com.lino.springframework| |-beans| | |-factory| | | |-config| | | | |-AutowireCapableBeanFactory.java| | | | |-BeanDefinition.java| | | | |-BeanReference.java| | | | |-ConfigurableBeanFactory.java| | | | |-SingletonBeanRegistry.java| | | |-support| | | | |-AbstractAutowireCapableBeanFactory.java| | | | |-AbstractBeabDefinitionReader.java| | | | |-AbstractBeabFactory.java| | | | |-BeabDefinitionReader.java| | | | |-BeanDefinitionRegistry.java| | | | |-CglibSubclassingInstantiationStrategy.java| | | | |-DefaultListableBeanFactory.java| | | | |-DefaultSingletonBeanRegistry.java| | | | |-InstantiationStrategy.java| | | | |-SimpleInstantiationStrategy.java| | | |-support| | | | |-XMLBeanDefinitionReader.java| | | |-BeanFactory.java| | | |-ConfigurableListableBeanFactory.java| | | |-HierarcgicalBeanFactory.java| | | |-ListableBeanFactory.java| | |-BeansException.java| | |-PropertyValue.java| | |-PropertyValues.java| |-core.io| | |-ClassPathResource.java| | |-DefaultResourceLoader.java| | |-FileSystemResource.java| | |-Resource.java| | |-ResourceLoader.java| | |-UrlResource.java| |-util| | |-ClassUtils.java|-test|-java|-com.lino.springframework.test|-bean| |-UserDao.java| |-UserService.java|-ApiTest.java|-resources|-important.properties|-spring.xml
3.2 资源加载器解析文件注册对象类图

- 为了能把 Bean 的定义、注册和初始化交给
spring.xml配置化处理,那么就需要实现两大块内容,分别是:资源加载器、xml资源处理类。- 实现过程主要以对接口
Resource、ReourceLoader的实现。 - 而另外
BeanDefinitionReader接口则是对资源的具体使用,将配置信息注册到 Spring 容器中去。
- 实现过程主要以对接口
- 在
Resource的资源加载器的实现中包括了:ClassPath、系统文件、云配置文件,这三部分与 Spring 源码中的设计和实现保持一致。- 最终在
DefaultResourceLoader中做具体的调用。
- 最终在
- 接口:
BeanDefinitionReader,实现类:AbstractBeanDefinitionReader,实现类:XMLBeanDefinitionReader。- 这三部分内容主要是合理清晰的处理了资源读取后的注册 Bean 容器操作。
- 接口管定义、抽象类处理非接口功能外的注册 Bean 组件填充,最终实现类即可只关心具体的业务实现。

BeanFactory:已经存在的 Bean 工厂接口用于获取 Bean 对象。- 这次新增加了按照类型获取 Bean 的方法:
<T> T getBean(String name, Class<T> requiredType)
- 这次新增加了按照类型获取 Bean 的方法:
ListableBeanFactory:是一个扩展 Bean 工厂接口的接口,新增加了getBeansOfType、getBeanDefinitionNames方法。HierarchicalBeanFactory:在 Spring 源码中它提供了可以获取父类 BeanFactory 方法,属于是一种扩展工厂的层次子接口AutowireCapableBeanFactory:是一个自动化处理 Bean 工厂配置的接口。ConfigurableBeanFactory:可获取 BeanPostProcessor、BeanClassLoader 等的一个配置化接口ConfigurableListableBeanFactory:提供分析和修改 Bean 以及预先实例化的操作接口。先在只有getBeanDefinition方法。
3.3 类工具类
ClassUtils
package com.lino.springframework.util;/*** @description: 类工具类*/
public class ClassUtils {/*** 获取默认类加载器* @return 类加载器*/public static ClassLoader getDefaultClassLoader() {ClassLoader cl = null;try {cl = Thread.currentThread().getContextClassLoader();} catch (Throwable ex) {// Cannot access thread context ClassLoader - falling back to system class loader...}if (cl == null) {// No thread context class loader -> use class loader of this class.cl = ClassUtils.class.getClassLoader();}return cl;}
}
3.4 资源加载接口定义和实现
3.4.1 定义资源加载接口
Resource.java
package com.lino.springframework.core.io;import java.io.IOException;
import java.io.InputStream;/*** @description: 资源处理接口*/
public interface Resource {/*** 加载资源流** @return 输入流* @throws IOException IO异常*/InputStream getInputStream() throws IOException;
}
- 在 Spring 框架下创建
core.io核心包,在这个包中主要用于处理资源加载流。 - 定义 Resource 接口,提供获取
InputStream流的方法,接下来再分别实现三种不同的流文件操作:classPath、FileSystem、URL
3.4.2 类路径资源实现接口
ClassPathResource.java
package com.lino.springframework.core.io;import cn.hutool.core.lang.Assert;
import com.lino.springframework.util.ClassUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;/*** @description: 类路径资源*/
public class ClassPathResource implements Resource {private final String path;private ClassLoader classLoader;public ClassPathResource(String path) {this(path, (ClassLoader) null);}public ClassPathResource(String path, ClassLoader classLoader) {Assert.notNull(path, "Path must not be null");this.path = path;this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());}@Overridepublic InputStream getInputStream() throws IOException {InputStream is = classLoader.getResourceAsStream(path);if (is == null) {throw new FileNotFoundException(this.path + " cannot be opened because it does not exist");}return is;}
}
- 这部分的实现是用于通过 ClassLoader 读取
ClassPath下的文件信息,具体的读取过程:classLoader.getResourceAsStream(path)。
3.4.3 文件资源实现类
FileSystemResource.java
package com.lino.springframework.core.io;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;/*** @description: 文件资源*/
public class FileSystemResource implements Resource {private final File file;private final String path;public FileSystemResource(File file) {this.file = file;this.path = file.getPath();}public FileSystemResource(String path) {this.file = new File(path);this.path = path;}@Overridepublic InputStream getInputStream() throws IOException {return new FileInputStream(this.file);}public final String getPath() {return this.path;}
}
- 通过指定文件路径的方式读取文件信息。
3.4.4 URL资源实现类
UrlResource.java
package com.lino.springframework.core.io;import cn.hutool.core.lang.Assert;import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;/*** @description: URL 资源*/
public class UrlResource implements Resource {private final URL url;public UrlResource(URL url) {Assert.notNull(url, "URL must not be null");this.url = url;}@Overridepublic InputStream getInputStream() throws IOException {URLConnection con = this.url.openConnection();try {return con.getInputStream();} catch (IOException ex) {if (con instanceof HttpURLConnection) {((HttpURLConnection) con).disconnect();}throw ex;}}
}
- 通过 HTTP 的方式读取云服务的文件。
3.5 包装资源加载器
💡 按照资源加载的不同方式,资源加载器可以把这些方式集中到统一的类服务下进行处理,外部用户只需要传递资源地址即可,简化使用。
3.5.1 资源加载器接口
ResourceLoader.java
package com.lino.springframework.core.io;/*** @description: 资源加载器*/
public interface ResourceLoader {/*** Pseudo URL prefix for loading from the class path: "classpath:"*/String CLASSPATH_URL_OREFIX = "classpath:";/*** 获取资源** @param location 资源名称* @return 资源*/Resource getResource(String location);
}
- 定义获取资源接口,里面传递
location地址即可。
3.5.2 默认实现的资源加载器
DefaultResourceLoader.java
package com.lino.springframework.core.io;import cn.hutool.core.lang.Assert;
import java.net.MalformedURLException;
import java.net.URL;/*** @description: 默认实现的资源处理器*/
public class DefaultResourceLoader implements ResourceLoader {@Overridepublic Resource getResource(String location) {Assert.notNull(location, "Location must not be null");if (location.startsWith(CLASSPATH_URL_OREFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_OREFIX.length()));} else {try {URL url = new URL(location);return new UrlResource(url);} catch (MalformedURLException e) {return new FileSystemResource(location);}}}
}
- 在获取资源的实现中,主要把三种不同类型的资源处理方式进行了包装,分为:判断是否为
ClassPath、URL、文件。 - 虽然 DefaultResourceLoader 类实现的过程简单,但这也是设计模式约定的具体结果,像是这里不会让外部调用方知道太多的细节,而是仅关心具体调用结果即可。
3.6 Bean定义读取接口及实现类
3.6.1 定义Bean读取接口
BeanDefinitionReader.java
package com.lino.springframework.beans.factory.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.core.io.ResourceLoader;/*** @description: Bean定义读取接口*/
public interface BeanDefinitionReader {/*** 获取bean对象注册对象** @return bean对象注册对象*/BeanDefinitionRegistry getRegistry();/*** 获取资源加载器** @return 资源加载器*/ResourceLoader getResourceLoader();/*** 加载bean定义方法** @param resource 资源* @throws BeansException bean异常*/void loadBeanDefinitions(Resource resource) throws BeansException;/*** 加载bean定义方法** @param resources 资源列表* @throws BeansException bean异常*/void loadBeanDefinitions(Resource... resources) throws BeansException;/*** 加载bean定义方法** @param location 路径名称* @throws BeansException bean异常*/void loadBeanDefinitions(String location) throws BeansException;
}
- 这是一个简单 Bean 读取接口,这个接口里面定义了几个方法,包括:
getRegistry、getResourceLoader,以及三个加载 Bean 定义的方法。 - 这里需要注意
getRegistry、getResourceLoader,都是用于提供给后面三个方法的工具,加载和注册。- 这两个方法的实现会包装到抽象类中,以免污染具体的接口实现方法。
3.6.2 定义Bean读取抽象类
AbstractBeanDefinitionReader.java
package com.lino.springframework.beans.factory.support;import com.lino.springframework.core.io.DefaultResourceLoader;
import com.lino.springframework.core.io.ResourceLoader;/*** @description: Bean定义读取抽象类*/
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {private final BeanDefinitionRegistry registry;private ResourceLoader resourceLoader;protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {this(registry, new DefaultResourceLoader());}public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {this.registry = registry;this.resourceLoader = resourceLoader;}@Overridepublic BeanDefinitionRegistry getRegistry() {return registry;}@Overridepublic ResourceLoader getResourceLoader() {return resourceLoader;}
}
- 抽象类把 BeanDefinitionReader 接口的前两个方法全部实现了,并提供了构造函数,让外部的调用使用方,把 Bean 定义注入类,传递进来。
- 这样在接口 BeanDefinitionReader 的具体实现类中,就可以把解析后的 XML 文件中的 Bean 信息,注册到 Spring 容器去了。
3.6.3 解析XML处理Bean注册
XMLBeanDefinitionReader.java
package com.lino.springframework.beans.factory.xml;import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanReference;
import com.lino.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.lino.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.core.io.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.IOException;
import java.io.InputStream;/*** @description: XML处理Bean注册*/
public class XMLBeanDefinitionReader extends AbstractBeanDefinitionReader {public XMLBeanDefinitionReader(BeanDefinitionRegistry registry) {super(registry);}public XMLBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {super(registry, resourceLoader);}@Overridepublic void loadBeanDefinitions(Resource resource) throws BeansException {try {try (InputStream inputStream = resource.getInputStream()) {doLoadBeanDefinitions(inputStream);}} catch (IOException | ClassNotFoundException e) {throw new BeansException("IOException parsing XML document from " + resource, e);}}@Overridepublic void loadBeanDefinitions(Resource... resources) throws BeansException {for (Resource resource : resources) {loadBeanDefinitions(resource);}}@Overridepublic void loadBeanDefinitions(String location) throws BeansException {ResourceLoader resourceLoader = getResourceLoader();Resource resource = resourceLoader.getResource(location);loadBeanDefinitions(resource);}protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {Document doc = XmlUtil.readXML(inputStream);Element root = doc.getDocumentElement();NodeList childNodes = root.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {// 判断元素if (!(childNodes.item(i) instanceof Element)) {continue;}// 判断对象if (!"bean".equals(childNodes.item(i).getNodeName())) {continue;}// 解析标签Element bean = (Element) childNodes.item(i);String id = bean.getAttribute("id");String name = bean.getAttribute("name");String className = bean.getAttribute("class");// 获取 Class, 方便获取类中的名称Class<?> clazz = Class.forName(className);// 优先级 id > nameString beanName = StrUtil.isNotEmpty(id) ? id : name;if (StrUtil.isEmpty(beanName)) {beanName = StrUtil.lowerFirst(clazz.getSimpleName());}// 定义beanBeanDefinition beanDefinition = new BeanDefinition(clazz);// 读取属性并填充for (int j = 0; j < bean.getChildNodes().getLength(); j++) {// 判断元素if (!(bean.getChildNodes().item(j) instanceof Element)) {continue;}// 判断对象if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) {continue;}// 解析标签:propertyElement property = (Element) bean.getChildNodes().item(j);String attrName = property.getAttribute("name");String attrValue = property.getAttribute("value");String attrRef = property.getAttribute("ref");// 获取属性值:引入对象、值对象Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;// 创建属性信息PropertyValue propertyValue = new PropertyValue(attrName, value);beanDefinition.getPropertyValues().addPropertyValue(propertyValue);}if (getRegistry().containsBeanDefinition(beanName)) {throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");}// 注册 BeanDefinitiongetRegistry().registerBeanDefinition(beanName, beanDefinition);}}
}
- XMLBeanDefinitionReader 类最核心的内容就是对 XML 文件的解析,把我们本来在代码中的操作放到了通过解析 XML 自动注册的方式。
loadBeanDefinitions方法,处理资源加载,这里新增加了一个内部方法:doLoadBeanDefinitions,它主要负责解析 XML。- 在
doLoadBeanDefinitions方法中,主要是对 XML 的读取:XmlUtil.readXML(inputStream)和元素 Element 解析。- 在解析的过程中通过循环操作,以此获取 Bean 配置以及配置中的
id、name、class、value、ref信息。
- 在解析的过程中通过循环操作,以此获取 Bean 配置以及配置中的
- 最终把读取出来的配置信息,创建成 BeanDefinition 以及 PropertyValue,最终把完整的 Bean 定义内容注册到 Bean 容器:
getRegistry().registerBeanDefinition(beanName, beanDefinition)
3.7 Bean工厂接口完善
3.7.1 Bean工厂接口
BeanFactory.java
package com.lino.springframework.beans.factory;import com.lino.springframework.beans.BeansException;/*** @description: 定义 Bean 工厂接口*/
public interface BeanFactory {/*** 返回 Bean 的实例对象** @param name 要检索的bean的名称* @return 实例化的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/Object getBean(String name) throws BeansException;/*** 返回含构造函数的 Bean 实例对象** @param name 要检索的bean的名称* @param args 构造函数入参* @return 实例化的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/Object getBean(String name, Object... args) throws BeansException;/*** 返回指定泛型的对象** @param name 要检索的bean的名称* @param requiredType 类型* @param <T> 泛型* @return 实例化的的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/<T> T getBean(String name, Class<T> requiredType) throws BeansException;
}
3.7.2 扩展Bean工厂子接口
ListableBeanFactory.java
package com.lino.springframework.beans.factory;import com.lino.springframework.beans.BeansException;import java.util.Map;/*** @description: Listable Bean 工厂子接口*/
public interface ListableBeanFactory extends BeanFactory {/*** 按照类型返回 Bean 实例** @param type 类型* @param <T> 泛型* @return 泛型Map* @throws BeansException Bean异常*/<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;/*** 返回注册表中所有的Bean名称** @return 注册表中所有的Bean名称*/String[] getBeanDefinitionNames();
}
3.7.3 扩展Bean工厂的层次子接口
HierarchicalBeanFactory.java
package com.lino.springframework.beans.factory;/*** @description: hierarchy bean工厂层次子接口*/
public interface HierarchicalBeanFactory extends BeanFactory {
}
3.7.4 自动化处理Bean工厂配置接口
AutowireCapableBeanFactory.java
package com.lino.springframework.beans.factory.config;import com.lino.springframework.beans.factory.BeanFactory;/*** @description: 自动化处理Bean工厂配置接口*/
public interface AutowireCapableBeanFactory extends BeanFactory {
}
3.7.5 Bean工厂配置化接口
ConfigurableBeanFactory.java
package com.lino.springframework.beans.factory.config;import com.lino.springframework.beans.factory.HierarchicalBeanFactory;/*** @description: 配置Bean工厂接口*/
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {String SCOPE_SINGLETON = "singleton";String SCOPE_PROTOTYPE = "prototype";
}
3.7.6 Bean工厂预先实例化的操作接口
ConfigurableListableBeanFactory.java
package com.lino.springframework.beans.factory;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.config.AutowireCapableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.ConfigurableBeanFactory;/*** @description: 配置列表 Bean工厂接口*/
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {/*** 根据对象名称获取bean对象** @param beanName 对象名称* @return bean对象* @throws BeansException bean异常*/BeanDefinition getBeanDefinition(String beanName) throws BeansException;
}
3.8 Bean接口及其实现类完善
3.8.1 Bean工厂抽象类
AbstractBeanFactory.java
package com.lino.springframework.beans.factory.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;/*** @description: 抽象的 Bean 工厂基类,定义模板方法*/
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null);}@Overridepublic Object getBean(String name, Object... args) throws BeansException {return doGetBean(name, args);}@Overridepublic <T> T getBean(String name, Class<T> requiredType) throws BeansException {return (T) getBean(name);}protected <T> T doGetBean(final String name, final Object[] args) {Object bean = getSingleton(name);if (bean != null) {return (T) bean;}BeanDefinition beanDefinition = getBeanDefinition(name);return (T) createBean(name, beanDefinition, args);}/*** 获取 Bean 对象** @param beanName 要检索的bean的名称* @return Bean 对象*/protected abstract BeanDefinition getBeanDefinition(String beanName);/*** 创建Bean对象** @param beanName 要检索的bean的名称* @param beanDefinition Bean对象* @param args 构造函数入参* @return 实例化的Bean对象*/protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args);
}
3.8.2 修改Bean注册接口
BeanDefinitionRegistry.java
package com.lino.springframework.beans.factory.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.config.BeanDefinition;/*** @description: Bean 定义注册接口*/
public interface BeanDefinitionRegistry {/*** 向注册表中注册 BeanDefinition** @param beanName Bean 名称* @param beanDefinition Bean 定义*/void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);/*** 使用Bean名称查询BeanDefinition** @param beanName bean名称* @return bean对象* @throws BeansException bean异常*/BeanDefinition getBeanDefinition(String beanName) throws BeansException;/*** 判断是否包含指定名称的BeanDefinition** @param beanName bean名称* @return 是否包含*/boolean containsBeanDefinition(String beanName);/*** 返回注册表中所有的Bean对象** @return Bean对象数组*/String[] getBeanDefinitionNames();
}
3.8.3 默认的Bean工厂实现类
DefaultListableBeanFactory.java
package com.lino.springframework.beans.factory.support;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.lino.springframework.beans.factory.config.BeanDefinition;import java.util.HashMap;
import java.util.Map;/*** @description: 默认的Bean工厂实现类*/
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry, ConfigurableListableBeanFactory {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();@Overridepublic BeanDefinition getBeanDefinition(String beanName) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition == null) {throw new BeansException("No bean named '" + beanName + "' is defined");}return beanDefinition;}@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {beanDefinitionMap.put(beanName, beanDefinition);}@Overridepublic boolean containsBeanDefinition(String beanName) {return beanDefinitionMap.containsKey(beanName);}@Overridepublic <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {Map<String, T> result = new HashMap<>(16);beanDefinitionMap.forEach((beanName, beanDefinition) -> {Class beanClass = beanDefinition.getBeanClass();if (type.isAssignableFrom(beanClass)) {result.put(beanName, (T) getBean(beanName));}});return result;}@Overridepublic String[] getBeanDefinitionNames() {return beanDefinitionMap.keySet().toArray(new String[0]);}
}
四、测试:资源加载器解析文件注册对象
4.1 添加配置文件
💡 这里的两份配置文件,一份用于测试资源加载器,另外
spring.xml用于测试整体的 Bean 注册功能。
4.1.1 properties配置文件
important.properties
# Config File
system.key=OLpj9823dZ
4.1.2 xml配置文件
spring.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans><bean id="userDao" class="com.lino.springframework.test.bean.UserDao"/><bean id="userService" class="com.lino.springframework.test.bean.UserService"><property name="uId" value="10001"/><property name="userDao" ref="userDao"/></bean>
</beans>
4.2 单元测试
4.2.1 xml配置测试
ApiTest.java
package com.lino.springframework.test;import cn.hutool.core.io.IoUtil;
import com.lino.springframework.beans.factory.support.DefaultListableBeanFactory;
import com.lino.springframework.beans.factory.xml.XMLBeanDefinitionReader;
import com.lino.springframework.core.io.DefaultResourceLoader;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.test.bean.UserService;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;/*** @description: 测试类*/
public class ApiTest {private DefaultResourceLoader resourceLoader;@Beforepublic void init() {resourceLoader = new DefaultResourceLoader();}@Testpublic void test_xml() {// 1.初始化 BeanFactoryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 2.读取配置文件&注册BeanXMLBeanDefinitionReader reader = new XMLBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions("classpath:spring.xml");// 3.获取Bean对象调用方法UserService userService = beanFactory.getBean("userService", UserService.class);String result = userService.queryUserInfo();System.out.println("测试结果:" + result);}
}
测试结果
查询用户信息: 张三
- 从测试结果来看,已经把注册 Bean 以及配置属性信息的内容,交给了
newXMLBeanDefinitionReader(beanFactory)类读取spring.xml的方式来处理,并通过了测试。
4.2.2 加载类路径测试
ApiTest.java
@Test
public void test_classpath() throws IOException {Resource resource = resourceLoader.getResource("classpath:important.properties");InputStream inputStream = resource.getInputStream();String content = IoUtil.readUtf8(inputStream);System.out.println(content);
}
测试结果
# Config File
system.key=OLpj9823dZ
4.2.3 加载文件路径测试
ApiTest.java
@Test
public void test_file() throws IOException {Resource resource = resourceLoader.getResource("src/test/resources/important.properties");InputStream inputStream = resource.getInputStream();String content = IoUtil.readUtf8(inputStream);System.out.println(content);
}
测试结果
# Config File
system.key=OLpj9823dZ
4.2.4 加载URL路径测试
ApiTest.java
@Test
public void test_url() throws IOException {Resource resource = resourceLoader.getResource("https://github.com/fuzhengwei/small-spring/important.properties");InputStream inputStream = resource.getInputStream();String content = IoUtil.readUtf8(inputStream);System.out.println(content);
}
测试结果
# Config File
system.key=OLpj9823dZ
五、总结:资源加载器解析文件注册对象
- 此时的工程结构,以配置文件为入口解析和注册 Bean 信息,最终再通过 Bean 工厂获取 Bean 以及做相应的调用操作。
相关文章:
手写Spring:第6章-资源加载器解析文件注册对象
文章目录 一、目标:资源加载器解析文件注册对象二、设计:资源加载器解析文件注册对象三、实现:资源加载器解析文件注册对象3.1 工程结构3.2 资源加载器解析文件注册对象类图3.3 类工具类3.4 资源加载接口定义和实现3.4.1 定义资源加载接口3.4…...
Redis 7 第八讲 集群模式(cluster)架构篇
集群架构 Redis 集群架构图 集群定义 Redis 集群是一个提供在多个Redis节点间共享数据的程序集;Redis集群可以支持多个master 应用场景 Redis集群支持多个master,每个master又可以挂载多个slave读写分离支持数据的高可用支持海量数据的读写存储操作集群自带Sentinel的故障…...
【PowerQuery】导入与加载XML
在标准数据格式类型里面,有一类比较特殊的数据类型,就是层次结构数据。层次结构数据和标准的结构型数据方式完全不同,在实际应用过程中使用最为频繁的几种数据类型如下。 XML数据格式Json 数据格式Yaml 数据格式我们将在本节和大家一起分享下XML格式数据集成,下一节和大家分…...
vue 预览视频
1.预览本地文件 1.1 直接给video或者embed的src赋值本地路径 <video :src"videoUrl"></video> // 或者 使用embed标签<embed :src"videoUrl" /> 1.2 读取文件流形式 <input type"file" ref"file" /> <vi…...
4个维度讲透ChatGPT技术原理,揭开ChatGPT神秘技术黑盒!(文末送书)
🤵♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞Ǵ…...
【无标题】@Scheduled 的cron
, :指定多个值。 -:表示一个区间。 / :指定一个值的增加幅度。n/m表示从n开始,每次增加m。 L:是last的缩写,表示最后一天,用在日表示一个月中的最后一天,用在周表示每周最后一天&…...
IP和MAC的作用区别
在 IP 地址的上一行是 link/ether fa:16:3e:c7:79:75 brd ff:ff:ff:ff:ff:ff,这个被称为 MAC 地址,是一个网卡的物理地址,用十六进制,6 个 byte 表示。 一个网络包要从一个地方传到另一个地方,除了要有确定的地址&…...
python趣味编程-数独游戏
数独游戏是一个用Python编程语言编写的应用程序。该项目包含可以显示实际应用程序的基本功能。该项目可以让修读 IT 相关课程并希望开发简单应用程序的学生受益。这个Python 数独游戏是一个简单的项目,可用于学习tkinter库的实践。这个数独游戏可以提供Python编程的基本编码技…...
MySQL/MariaDB 查询某个 / 多个字段重复数据
创建测试表和数据 # 创建表 create table if not exists t_duplicate (name varchar(255) not null,age int not null );# 插入测试数据 insert into t_duplicate(name, age) values(a, 1); insert into t_duplicate(name, age) values(a, 2);查询单个字段重复 使用 count() …...
【力扣每日一题】2023.9.10 课程表Ⅱ
目录 题目: 示例: 分析: 代码: 题目: 示例: 分析: 今天的题目和昨天类似,不过今天要我们求出学习所有课程的先后顺序。 昨天只需要我们求出能否学习完所有课程,因此…...
VSCODE CMAKE C++ 工程调试, C++不以科学计数法输出并控制小数位数
1. VSCODE调试CMAKE工程配置1.1 修改CMakeLists.txt文件1.2. 程序中1.3. launch.json配置1.4 开始调试1.5 注意 2. C设置输出浮点数且保留位数固定 1. VSCODE调试CMAKE工程配置 1.1 修改CMakeLists.txt文件 加这一句 set(CMAKE_BUILD_TYPE "Debug")1.2. 程序中 在…...
Drools规则引擎入门学习记录
业务开发过程中,对于某些判断性的通用规则是基于if-else封装,还是基于策略模式封装?无论以上那种封装出来的方法,只能在单体软件包中共用,且不能无感部署,然而对于业务而言,可能规则改变的比较频…...
肖sir__设计测试用例方法之判定表06_(黑盒测试)
设计测试用例方法之判定表 1、判定表:是一种表达逻辑判断的工具。 2、判定表:包含四部分 1)条件桩(condition stub):列出问题的 所有条件(通常条件次序无关紧要)。 2)条件项&#x…...
<图像处理> 空间滤波基础
空间滤波基础 图像滤波是一种常见的图像处理技术,用于平滑图像、去除噪音和边缘检测等任务。图像滤波的基本原理是在进行卷积操作时,通过把每个像素的值替换为该像素及其邻域的设定的函数值来修改图像。 预备知识:可分离滤波核、边缘填充。…...
如何在Django中使用django-crontab启动定时任务、关闭任务以及关闭指定任务
安装django-crontab包: pip install django-crontab 在Django项目的settings.py文件中,找到INSTALLED_APPS配置,并添加django_crontab到列表中: INSTALLED_APPS [ ... django_crontab,... ] 在settings.py文件的末尾,添加以下配置以设…...
mysql配置项整理
二、:mysql服务器参数 general 基础配置 datadir/var/lib/mysql #数据文件存放的目录 socket/var/lib/mysql/mysql.sock #mysql.socket表示server和client在同一台服务器,并且使用localhost进行连接,就会使用socket进行连接 pid_file/v…...
【KRouter】一个简单且轻量级的Kotlin Routing框架
【KRouter】一个简单且轻量级的Kotlin Routing框架 KRouter(Kotlin-Router)是一个简单而轻量级的Kotlin路由框架。 具体来说,KRouter是一个通过URI来发现接口实现类的框架。它的使用方式如下: val homeScreen KRouter.route&l…...
时间管理类书籍阅读笔记
背景 这段时间看了时间管理方面的书籍,大部分和早晨时间利用相关。之所以有了利用早晨时间的想法,是某天下班后,感觉很疲惫,什么都不想做,于是就打了一晚上游戏,然后第二天重复着这样的生活。 突然意识到…...
CSS文字居中对齐学习
CSS使用text-align属性设置文字对齐方式;text-align:center,这样就设置了文字居中对齐; <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>css 水平居中</title><style>.box …...
《论文阅读》CARE:通过条件图生成的共情回复因果关系推理 EMNLP 2022
《论文阅读》CARE:通过条件图生成的移情反应因果关系推理 前言简介基础知识TransformerVariational Graph Auto-Encoder 变分图自编码器`邻接矩阵(adjacency matrix)``图神经网络(GNN)``图卷积神经网络(GCN)``自编码器(Auto Encoder)``图自编码器(GAE)``变分图自编码…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...
