使用xml配置Bean的示例
定义一个类BookServiceImpl
public class BookServiceImpl implements BookService{
@Override
public double getBookPrice() {
return 58.8;
}
}
定义另一个类PressServiceImpl依赖BookServiceImpl
public class PressServiceImpl implements PressService{
private BookService bookService;
public void setBookService(BookService bookService) {
this.bookService = bookService;
}
@Override
public String say() {
return "本书的价格是:"+bookService.getBookPrice();
}
}
定义spring-chapter2.xml文件
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="bookService" class="com.tool.generate.s0620.BookServiceImpl"></bean>
<bean id="pressService" class="com.tool.generate.s0620.PressServiceImpl">
<property name="bookService" ref="bookService"></property>
</bean>
</beans>
@Test
public void testGet(){
ApplicationContext applicationContext=
new FileSystemXmlApplicationContext("classpath:spring-chapter2.xml");
PressService pressService=(PressService)applicationContext.getBean("pressService");
String price=pressService.say();
System.out.println(price);
}
运行结果如下:
本书的价格是:58.8
从上面的示例可以看出,采用xml配置Bean的方式,入口类是FileSystemXmlApplicationContext,我们就先从该类来分析Spring
解析源码1-FileSystemXmlApplicationContext构造函数
执行的是FileSystemXmlApplicationContext构造函数
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);//初始化父容器AbstractApplicationContext
this.setConfigLocations(configLocations);//设置资源文件的位置
if (refresh) {
this.refresh();//使用核心方法refresh(),其实是在超类AbstractApplicationContext中定义的一个模版方法(模版方法模式)
}
}
查看FileSystemXmlApplicationContext类图
由类关系图可知,ClassPathXmlApplicationContext 最顶层父类接口有七个:
- BeanFactory
BeanFactory 是访问 Spring Bean 容器的根接口;定义根据 Bean name 获取 Bean 的接口;
- Aware
1.Spring 的依赖注入最大亮点就是所有的 Bean 对 Spring 容器的存在是没有意识的;
2.但是在实际项目中,我们不可避免的要用到 Spring 容器本身提供的资源,这时候要让 Bean 主动意识到 Spring 容器的存在,才能调用 Spring 所提供的资源,这就是 Spring Aware;
- LifeCycle
1.定义启动/停止生命周期控制方法的通用接口;
- InitializingBean
1.Spring 提供的拓展性接口,InitializingBean 接口为 Bean 提供了属性初始化后的处理方法,它只有一个 afterPropertiesSet() 方法,凡是继承该接口的类,在 Bean 的属性初始化后都会执行该方法,进行其总体配置和最终初始化验证;
- ResourceLoader
1.用于加载资源(例如,类路径或文件系统资源)的策略接口;
1.Spring 中整合了获取资源的工具,就是使用 Resource 接口。此接口是 Spring 为了统一读取诸如本地文件、classpath 项目路径下的文件、url 互联网上的文件等不同类型渠道的资源,封装隐藏如打开流、关闭流、报错处理等大量重复模板代码,而专程设计提供的接口类。
2.而 Spring 框架为了更方便的获取资源,尽量弱化程序员对各个 Resource 接口实现类的感知与分辨,降低学习与使用成本,定义了另一个接口,就是:ResourceLoader接口。
- ApplicationEventPublisher
1.封装事件发布功能的接口;
2.函数式接口;
- AutoCloseable
1.当一个资源类实现了该接口 close 方法,在使用 try-with-resources 语法创建的资源抛出异常后,JVM 会自动调用 close 方法进行资源释放;当没有抛出异常正常退出 try 代码块时也会自动调用 close 方法。像数据库链接类 Connection,io 类 InputStream 或 OutputStream 都直接或者间接实现了该接口;
以上内容来自:https://blog.csdn.net/weixin_39651041/article/details/129674784
1、 super(parent);解析
super(parent);执行如下
目的:初始化AbstractApplicationContext容器
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
this.setParent(parent);
}
public AbstractApplicationContext() {
this.logger = LogFactory.getLog(this.getClass());
this.id = ObjectUtils.identityToString(this);
this.displayName = ObjectUtils.identityToString(this);
this.beanFactoryPostProcessors = new ArrayList();
this.active = new AtomicBoolean();
this.closed = new AtomicBoolean();
this.startupShutdownMonitor = new Object();
this.applicationStartup = ApplicationStartup.DEFAULT;
this.applicationListeners = new LinkedHashSet();
this.resourcePatternResolver = this.getResourcePatternResolver();
}
public void setParent(@Nullable ApplicationContext parent) {
this.parent = parent;
if (parent != null) {
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
}
}
}
2、this.setConfigLocations(configLocations);
AbstractRefreshableConfigApplicationContext 设置资源文件的位置
3、refresh()解析
ConfigurableApplicationContext类定义了
void refresh() throws BeansException, IllegalStateException;
抽象类AbstractApplicationContext实现了ConfigurableApplicationContext接口,实现了refresh();
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}
}
}
AbstractApplicationContext.refresh()方法是个模版方法,定义了需要执行的一些步骤。并不是实现了所有的逻辑,只是充当了一个模版,由其子类去实现更多个性化的逻辑。
最核心的两步如下:1、创建BeanFactory:
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
2、实例化Bean:
this.finishBeanFactoryInitialization(beanFactory);
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();方法有两个步:
1、this.refreshBeanFactory();刷新BeanFactory;
这个方法的定义是在 AbstractApplicationContext中,是一个抽象方法,也是一个模版方法,需要AbstractApplicationContext的子类来实现逻辑。其具体实现是其子类AbstractRefreshableApplicationContext中完成的。refreshBeanFactory()方法实现的部分代码如下:
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
this.loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
} catch (IOException var2) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
}
}
可以发现,在refreshBeanFactory()方法的实现中,首先检查当前上下文是否已经存在BeanFactory。如果已经存在 BeanFactory,先销毁 Bean 和 BeanFactory,然后创建新的 BeanFactory。DefaultListableBeanFactory beanFactory= createBeanFactory();这行代码只是创建了一个空的BeanFactory,其中没有任何Bean。因此refreshBeanFactory()方法的核心功能是在loadBeanDefinitions(beanFactory);这行代码中实现的。进入 loadBeanDefinitions(beanFactory)方法进行分析。
AbstractRefreshableApplicationContext类的抽象方法
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException;
此方法是抽象方法,需要其子类实现。其具体实现是在AbstractXmlApplicationContext类中。其方法实现如下所示:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中,通过上一步创建的空的BeanFactory 来创建一个XmlBeanDefinitionReader对象。XmlBeanDefinitionReader 是用来解析 XML中定义的 bean 的。下面重点讲解 loadBeanDefinitions(beanDefinitionReader)方法,这是一个重载的方法,这个方法的入参是刚刚生成的XmlBeanDefinitionReader对象。下面将进入重载的loadBeanDefinitions方法进行分析,代码如下:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = this.getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = this.getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
这个方法主要功能是解析资源文件的位置,然后调用XmlBeanDefinitionReader 对象的
loadBeanDefinitions 方法解析 Bean 的定义。核心逻辑reader.loadBeanDefinitions(configLocations);XmlBeanDefinitionReader是AbstractBeanDefinitionReader的子类,所以reader.loadBeanDefinitions(configLocations)会调用其父类的方法 loadBeanDefinitions。分析AbstractBeanDefinitionReader类的loadBeanDefinitions(String… locations)
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
String[] var3 = locations;
int var4 = locations.length;
for(int var5 = 0; var5 < var4; ++var5) {
String location = var3[var5];
count += this.loadBeanDefinitions(location);
}
return count;
}
可以发现 loadBeanDefinitions(String… locations)方法会遍历资源数组,最终会调用重载方法loadBeanDefinitions(String location,@Nullable SetactualResources),重载方法的部分实现代码如下:
2、return this.getBeanFactory();获取BeanFactory