1. 什么是容器
大家在使用spring框架时,其实一直都在跟容器打交道,比如我们使用@Autowired注解时,就是从容器中获取到对应的对象的,也使用bean标签可以向容器中注册对象。其实我们可以把容器理解成一个“桶”,这个“桶”里面装的就是我需要使用的对象。
2. 带着疑问阅读
- 什么是容器?
- 如果构建spring容器?
- 什么是bean定义?
- bean注册的大体流程是怎么样的?
3.基础使用
3.1 构建一个基于xml的spring容器
创建一个需要交给容器管理java类
public class UserServiceImpl{
public String getName(){
return "普通用户->张三";
}
}
构建application-base.xml将其放在resource目录下,该xml中使用bean标签向容器中注册UserServiceImpl的定义对象(bean标签、如何注册、定义对象如何不清楚没关系,后面会一一解释的)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userServiceImpl" class="com.qiupeng.bean.base.service.Impl.UserServiceImpl" />
</beans>
定义一个测试函数
public class BeanBaseTest{
private static final Logger logger = LogManager.getLogger(BeanBaseTest.class);
public static void main(String[] arg){
ApplicationContext context = new ClassPathXmlApplicationContext(“classpath:application-base.xml”);
UserServiceImpl userServiceImpl = context.getBean("userServiceImpl",UserServiceImpl.class);
logger.info(userServiceImpl.getName());
Arrays.stream(context.getBeanDefinitionNames()).forEach(logger::info);
}
}
最终输出
[INFO ] 2023-06-15 14:25:41,090 com.qiupeng.bean.base.BeanBaseTest - 普通用户->张三
[INFO ] 2023-06-15 14:25:41,097 com.qiupeng.bean.base.BeanBaseTest - userServiceImpl
通过上述例子就已经完成一个容器的构建,已经实现了bean的注册和使用流程。
3.2 执行流程
通过创建一个 ClassPathXmlApplicationContext 对象并且传入一个xml,就能构建一个基于xml的容器对象了。我画了一个图帮助大家理解流程。
在图中可以看出,首先是一个“bean定义的生成工厂”,从xml中读取需要使用bean标签,然后在将其转为bean定义对象,在存入到容器中,我们可以使用以下个代码进行验证。
public class BeanBaseTest {
private static final Logger logger = LogManager.getLogger(BeanBaseTest.class);
public static void main(String[] arg){
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("classpath:application-base.xml");
context.refresh();
UserServiceImpl userServiceImpl = context.getBean("userServiceImpl",UserServiceImpl.class);
logger.info(userServiceImpl.getName());
Arrays.stream(context.getBeanDefinitionNames()).forEach(logger::info);
}
}
最终输出
[INFO ] 2023-06-15 14:25:41,090 com.qiupeng.bean.base.BeanBaseTest - 普通用户->张三
[INFO ] 2023-06-15 14:25:41,097 com.qiupeng.bean.base.BeanBaseTest - userServiceImpl
上述代码中创建了一个GenericApplicationContext 容器对象,再构建了一个XmlBeanDefinitionReader加载xml的“bean定义的生成工厂”,输出和上一个例子是样的。
4.总结
- 容器其实就是一个存储bean对象的“桶”。
- 可以使用ClassPathXmlApplicationContext或者GenericApplicationContext快速构建容器。(其实构建容器的方式还有很多后面会一一介绍)。
- bean定义其实是就是一个存储class的属性的一个中间对象,容器通过这个中间对象来生成对应对象实例。(在srping中BeanDefinition就是bean定义对象)。
- bean加载流程:原始数据(xml等)=> “bean定义的生成工厂”=>bean定义=>容器=>实例化
5. 扩展知识
通过上述文章的理解,容器必须基于xml吗?
其实很显然不是,我们可以自己来构建“bean定义的加工厂”来生成对应bean定义对象,接下来我们就来实现一个基于txt的容器。
构建一个bean定义生成工厂
public class TxtBeanDefinitionReader extends AbstractBeanDefinitionReader {
public MyBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//获取bean容器
BeanDefinitionRegistry registry = getRegistry();
//记录初始化beanDefinition数量
int count = 0;
String contentAsString = "";
try {
if (resource.isOpen()) {
contentAsString = resource.getContentAsString(StandardCharsets.UTF_8);
resource.getInputStream().close();
} else {
contentAsString = resource.getContentAsString(StandardCharsets.UTF_8);
}
} catch (IOException e) {
e.printStackTrace();
}
String[] split = contentAsString.split("\r\n");
for (String classPath : split) {
if (createBeanDefinition(classPath, registry) != null) {
count++;
}
}
return count;
}
public RootBeanDefinition createBeanDefinition(String classPath, BeanDefinitionRegistry registry) {
try {
String[] beanData = classPath.split(",");
if (beanData.length != 2) {
return null;
}
Class<?> aClass = Class.forName(beanData[0]);
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setBeanClass(aClass);
registry.registerBeanDefinition(beanData[1], rootBeanDefinition);
return rootBeanDefinition;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
创建一个txtBean.txt文件,该文件中存储了class全路径和bean的id,使用“,”进行分割。
com.qiupeng.bean.base.service.Impl.UserServiceImpl,userServiceImpl
创建测试类
public class BeanBaseTest {
private static final Logger logger = LogManager.getLogger(BeanBaseTest.class);
public static void main(String[] arg){
GenericApplicationContext context = new GenericApplicationContext();
new MyBeanDefinitionReader(context).loadBeanDefinitions("txtBean.txt");
context.refresh();
UserServiceImpl userServiceImpl = context.getBean("userServiceImpl",UserServiceImpl.class);
logger.info(userServiceImpl.getName());
Arrays.stream(context.getBeanDefinitionNames()).forEach(logger::info);
}
}
最终输出
[INFO ] 2023-06-15 14:25:41,090 com.qiupeng.bean.base.BeanBaseTest - 普通用户->张三
[INFO ] 2023-06-15 14:25:41,097 com.qiupeng.bean.base.BeanBaseTest - userServiceImpl
上述代码就是通过txt配置bean对象,在通过自己编写的”bean定义生成工厂“来构建bean定义对象,然后注册到容器中去。