BeanDefinition
application.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.xsd">
<!--XML方式配置Bean-->
<bean id="student" class="com.springleanring.Student">
</bean>
</beans>
Spring会把配置在XML中的bean信息转为IoC容器内部的数据结构,这个数据结构就是 BeanDefinition,也就是说在Spring的XML配置文件中存在的元素,在容器初始化时,会读取这些XML文件,从中解析,然后封装为BeanDefinition对象。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
void setParentName(@Nullable String parentName);
@Nullable
String getParentName();
void setBeanClassName(@Nullable String beanClassName);//设置bean的className
@Nullable
String getBeanClassName();
void setScope(@Nullable String scope);//设置bean的scope属性
@Nullable
String getScope();
void setLazyInit(boolean lazyInit);//设置lazyInit
boolean isLazyInit();//
void setDependsOn(@Nullable String... dependsOn);
//省略了其他方法
}
IoC容器初始化过程
(1)Resource定位过程
Resource定位指的是BeanDefinition的资源定位,这个过程类似于容器寻找数据的过程。
以在application.xml中配置的为例:
BeanDefinition是元素在IoC容器内部的存在形式,对BeanDefinition的定位也就是对元素所在资源文件application.xml的定位,定位到资源文件的位置 才能从application.xml中读取信息,因此IoC容器初始化时首先要完成对Resource的定位。
(2)BeanDefinition的载入
这个载入过程是把用户定义好的Bean表示成IoC容器的内部数据结构,这个容器内部数据结构就是BeanDefinition,也就是POJO对象在IoC容器中的抽象,通过BeanDefinition IoC容器可以方便的对Bean进行管理。
(3)向IoC容器注册这些BeanDefinition
BanDefinition注册指的是将第(2)步中得到的每一个BeanDefinition注册到一个HashMap中,IoC容器通过这个HashMap来持有这些BeanDefinition数据。
Spring IoC中的两个主要容器
IoC容器可以理解为spring管理Bean的地方。
(1)BeanFactory
是一个接口,定义了IoC容器最基本的功能规范,提供容器最基本的功能。如getBean方法,通过该方法可以从容器中获取Bean。
public interface BeanFactory {
/**
* 通过name获取容器中管理的bean
* @param name bean的name
* @return bean的一个实例
*/
Object getBean(String name) throws BeansException;
<T> T getBean(String name, @Nullable Class<T> requiredType)throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
boolean containsBean(String name);//是否包含某个bean
//是否是单 模式
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//是否是原型模式
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//省略了其他方法
}
(2)ApplicationContext
如果说BeanFactory是IoC容器的基本形式,那么ApplicationContext就是IoC容器的高级表现形式,它除了包含BeanFactory中的基本方法,还提供了一些其他的方法。
ApplicationContext同样是一个接口,它继承了ListableBeanFactory接口,ListableBeanFactory继承了BeanFacotry接口,因此ApplicationContext包含了BeanFactory接口中的所有方法。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
String getId();
String getApplicationName();
String getDisplayName();
long getStartupDate();
ApplicationContext getParent();
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
常用的实现ApplicationContext接口的三种容器:
FileSystemXmlApplicationContext:从文件系统载入Resource
ClassPathXmlApplicationContext:从class path载入Resource
XmlWebApplicationContext:在web容器中载入Resource
通过ApplicationContext从容器中获取一个bean:
//使用FileSystemXmlApplicationContext加载配置文件
ApplicationContext context=new FileSystemXmlApplicationContext("classpath*:/spring/applicationContext.xml");
//ClassPathXmlApplicationContext加载
//ApplicationContext context=new ClassPathXmlApplicationContext("classpath*:/spring/applicationContext.xml");
//从容器中获取bean
Student student= (Stuedent) context.getBean("student");//根据id从配置文件中获取bean
student.getName();
那么就以FileSystemXmlApplicationContext为例,分析ApplicationContext的实现过程。
1.FileSystemXmlApplicationContext
public FileSystemXmlApplicationContext {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
/**
* 构造函数:需要传入XML资源文件的路径
*/
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
/**
* 构造函数:传入多个资源文件
*/
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, (ApplicationContext)null);
}
/**
* 构造函数
* @param configLocations 资源文件数组
* @param parent 双亲IoC容器
*/
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
/**
* 构造函数
* @param configLocations 资源文件路径
* @param refresh 是否调用refresh方法载入BeanDefinition
*/
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, (ApplicationContext)null);
}
/**
* 构造函数,其他构造函数最终调用的都是该函数
* @param configLocations 资源文件路径
* @param refresh 是否调用refresh方法载入BeanDefinition
*/
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if(refresh) {
this.refresh();//分析容器初始化的一个重要入口
}
}
/**
* 通过资源文件路径获取Resource对象,返回的是一个FileSystemResource对象,通过这个对象可以进行相关I/O操作,完成BeanDefinition定位
* @param path 资源文件路径
* @return Resource spring中的资源对象
*/
protected Resource getResourceByPath(String path) {
if(path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
- 从第二个构造函数开始,实际上都是调用的 FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)这个构造函数。
- 对BeanDefinition资源定位的过程是从refresh方法开始的,refresh方法是分析容器初始化的重要入口。
2.AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
// 调用obtainFreshBeanFactory()方法获取IoC容器,容器的创建和对Resource的定位就在该方法中
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if(this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
}
/**
* 获取BeanFactory
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//refreshBeanFactory中会创建IoC容器
this.refreshBeanFactory();
//获取创建的IoC容器
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
- refreshBeanFactory()方法,在AbstractApplicationContext中,该方法是一个抽象方法,具体的实现由AbstractApplicationContext的子类AbstractRefreshableApplicationContext来实现,该方法中创建了IoC容器BeanFacotry对象。
- getBeanFactory()方法也是抽象方法,在AbstractRefreshableApplicationContext中实现,其实就是返回refreshBeanFactory方法中创建的BeanFacotry对象。
3.AbstractRefreshableApplicationContext
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
protected final void refreshBeanFactory() throws BeansException {
//如果已经建立了BeanFactory,销毁并关闭BeanFactory
if(this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
//构造了一个BeanFactory,这里使用DefaultListableBeanFactory实现
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
//载入BeanDefinition
this.loadBeanDefinitions(beanFactory);
Object var2 = this.beanFactoryMonitor;
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
/**
* 创建DefaultListableBeanFactory的地方
* getInternalParentBeanFactory()的具体实现可以参看AbstractApplicationContext的实现
*
* @return
*/
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(this.getInternalParentBeanFactory());
}
/**
* 获取创建的IoC容器
*/
public final ConfigurableListableBeanFactory getBeanFactory() {
Object var1 = this.beanFactoryMonitor;
synchronized(this.beanFactoryMonitor) {
if(this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext");
} else {
return this.beanFactory;
}
}
}
/**
* 抽象方法,载入BeanDefinition,由子类AbstractXmlApplicationContext实现
*
* @param beanFactory
* @throws BeansException
* @throws IOException
*/
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
}
- refreshBeanFactory()方法中调用了createBeanFactory()方法完成了容器的创建,使用的是DefaultListableBeanFactory类型的容器。
- 创建容器之后,调用了loadBeanDefinitions()方法载入BeanDefinition,该方法由子类AbstractXmlApplicationContext实现。
- loadBeanDefinitions()方法中包含了Resource资源定位的过程。
4.AbstractXmlApplicationContext
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建XmlBeanDefinitionReader对象,通过回调设置到BeanFactory中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
//设置ResourceLoader
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//启动Bean定义信息载入的过程,委托给beanDefinitionReader完成
this.initBeanDefinitionReader(beanDefinitionReader);
//载入BeanDefinition
this.loadBeanDefinitions(beanDefinitionReader);
}
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
reader.setValidating(this.validating);
}
/**
* 载入BeanDefinition
* @param reader
* @throws BeansException
* @throws IOException
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取Reource类型的配置文件的地址
Resource[] configResources = getConfigResources();
if (configResources != null) {
//调用AbstractBeanDefinitionReader中的loadBeanDefinitions方法
reader.loadBeanDefinitions(configResources);
}
//获取字符串类型的配置文件的地址
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//调用AbstractBeanDefinitionReader中的loadBeanDefinitions方法
reader.loadBeanDefinitions(configLocations);
}
}
protected Resource[] getConfigResources() {
return null;
}
}
- loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中首先创建了XmlBeanDefinitionReader对象,从名称上可以猜到,它可以读取XML中定义的bean信息。
- loadBeanDefinitions(XmlBeanDefinitionReader reader)方法中又调用了XmlBeanDefinitionReader中的loadBeanDefinitions()方法,XmlBeanDefinitionReader是AbstractBeanDefinitionReader的子类,该方法在AbstractBeanDefinitionReader中实现。
5.AbstractBeanDefinitionReader
载入BeanDefinition时:
- AbstractBeanDefinitionReader中实现的是参数类型是String的方法。
- 如果传入的参数是Resource类型的,先进入到loadBeanDefinitions(Resource… resources)方法中,在该方法中又会调用XmlBeanDefinitionReader中的loadBeanDefinitions方法,也就是如果参数类型是Resource,是由XmlBeanDefinitionReader实现的。
查看上面FileSystemXmlApplicationContext用法的例子,传入的参数是资源文件的地址,因此调用的是AbstractBeanDefinitionReader的loadBeanDefinitions(String… locations)方法:
FileSystemXmlApplicationContext("classpath*:/spring/applicationContext.xml");
AbstractBeanDefinitionReader类中实现的方法为例:
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
/**
* 载入参数类型是Resource对象的BeanDefinition,由子类XmlBeanDefinitionReader实现
*/
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
//对载入Bean的数量进行统计
int counter = 0;
//遍历整个Resource集合,从每个集合中载入所有的BeanDefinition
for (Resource resource : resources) {
//loadBeanDefinitions方法是一个接口方法,在XmlBeanDefinitionReader中有具体的实现
counter += loadBeanDefinitions(resource);
}
return counter;
}
/**
* 根据配置文件的地址,载入BeanDefinition
*/
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取ResourceLoader对象,实际使用的是DefaultResourceLoader
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//这里对Resource的路径模式进行解析,得到Resource集合,这些集合指向我们已经定义好的BeanDefinition信息
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//调用DefaultResourceLoader的getResource完成Resource定位,将传入的资源文件的地址,转为Resource对象
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
/**
* 根据配置文件的地址,载入BeanDefinition
*/
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
}
- 在loadBeanDefinitions(String location, @Nullable Set actualResources)方法中定义了ResourceLoader对象,由ResourceLoader完成资源的加载。ResourceLoader其实是使用了DefaultResourceLoader对象的getResource方法完成Resource对象的定位。
- 在loadBeanDefinitions(String location, @Nullable Set actualResources)方法中根据location资源文件地址将资源文件转为了Resource对象,然后还是调用的XmlBeanDefinitionReader中的loadBeanDefinitions(Resource resource)完成BeanDefinition的载入。
6.DefaultResourceLoader
进入DefaultResourceLoader看一下如何根据配置文件地址完成对Resource的定位:
public class DefaultResourceLoader implements ResourceLoader {
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
Iterator var2 = this.protocolResolvers.iterator();
Resource resource;
do {
if(!var2.hasNext()) {
//处理带有/反斜杠的resource
if(location.startsWith("/")) {
return this.getResourceByPath(location);
}
//处理带有calsspath标识的resource
if(location.startsWith("classpath:")) {
return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());
}
try {
//处理URL标识的resource
URL url = new URL(location);
return (Resource)(ResourceUtils.isFileURL(url)?new FileUrlResource(url):new UrlResource(url));
} catch (MalformedURLException var5) {
return this.getResourceByPath(location);
}
}
ProtocolResolver protocolResolver = (ProtocolResolver)var2.next();
resource = protocolResolver.resolve(location, this);
} while(resource == null);
return resource;
}
protected Resource getResourceByPath(String path) {
return new DefaultResourceLoader.ClassPathContextResource(path, this.getClassLoader());
}
}
到此,IoC容器对Resource的定位过程已经完成。
最后简单总结一下重点流程:
1.初始化时需要创建容器BeanFactory,默认使用的是DefaultListableBeanFactory类型的容器。 2.容器创建之后,会调用loadBeanDefinitions()方法载入BeanDefinition. 载入过程中的流程: (1)创建XmlBeanDefinitionReader对象,为读取XML资源文件中定义的bean信息做准备。 (2)为XmlBeanDefinitionReader对象设置ResoucrLoader,用于资源文件的加载。 (3)调用XmlBeanDefinitionReader父类AbstractBeanDefinitionReader中实现的loadBeanDefinitions()方法,传入资源文件对象或文件地址,在这一步中,ResourceLoader对象实际使用的是DefaultResourceLoader。 (4)在DefaultResourceLoader中完成了对Resource的定位。
参考:
spring技术内幕:深入解析spring架构与设计原理
没有回复内容