pc网站怎么建设流程,wordpress my-account,WordPress最好的免费主题,网站建设收费标准渠道IoC容器是什么#xff1f;
IoC文英全称Inversion of Control#xff0c;即控制反转#xff0c;我么可以这么理解IoC容器#xff1a;
“把某些业务对象的的控制权交给一个平台或者框架来同一管理#xff0c;这个同一管理的平台可以称为IoC
容器。”
我们刚开始学习…IoC容器是什么
IoC文英全称Inversion of Control即控制反转我么可以这么理解IoC容器
“把某些业务对象的的控制权交给一个平台或者框架来同一管理这个同一管理的平台可以称为IoC
容器。”
我们刚开始学习spring的时候会经常看到的类似下面的这代码
上面代码中在创建ApplicationContext实例对象过程中会创建一个spring容器该容器会读取配置文
件cjj/models/beans.xml,并统一管理由该文件中定义好的所有bean实例对象如果要获取某个bean
实例使用getBean方法就行了。例如我们只需要将Person提前配置在beans.xml文件中可以理解为
注入之后我们可以不需使用new Person()的方式创建实例而是通过容器来获取Person实例这就
相当于将Person的控制权交由spring容器了差不多这就是控制反转的概念。
那在创建IoC容器时经历了哪些呢为此先来了解下Spring中IoC容器分类继而根据一个具体的容器
来讲解IoC容器初始化的过程。
Spring中有两个主要的容器系列
1. 实现BeanFactory接口的简单容器
2. 实现ApplicationContext接口的高级容器。
ApplicationContext appContext new
ClassPathXmlApplicationContext(cjj/models/beans.xml);
Person p (Person)appContext.getBean(person);ApplicationContext比较复杂它不但继承了BeanFactory的大部分属性还继承其它可扩展接口扩
展的了许多高级的属性其接口定义如下
public interface ApplicationContext extends EnvironmentCapable,
ListableBeanFactory, //继承于
BeanFactory HierarchicalBeanFactory,//继承于
BeanFactory
MessageSource,
//
ApplicationEventPublisher,// ResourcePatternResolver
//继承ResourceLoader用于获取resource对象
在BeanFactory子类中有一个DefaultListableBeanFactory类它包含了基本Spirng IoC容器所具有的
重要功能开发时不论是使用BeanFactory系列还是ApplicationContext系列来创建容器基本都会使用
到DefaultListableBeanFactory类可以这么说在spring中实际上把它当成默认的IoC容器来使用。下
文在源码实例分析时你将会看到这个类。
注文章有点长需要细心阅读不同版本的spring中源码可能不同但逻辑几乎是一样的如果可
以建议还是看源码 ^_^
回到本文正题上来关于Spirng IoC容器的初始化过程在《Spirng技术内幕深入解析Spring架构与设
计原理》一书中有明确的指出IoC容器的初始化过程可以分为三步
1. Resource定位Bean的定义文件定位
2. 将Resource定位好的资源载入到BeanDefinition
3. 将BeanDefiniton注册到容器中
第一步 Resource定位
Resource是Sping中用于封装I/O操作的接口。正如前面所见在创建spring容器时通常要访问XML配
置文件除此之外还可以通过访问文件类型、二进制流等方式访问资源还有当需要网络上的资源时可
以通过访问URLSpring把这些文件统称为ResourceResource的体系结构如下:
常用的resource资源类型如下
FileSystemResource以文件的绝对路径方式进行访问资源效果类似于Java中的File;
ClassPathResourcee以类路径的方式访问资源效果类似于
this.getClass().getResource(/).getPath();ServletContextResourceweb应用根目录的方式访问资源效果类似于
request.getServletContext().getRealPath();
UrlResource访问网络资源的实现类。例如file: http: ftp:等前缀的资源对象;
ByteArrayResource: 访问字节数组资源的实现类。
那如何获取上图中对应的各种Resource对象呢
Spring提供了ResourceLoader接口用于实现不同的Resource加载策略该接口的实例对象中可以获取
一个resource对象也就是说将不同Resource实例的创建交给ResourceLoader的实现类来处理。
ResourceLoader接口中只定义了两个方法
Resource getResource(String location); //通过提供的资源location参数获取Resource实例
ClassLoader getClassLoader(); // 获取ClassLoader,通过ClassLoader可将资源载入JVM
注ApplicationContext的所有实现类都实现RecourceLoader接口因此可以直接调用
getResource参数获取Resoure对象。不同的ApplicatonContext实现类使用getResource方法取得
的资源类型不同例如FileSystemXmlApplicationContext.getResource获取的就是
FileSystemResource实例ClassPathXmlApplicationContext.gerResource获取的就是
ClassPathResource实例XmlWebApplicationContext.getResource获取的就是
ServletContextResource实例另外像不需要通过xml直接使用注解Configuation方式加载资源的
AnnotationConfigApplicationContext等等。
在资源定位过程完成以后就为资源文件中的bean的载入创造了I/O操作的条件如何读取资源中的数
据将会在下一步介绍的BeanDefinition的载入过程中描述。
*第二步 通过返回的*resource对象进行BeanDefinition的载入
1.什么是BeanDefinition? BeanDefinition与Resource的联系呢
官方文档中对BeanDefinition的解释如下
A BeanDefinition describes a bean instance, which has property values, constructor argument
values, and further information supplied by concrete implementations.
它们之间的联系从官方文档描述的一句话Load bean definitions from the specified resource中可见
一斑。
/**
* Load bean definitions from the specified resource.
* param resource the resource descriptor
* return the number of bean definitions found
* throws BeanDefinitionStoreException in case of loading or parsing errors
*/
int loadBeanDefinitions(Resource resource) throws
BeanDefinitionStoreException;
总之BeanDefinition相当于一个数据结构这个数据结构的生成过程是根据定位的resource资源
对象中的bean而来的这些bean在Spirng IoC容器内部表示成了的BeanDefintion这样的数据结构
IoC容器对bean的管理和依赖注入的实现都是通过操作BeanDefinition来进行的。
2.如何将BeanDefinition载入到容器
在Spring中配置文件主要格式是XML对于用来读取XML型资源文件来进行初始化的IoC 容器而
言该类容器会使用到AbstractXmlApplicationContext类该类定义了一个名为
loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的方法用于获取BeanDefinition// 该方法属于AbstractXmlApplicationContect类
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);
// 用于获取BeanDefinition
this.loadBeanDefinitions(beanDefinitionReader);
}
此方法在具体执行过程中首先会new一个与容器对应的BeanDefinitionReader型实例对象然后将生成
的BeanDefintionReader实例作为参数传入loadBeanDefintions(XmlBeanDefinitionReader)继续往
下执行载入BeanDefintion的过程。例如AbstractXmlApplicationContext有两个实现类
FileSystemXmlApplicationContext、ClassPathXmlApplicationContext这些容器在调用此方法时会
创建一个XmlBeanDefinitionReader类对象专门用来载入所有的BeanDefinition。
下面以XmlBeanDefinitionReader对象载入BeanDefinition为例使用源码说明载入BeanDefinition的
过程
// 该方法属于AbstractXmlApplicationContect类protected void
loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException,
IOException {
Resource[] configResources getConfigResources();//获取所有定位到的
resource资源位置用户定义
if (configResources ! null) {
reader.loadBeanDefinitions(configResources);//载入resources
}
String[] configLocations getConfigLocations();//获取所有本地配置文件的位置
容器自身
if (configLocations ! null) {
reader.loadBeanDefinitions(configLocations);//载入resources
}
}
通过上面代码将用户定义的资源以及容器本身需要的资源全部加载到reader中
reader.loadBeanDefinitions方法的源码如下// 该方法属于AbstractBeanDefinitionReader类, 父接口BeanDefinitionReader
Override
public int loadBeanDefinitions(Resource... resources) throws
BeanDefinitionStoreException {
Assert.notNull(resources, Resource array must not be null);
int counter 0;
for (Resource resource : resources) {
// 将所有资源全部加载交给AbstractBeanDefinitionReader的实现子类处理这些
resource
counter loadBeanDefinitions(resource);
}
return counter;
}
BeanDefinitionReader接口定义了 int loadBeanDefinitionsResource resource方法
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
int loadBeanDefinitions(Resource... resources) throws
BeanDefinitionStoreException;
XmlBeanDefinitionReader 类实现了BeanDefinitionReader接口中的loadBeanDefinitions(Resource)
方法其继承关系如上图所示。XmlBeanDefinitionReader类中几主要对加载的所有resource开始进行
处理大致过程是先将resource包装为EncodeResource类型然后处理为生成BeanDefinition对
象做准备其主要几个方法的源码如下
public int loadBeanDefinitions(Resource resource) throws
BeanDefinitionStoreException {
// 包装resource为EncodeResource类型
return loadBeanDefinitions(new EncodedResource(resource));
}
// 加载包装后的EncodeResource资源
public int loadBeanDefinitions(EncodedResource encodedResource) throws
BeanDefinitionStoreException {
Assert.notNull(encodedResource, EncodedResource must not be null);
if (logger.isInfoEnabled()) {
logger.info(Loading XML bean definitions from
encodedResource.getResource());
}try {
// 通过resource对象得到XML文件内容输入流并为IO的InputSource做准备
InputStream inputStream
encodedResource.getResource().getInputStream();
try {
// Create a new input source with a byte stream.
InputSource inputSource new InputSource(inputStream);
if (encodedResource.getEncoding() ! null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 开始准备 load bean definitions from the specified XML file
return doLoadBeanDefinitions(inputSource,
encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
IOException parsing XML document from
encodedResource.getResource(), ex);
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource
resource)
throws BeanDefinitionStoreException {
try {
// 获取指定资源的验证模式
int validationMode getValidationModeForResource(resource);
// 从资源对象中加载DocumentL对象大致过程为将resource资源文件的内容读入到
document中
// DocumentLoader在容器读取XML文件过程中有着举足轻重的作用
// XmlBeanDefinitionReader实例化时会创建一个DefaultDocumentLoader型的私有
属性,继而调用loadDocument方法
// inputSource--要加载的文档的输入源
Document doc this.documentLoader.loadDocument(
inputSource, this.entityResolver, this.errorHandler,
validationMode, this.namespaceAware);
// 将document文件的bean封装成BeanDefinition并注册到容器
return registerBeanDefinitions(doc, resource);
}
catch ...(略)
}
DefaultDocumentLoader大致了解即可感兴趣可继续深究其源码如下看完收起便于阅读下
文
View Code
上面代码分析到了registerBeanDefinitions(doc, resource)这一步也就是准备将Document中的Bean
按照Spring bean语义进行解析并转化为BeanDefinition类型这个方法的具体过程如下/**
* 属于XmlBeanDefinitionReader类
* Register the bean definitions contained in the given DOM document.
* param doc the DOM document
* param resource
* return the number of bean definitions found
* throws BeanDefinitionStoreException
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws
BeanDefinitionStoreException {
// 获取到DefaultBeanDefinitionDocumentReader实例
BeanDefinitionDocumentReader documentReader
createBeanDefinitionDocumentReader();
// 获取容器中bean的数量
int countBefore getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
通过 XmlBeanDefinitionReader 类中的私有属性 documentReaderClass 可以获得一个
DefaultBeanDefinitionDocumentReader 实例对象
private Class? documentReaderClass
DefaultBeanDefinitionDocumentReader.class;
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return
BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.document
ReaderClass));
}
DefaultBeanDefinitionDocumentReader实现了BeanDefinitionDocumentReader接口它的
registerBeanDefinitions方法定义如下
// DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
{
this.readerContext readerContext;
logger.debug(Loading bean definitions);
// 获取doc的root节点通过该节点能够访问所有的子节点
Element root doc.getDocumentElement();
// 处理beanDefinition的过程委托给BeanDefinitionParserDelegate实例对象来完成
BeanDefinitionParserDelegate delegate createHelper(readerContext, root);
// Default implementation is empty.
// Subclasses can override this method to convert custom elements into
standard Spring bean definitions
preProcessXml(root);
// 核心方法代理
parseBeanDefinitions(root, delegate);postProcessXml(root);
}
上面出现的BeanDefinitionParserDelegate类非常非常重要需要了解代理技术如JDK动态代理、
cglib动态代理等。Spirng BeanDefinition的解析就是在这个代理类下完成的此类包含了各种对符合
Spring Bean语义规则的处理比如、、等的检测。
parseBeanDefinitions(root, delegate)方法如下
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate
delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl root.getChildNodes();
// 遍历所有节点做对应解析工作
// 如遍历到import标签节点就调用importBeanDefinitionResource(ele)方法对应
处理
// 遍历到bean标签就调用processBeanDefinition(ele,delegate)方法对应处理
for (int i 0; i nl.getLength(); i) {
Node node nl.item(i);
if (node instanceof Element) {
Element ele (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
//对应用户自定义节点处理方法
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate
delegate) {
// 解析import标签
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 解析alias标签
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 解析bean标签,最常用过程最复杂
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 解析beans标签
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);}
}
这里针对常用的标签中的方法做简单介绍其他标签的加载方式类似
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele,
BeanDefinitionParserDelegate delegate) {
// 该对象持有beanDefinition的name和alias可以使用该对象完成beanDefinition向容
器的注册
BeanDefinitionHolder bdHolder
delegate.parseBeanDefinitionElement(ele);
if (bdHolder ! null) {
bdHolder delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册最终被修饰的bean实例下文注册beanDefinition到容器会讲解该方法
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(Failed to register bean definition with
name
bdHolder.getBeanName() , ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new
BeanComponentDefinition(bdHolder));
}
}
parseBeanDefinitionElement(Element ele)方法会调用parseBeanDefinitionElement(ele, null)方法
并将值返回BeanDefinitionHolder类对象这个方法将会对给定的标签进行解析如果在解析标签的
过程中出现错误则返回null。
需要强调一下的是parseBeanDefinitionElement(ele, null)方法中产生了一个抽象类型的BeanDefinition
实例这也是我们首次看到直接定义BeanDefinition的地方这个方法里面会将标签中的内容解析到
BeanDefinition中之后再对BeanDefinition进行包装将它与beanName,Alias等封装到
BeanDefinitionHolder 对象中该部分源码如下
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele,
BeanDefinition containingBean) {
String id ele.getAttribute(ID_ATTRIBUTE);String nameAttr ele.getAttribute(NAME_ATTRIBUTE);
...(略)
String beanName id;
...略
// 从上面按过程走来首次看到直接定义BeanDefinition
// 该方法会对bean节点以及其所有子节点如property、List、Set等做出解析具
体过程本文不做分析太多太长
AbstractBeanDefinition beanDefinition parseBeanDefinitionElement(ele,
beanName, containingBean);
if (beanDefinition ! null) {
if (!StringUtils.hasText(beanName)) {
...(略)
}
String[] aliasesArray StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName,
aliasesArray);
}
return null;
}
第三步将BeanDefiniton注册到容器中
最终Bean配置会被解析成BeanDefinition并与beanName,Alias一同封装到BeanDefinitionHolder
类中 之后beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition())注册
到DefaultListableBeanFactory.beanDefinitionMap中。之后客户端如果要获取Bean对象Spring容
器会根据注册的BeanDefinition信息进行实例化。
BeanDefinitionReaderUtils类
public static void registerBeanDefinition(
BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory)
throws BeansException {
// Register bean definition under primary name.
String beanName bdHolder.getBeanName(); // 注册beanDefinition!!!
beanFactory.registerBeanDefinition(beanName,
bdHolder.getBeanDefinition());
// 如果有别名的话也注册进去Register aliases for bean name, if any.
String[] aliases bdHolder.getAliases();
if (aliases ! null) {
for (int i 0; i aliases.length; i) {
beanFactory.registerAlias(beanName, aliases[i]);
}
}
}DefaultListableBeanFactory实现了上面调用BeanDefinitionRegistry接口的
registerBeanDefinition( beanName, bdHolder.getBeanDefinition())方法这一部分的主要逻辑是向
DefaultListableBeanFactory对象的beanDefinitionMap中存放beanDefinition当初始化容器进行
bean初始化时在bean的生命周期分析里必然会在这个beanDefinitionMap中获取beanDefition实
例有机会成文分析一下bean的生命周期到时可以分析一下如何使用这个beanDefinitionMap。
registerBeanDefinition( beanName, bdHolder.getBeanDefinition() )方法具体方法如下
/** Map of bean definition objects, keyed by bean name */
private final MapString, BeanDefinition beanDefinitionMap new
ConcurrentHashMapString, BeanDefinition(256);
public void registerBeanDefinition(String beanName, BeanDefinition
beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, Bean name must not be empty);
Assert.notNull(beanDefinition, Bean definition 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);
}
}
// beanDefinitionMap是个ConcurrentHashMap类型数据用于存放
beanDefinition,它的key值是beanName
Object oldBeanDefinition this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition ! null) {
if (!this.allowBeanDefinitionOverriding) {
throw new
BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
Cannot register bean definition [ beanDefinition ]
for bean beanName
: theres already [ oldBeanDefinition ] bound);
}
else {
if (logger.isInfoEnabled()) {
logger.info(Overriding bean definition for bean
beanName
: replacing [ oldBeanDefinition ] with [
beanDefinition ]);
}
}
}
else {
this.beanDefinitionNames.add(beanName);
} // 将获取到的BeanDefinition放入Map中容器操作使用bean时通过这个
HashMap找到具体的BeanDefinition