spring 系列
1.spring 是一个一个引擎,
2.Spring MVC 是基于 Spring 的一个 MVC 框架
3.Spring Boot 是基于 Spring4 的条件注册的一套快速开发整合包
1.spring 源码解读:
我们来理一下其中的关系:
core container(核心容器):包含了 beans ,core,context, expression language模块,
core 和 bean 是spring的基础部分,提供IOC和 DI 特性,实现是用工厂设计模式实现的,beanFactory,
Spring 的核心之IOC:
IOC (Inversion of Control): 控制反转,把对象本身的控制权转向Spring容器,从而由Spring来管理对象的生命周期。
IOC 大家都知道,应用也很广泛,因为java里面都是各种各样的bean ,ioc 刚好是来管理bean的,而 Ioc 的实现原理和具体实现你可能不是很清楚,那我们来看一下
核心类:
DefaultListableBeanFactory,这个类就是ioc实现的核心类了,所以重要的操作都在,
他继承了 AbstractAutowireCapableBeanFactory 并且实现了两个接口类ConfigurableListableBeanFactory,BeanDefinitionRegistry,并且序列化了。下面是相关类的组成结构。
首先我们来看一下
1.配置文件封装
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(“BizAccountDao.xml”));
具体操作步骤是这样的
,首先我用ClassPathResource的构造函数来构造Resource资源文件的实例对象,因为XmlBeanFactory的构造函数需要Resource的实例,后面的操作跟这个Resource实例都是连着的,有了Resource对象就可以进行xmlbeanfactory的初始化了
那么 Resource 是如何封装的呢? 找到Resource这个类看一下,这个类继承了 InputStreamSource接口类, InputStreamSource 封装任何能返回 InputStream的类,例如file,classpath的资源文件,和byte ,Array之类的,
Resource 抽象了需要使用到的底层资源:file,url等,还有有对文件的相关操作接口,我们传入的参数十一个url的字符串地址,原来他是根据我们传入的路径来查找对应的文件的,
public interface Resource extends InputStreamSource {
/**
*文件是否存在
**/
boolean exists();
/**
*文件是否可读
**/
boolean isReadable();
/**
*文件是否打开状态
**/
boolean isOpen();
/**
*得到url对象
**/
URL getURL() throws IOException;
/**
*得到uri对象
**/
URI getURI() throws IOException;
/**
*得到file对象
**/
File getFile() throws IOException;
/**
*获取对象的内容长度
**/
long contentLength() throws IOException;
/**
*
**/
long lastModified() throws IOException;
/**
*基于当前资源 创建一个Resource 资源对象
**/
Resource createRelative(String var1) throws IOException;
/**
*获取Resource文件的名称
**/
String getFilename();
/**
*打印错误信息
**/
String getDescription();
}
public interface InputStreamSource {
/**
*得到 InputStream 对象
**/
InputStream getInputStream() throws IOException;
}
那我们在整理一下逻辑; 如下图
首先我们需要一个resource对象,用classpathresource来创建resource对象,创建resource对象之后我们可以使用resource提供的功能对resource操作,resource实例创建好了之后,把他传入到xmlbeanfactory的构造函数中,由xmlbeanfactory进行初始化。
@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
//初始化 resource
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
//加载
this.reader.loadBeanDefinitions(resource);
}
}
接下来xmlbeanfactory做的事情就是把xml文件转bean并初始化的一个过程,resource传入后构造函数之后,xmlbeanfactory又调用了另一个构造函数, super(parentBeanFactory); 他调用了父类的构造函数对beanfactory进行初始化, 下面是他的初始化过程。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
//调用AbstractAutowireCapableBeanFactory 父类中的构造函数(2)
super(parentBeanFactory);
}
(2)
public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
//初始化(3)
this();
//实例化 (AbstractBeanFactory父类4)
this.setParentBeanFactory(parentBeanFactory);
}
//3
public AbstractAutowireCapableBeanFactory() {
this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
this.parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
this.allowCircularReferences = true;
this.allowRawInjectionDespiteWrapping = false;
this.ignoredDependencyTypes = new HashSet();
this.ignoredDependencyInterfaces = new HashSet();
this.factoryBeanInstanceCache = new ConcurrentHashMap(16);
this.filteredPropertyDescriptorsCache = new ConcurrentHashMap(256);
this.ignoreDependencyInterface(BeanNameAware.class);
this.ignoreDependencyInterface(BeanFactoryAware.class);
this.ignoreDependencyInterface(BeanClassLoaderAware.class);
}
//4
public void setParentBeanFactory(BeanFactory parentBeanFactory) {
if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
} else {
this.parentBeanFactory = parentBeanFactory;
}
}
}
最后关键部分道到了,上面初始化的前置工作完成之后,Wimbledon就要真正实现资源的加载了,如下:
//XmlBeanFactory类
this.reader.loadBeanDefinitions(resource);
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//再次调用构造方法
return this.loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isInfoEnabled()) {
this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
public final Resource getResource() {
return this.resource;
}
这里我们提一下一个重要的东西,
为什么要提到他呢,那肯定是他的作用啦,作用:忽略给定接口的自动装配,什么意思呢?例如:有两个类A,B,A中有属性B,那么spring获取A的bean时,如果其属性B还有初始化,那么spring会自动初始化B,这也是spring提供的一个重要的特性,but,也有不会自动初始化的情况,
例如B 实现了c接口,而spring是这样解释的:自动装配时忽略给定的依赖接口。我们在接口和接口实现类中就有应用到,下面举个例子:
public interface Aservice{
public String get();
}
public interface AserviceImpl{
public String get(){
retrun "A";
}
}
//使用
public class Acontroller{
@Autowired
Aservice aservice;
public Map get(){
Map<String,Object> map = new HashMap();
map.put("A",aservice.get());
retrun map;
}
}
这是典型的通过解析Application上下文注册依赖,进行对应的注入。
2.加载
封装完成后就需要加载他了,那么什么时候才是加载他呢?我们来看一下
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}
}
this.reader.loadBeanDefinitions(resource);实现真正的加载操作,
1.
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//对resource对象进行封装,再调用另一个函数来处理接下里的事情(2)
return this.loadBeanDefinitions(new EncodedResource(resource));
}
2.
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isInfoEnabled()) {
this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources =(Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
//获取输入流,构造inputsource
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//实例化输入流
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
//对resource进行编码
inputSource.setEncoding(encodedResource.getEncoding());
}
//获取document (核心)
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
根据上面的函数我们可以看到他的加载过程,
看到这里是不是觉得前面讲好多没必要的东西!那我现在在屡一下逻辑
1.首先我们需要一个resource参数做封装
2.对resource进行编码处理
3.通过sax读取xml文件凡是来准备inputsource对象
4.把前面准备好的inputsource出入核心函数doLoadBeanDefinitions中,
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//1.加载xml文件,得到对应的document对象
Document doc = this.doLoadDocument(inputSource, resource);
//2.根据document注册bean信息
return this.registerBeanDefinitions(doc, resource);
//3.下面异常是验证xml的格式是否正确
} catch (BeanDefinitionStoreException var4) {
throw var4;
} catch (SAXParseException var5) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
} catch (SAXException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
} catch (ParserConfigurationException var7) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
} catch (IOException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
} catch (Throwable var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
}
}
上面的三个步骤都很重要,那么这里又引申出一个重要的点,那就是xml的验证。
2.1 xml的验证码模式
xml格式分为两种 DTD 和 XSD
DTD(document type definition)文档类型定义:xml月数模式语言的一种,是xml文件的验证机制,可通过比较xml文档和DTD文件;哎看文件是否符合规范,元素和标签是否正确,一个DTD文档包含元素的定义规则,元素减关系的定义规则,元素的可使用性,可使用的实体或符号规则,
例如:在document标签中声明为.dtd,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huimi.core.business.dao.BizAccountDao">
<resultMap type="com.huimi.core.business.entity.BizAccount" id="BizAccountMap">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="uuid" column="uuid" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
</mapper>
2.2 XSD(XML schemas definition)可描述 XML 文档的结构:
什么是xml schema:XML Schema 的作用是定义 XML 文档的合法构建模块,类似 DTD。
定义可出现在文档中的元素
定义可出现在文档中的属性
定义哪个元素是子元素
定义子元素的次序
定义子元素的数目
定义元素是否为空,或者是否可包含文本
定义元素和属性的数据类型
定义元素和属性的默认值以及固定值
xml schema描述了xml文档的结构。可以用指定的xml schema来验证某个xml文档,已检查xml文档是否符合其要求,xml schema 本身是一个xml 文档,他符合xml语法结构,可以用通用的xml 解析器来解析它。
在使用xml schema文档对xml实例进行校验,处理要声明空间外,还需要指定该名称空间所对应的存储位置,通过 schemalocation属性来指定名称空间所对应的xml schema文档的存储位置,他包含两部分,
1.命名空间的URI,
2.名称空间所标识的xml schema文件位置或者URL地址(xsi:noNamespaceSchemaLocation=“shiporder.xsd”)
代码如下:
<?xml version="1.0" encoding="ISO-8859-1"?>
<shiporder orderid="889923"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="shiporder.xsd">
<orderperson>George Bush</orderperson>
<shipto>
<name>John Adams</name>
<address>Oxford Street</address>
<city>London</city>
<country>UK</country>
</shipto>
<item>
<title>Empire Burlesque</title>
<note>Special Edition</note>
<quantity>1</quantity>
<price>10.90</price>
</item>
<item>
<title>Hide your heart</title>
<quantity>1</quantity>
<price>9.90</price>
</item>
</shiporder>
了解了DTD和XSD的区别之后我们再去分析Spring中对验证模式的提取就稍微容易理解一点了,通过之前的分析我们往下找了一下,找到了getValidationModeForResourcef函数,这个函数是spring获取对应资源的验证模式,
return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
protected int getValidationModeForResource(Resource resource) {
//默认等于1
int validationModeToUse = this.getValidationMode();
//手动设置指定凡是验证模式
if (validationModeToUse != 1) {
return validationModeToUse;
} else {
//默认自动检测
int detectedMode = this.detectValidationMode(resource);
return detectedMode != 1 ? detectedMode : 3;
}
}
//自动检测
protected int detectValidationMode(Resource resource) {
if (resource.isOpen()) {
throw new BeanDefinitionStoreException("Passed-in Resource [" + resource + "] contains an open stream: cannot determine validation mode automatically. Either pass in a Resource that is able to create fresh streams, or explicitly specify the validationMode on your XmlBeanDefinitionReader instance.");
} else {
InputStream inputStream;
try {
inputStream = resource.getInputStream();
} catch (IOException var5) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: cannot open InputStream. Did you attempt to load directly from a SAX InputSource without specifying the validationMode on your XmlBeanDefinitionReader instance?", var5);
}
try {
return this.validationModeDetector.detectValidationMode(inputStream);
} catch (IOException var4) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", var4);
}
}
}
自动检测的流程是调用detectValidationMode函数,detectValidationMode函数中获取resource的输入流之后出入到XmlValidationModeDetector类的detectValidationMode函数中,代码如下:
protected int detectValidationMode(Resource resource) {
if (resource.isOpen()) {
throw new BeanDefinitionStoreException("Passed-in Resource [" + resource + "] contains an open stream: cannot determine validation mode automatically. Either pass in a Resource that is able to create fresh streams, or explicitly specify the validationMode on your XmlBeanDefinitionReader instance.");
} else {
InputStream inputStream;
try {
inputStream = resource.getInputStream();
} catch (IOException var5) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: cannot open InputStream. Did you attempt to load directly from a SAX InputSource without specifying the validationMode on your XmlBeanDefinitionReader instance?", var5);
}
try {
return this.validationModeDetector.detectValidationMode(inputStream);
} catch (IOException var4) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", var4);
}
}
}
XmlValidationModeDetector类
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
byte var4;
try {
boolean isDtdValidated = false;
while(true) {
String content;
if ((content = reader.readLine()) != null) {
content = this.consumeCommentTokens(content);
//跳过注释或者空字段内容
if (this.inComment || !StringUtils.hasText(content)) {
continue;
}
if (this.hasDoctype(content)) {
isDtdValidated = true;
//读取到内容以<符号开始 ,验证模式一定会在开始符号之前
} else if (!this.hasOpeningTag(content)) {
continue;
} }
int var5 = isDtdValidated ? 2 : 3;
return var5;
}
} catch (CharConversionException var9) {
var4 = 1;
} finally {
reader.close();
}
return var4;
}
private boolean hasDoctype(String content) {
return content.contains("DOCTYPE");
}
private boolean hasOpeningTag(String content) {
if (this.inComment) {
return false;
} else {
int openTagIndex = content.indexOf(60);
return openTagIndex > -1 && content.length() > openTagIndex + 1 && Character.isLetter(content.charAt(openTagIndex + 1));
}
}
这个方法是来验证码xml的格式是xsd还是DTD的,没其他的内容了。
验证模式准备好了就可以加载document了,下面的loadDocument就是加载document了,
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
-------------------------------------------------------------------
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); }
DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
下面就是实现的具体代码了,他用sax方法去解析xml,先创建一个documentbuilderfactory,在通过documentbuilderfactory创建documentbuilder,在对inputsource解析后返回document对象,这里需要提一下参数this.getEntityResolver(),
他解释说:如果sax应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setentityresolver方法先sax驱动注册一个实例。意思是说对于解析一个xml,sax首先读取该xml文档删的声明,根据声明去寻找想要的DTD定义,一百年对文档进行一个验证,默认的寻找规则,通过网络来下载相应的DTD声明,并进行认证,下载的过程很漫长,而且网络不能中断,这里可能会抛异常,就是因为DTD没有找到声明的原因。但是我们可以吧DTD的声明放在项目的某一个地方,这样就避免了去查找声明了。
这个类中有一个抽象方法,代码如下:
public abstract InputSource resolveEntity (String publicId,
String systemId)
throws SAXException, IOException;
}
他这里接收了两个参数 publicId,systemId,这两个参数就是来区分xsd和dtd文件的
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.springframework.org/schema/beans/spring-beans.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
例1:xsd文件
publicId:null
publicId:http://www.springframework.org/schema/beans/spring-beans.xsd
例2:dtd文件
publicId:null
publicId:http://mybatis.org/dtd/mybatis-3-mapper.dtd
接下来我们要做的就是解析和注册了,上面我们吧文件转换成了document对象后,需要提取document和在spring中注册。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//实例化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
//设置环境变量
int countBefore = this.getRegistry().getBeanDefinitionCount();
//加载注册bean
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
//记录本次加载的BeanDefinition个数
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
这里的Document doc参数是前面加载转换出来的document对象。而他真正的处理是交给BeanDefinitionDocumentReader类,类中有个接口,具体实现是在DefaultBeanDefinitionDocumentReader类中,代码如下:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
this.logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
//注册操作(核心)
this.doRegisterBeanDefinitions(root);
}
//底层实现代码
protected void doRegisterBeanDefinitions(Element root) {
//处理profile属性
BeanDefinitionParserDelegate parent = this.delegate;
//处理解析
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute("profile");
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
}
return;
}
}
}
//解析前处理,方法没有做实现,具体实现留在子类中
this.preProcessXml(root);
this.parseBeanDefinitions(root, this.delegate);
//解析后处理,方法没有做实现,具体实现留在子类中
this.postProcessXml(root);
this.delegate = parent;
}
根据上面的代码我们可以看到他的实现过程,但是有两处的方法都是空的,这就很有意思了,都说不写无用的代码,每句代码都是有他存在的意义,那么他存在肯定是有意义的,那么为什么方法中没有具体实现呢,这个就要提一下java的的一个点了,面向对象,面向继承,如果子类继承了他,只要重写他的方法,就可以根据当前的需求做具体的实现了。上面代码中是不是发现一个疑问点,那就是prolife(侧面描述)变量,profile,这个属性是用来提取xml配置的profile属性的值,根据这个值去加载对应的配置文件,这个在web集成部署的时候方便切换开发和生产环境,我现在开发项目的时候基本是这样的,这样真的很方便。处理了profile户就可以进行xml读取了,跟踪代码进入~~~
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//处理bean 自定义
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
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)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
//默认
delegate.parseCustomElement(root);
}
}
这里的两种方式是有区别的,一个是默认的,一个是需要自己定义的,我们都知道默认的不需要我们手动操作什么,如果是自定义的,那么就需要手动增加一些配置信息了,比如说我们要配置事物,就需要用到tx标签等,
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd "
>
加载的时候如果获取的空间命名为:http://www.springframework.org/schema/beans则为默认的
下面是对不同标签的处理方案
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
我们先看一个的实现原理,其他的理解就比较容易了
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//解析id属性
String id = ele.getAttribute("id");
//解析name属性
String nameAttr = ele.getAttribute("name");
List<String> aliases = new ArrayList();
//分割name属性
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(id) && !aliases.isEmpty()) {
beanName = (String)aliases.remove(0);
if (this.logger.isDebugEnabled()) {
this.logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
this.checkNameUniqueness(beanName, aliases, ele);
}
AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
//如果不存在 beanName那么根据spring中提供的命名规则为当前的bean生成对应的beanname
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
}
} catch (Exception var9) {
this.error(var9.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} else {
return null;
}
}
这个方法开始没怎么看懂,但是有个点是清楚的,那就是解析xml属性,转换成bean的属性,bdHolder 这个对象中就有id,class,beanName,aliases等属性,在解析xml后,自动填充属性值,bdHolder不为null时,若存在默认标签的子节点下还存在自定义属性,还需要decorateBeanDefinitionIfRequired方法去解析,解析完成后,需要对bdholder进行注册,这个操作在registerBeanDefinition实现了,最后我们就是给spring发送通知,告诉他这个bean已经加载完成了,呼,是不是觉得一个很简单的东西,到这来看着就不是那么简单了。
思路大致是这样的:
1.DefaultBeanDefinitionDocumentReader 获取并装载xml文件
2.获取BeanDefinitionHolder 实例对象,
3.在对BeanDefinitionHolder 进行注册
4.通知spring,bean已经加载并注册完毕
上面有一个部分是画了红线的部分,我需要了解一下,我第一眼看的时候 以为我看错类了,因为从DefaultBeanDefinitionDocumentReader进到BeanDefinitionParserDelegate也是这个方法,代码如下:
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
//解析class属性
if (ele.hasAttribute("class")) {
className = ele.getAttribute("class").trim();
}
try {
String parent = null;
//解析parent属性
if (ele.hasAttribute("parent")) {
parent = ele.getAttribute("parent");
}
//创建一个可以承载属性的 AbstractBeanDefinition
AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
//解析bean的各种属性(硬编码)
this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
//解析元数据
this.parseMetaElements(ele, bd);
//解析Lookup-method属性
this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析Replaced-method属性
this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析构造函数参数
this.parseConstructorArgElements(ele, bd);
//解析property元素
this.parsePropertyElements(ele, bd);
//解析qualificer元素
this.parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(this.extractSource(ele));
AbstractBeanDefinition var7 = bd;
return var7;
} catch (ClassNotFoundException var13) {
this.error("Bean class [" + className + "] not found", ele, var13);
} catch (NoClassDefFoundError var14) {
this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
} catch (Throwable var15) {
this.error("Unexpected failure during bean definition parsing", ele, var15);
} finally {
this.parseState.pop();
}
return null;
}
这样bean标签的所有属性都解析都列出来了,but,好需要进一步解析
1.第一步需要一个实例来装载解析出来的属性,不然解析出来没地方放那就**了。
AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
这里是创建AbstractBeanDefinition 实例的过程
public static AbstractBeanDefinition createBeanDefinition(String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
} else {
bd.setBeanClassName(className);
}
}
return bd;
}
2.解析各种属性
this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute("singleton")) {
this.error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
} else if (ele.hasAttribute("scope")) {
bd.setScope(ele.getAttribute("scope"));
} else if (containingBean != null) {
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute("abstract")) {
bd.setAbstract("true".equals(ele.getAttribute("abstract")));
}
String lazyInit = ele.getAttribute("lazy-init");
if ("default".equals(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit("true".equals(lazyInit));
String autowire = ele.getAttribute("autowire");
bd.setAutowireMode(this.getAutowireMode(autowire));
String dependencyCheck = ele.getAttribute("dependency-check");
bd.setDependencyCheck(this.getDependencyCheck(dependencyCheck));
String autowireCandidate;
if (ele.hasAttribute("depends-on")) {
autowireCandidate = ele.getAttribute("depends-on");
bd.setDependsOn(StringUtils.tokenizeToStringArray(autowireCandidate, ",; "));
}
autowireCandidate = ele.getAttribute("autowire-candidate");
String destroyMethodName;
if (!"".equals(autowireCandidate) && !"default".equals(autowireCandidate)) {
bd.setAutowireCandidate("true".equals(autowireCandidate));
} else {
destroyMethodName = this.defaults.getAutowireCandidates();
if (destroyMethodName != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(destroyMethodName);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
if (ele.hasAttribute("primary")) {
bd.setPrimary("true".equals(ele.getAttribute("primary")));
}
if (ele.hasAttribute("init-method")) {
destroyMethodName = ele.getAttribute("init-method");
if (!"".equals(destroyMethodName)) {
bd.setInitMethodName(destroyMethodName);
}
} else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
if (ele.hasAttribute("destroy-method")) {
destroyMethodName = ele.getAttribute("destroy-method");
bd.setDestroyMethodName(destroyMethodName);
} else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
if (ele.hasAttribute("factory-method")) {
bd.setFactoryMethodName(ele.getAttribute("factory-method"));
}
if (ele.hasAttribute("factory-bean")) {
bd.setFactoryBeanName(ele.getAttribute("factory-bean"));
}
return bd;
}
上面是spring 解析xml中常见的属性和一些不常用的属性 如果你们有兴趣的话 可以自己去看看对应的方法,下面几个方法都是用来解析其他元素,但是都是有正对性的提取和解析属性。
上面这部分是 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 这一段的内容,可能是有点多,有些也没讲的很全面,因为其他解析的原理都大致相同,
那下面我们在看看这个 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);,当前面不为空的时候 执行的是这个函数,这个函数的意思是:当Spring中的bean使用的是默认的标签配置时,但是其中的子元素使用了自定义的配置时,执行该函数,也就是这个函数是用来解析自定义类型配置的,
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
return this.decorateBeanDefinitionIfRequired(ele, definitionHolder, (BeanDefinition)null);
}
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = definitionHolder;
NamedNodeMap attributes = ele.getAttributes();
for(int i = 0; i < attributes.getLength(); ++i) {
Node node = attributes.item(i);
finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd);
}
NodeList children = ele.getChildNodes();
for(int i = 0; i < children.getLength(); ++i) {
Node node = children.item(i);
if (node.getNodeType() == 1) {
finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
public BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
String namespaceUri = this.getNamespaceURI(node);
if (!this.isDefaultNamespace(namespaceUri)) {
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
}
if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
return originalDef;
}
上面是针对自定义的属性解析,不过这里有点不同了,这里是首先获取属性或者元素的空间命名,以此来判断该元素户这话属性是否适用于自定义标签的解析条件,找出自定义类型所对应的namespaceHandler并进行进一步解析,
那么xml配置文件解析就解释到这来了,有兴趣的朋友可以去看看相关的文章。
前面做的都是为了后续的工作做准备,那么前面的内容已经准备好了,剩下的就是把解析好的数据注册到spring中了。
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
//获取beanName 作为注册的唯一标识
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//注册所以的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
registry.registerAlias(beanName, alias);
}
}
}
上面源码中科院看出,注册有两种方式
1.beanName 注册 beandefinition 源码如下
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//注册前校验AbstractBeanDefinition的methodoverrides校验
校验methodoverride是否与工厂犯法并存或者不存在
((AbstractBeanDefinition)beanDefinition).validate();
} catch (BeanDefinitionValidationException var9) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);
}
}
BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
//处理注册已经注册的beanname情况
if (oldBeanDefinition != null) {
//如果对于的beanname已经注册而且在配置中配置了bean不允许覆盖,则抛出异常
if (!this.isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound.");
}
if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
} else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
if (this.hasBeanCreationStarted()) {
Map var4 = this.beanDefinitionMap;
//在多线程中可能存在不同步的的问题 特此加同步锁
synchronized(this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
} else {
//记录beanname
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
//注册beandefinition
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || this.containsSingleton(beanName)) {
//重置所有beanname对于的缓存
this.resetBeanDefinition(beanName);
}
}
用beanName注册也就是把beanN作为注册的key,根据上面的源码来看 是这样的,不过除此之外,还做了其他的,
1).abstractbeandefinition 的属性校验
2).对beanname已经注册的情况处理,如果有设置不允许覆盖则抛异常,没有直接覆盖
3).加入了map缓存
4).清除解析钱留下的对应beanname的缓存
2.别名注册beandefinition
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
Map var3 = this.aliasMap;
synchronized(this.aliasMap) {
//alias 和 beanname相同则不记录并删除对应的alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
} else {
//如果alias不允许呗覆盖则抛出异常
String registeredName = (String)this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
return;
}
if (!this.allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
}
}
//校验 当A->B 存在是,若再次出现A->C->B的时候回抛出异常,可以看源码
this.checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
}
在了解前面的注册原理之后,理解别名注册就简单多了,
1)alias与beanname相同情况时的处理,若alias与beanname并名称相同则不需要处理并删除原有的alias,
2)alias覆盖处理,若aliasName已经用并已经指向了另一个beanname则需要用户的设置进行处理
3)alias循环检查,当A->B存时,若再次出现A->C->B的时候回抛出异常
4)最后 注册 alias
那注册完了,就需要通知监听器,让spring知道,注册已经完成了。但是里面并没有做任何的逻辑处理,这里只是做了扩展,当开发的时候有特定的需要可以将其进行扩展,进行相应的逻辑处理。
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
public class EmptyReaderEventListener implements ReaderEventListener {
public void componentRegistered(ComponentDefinition componentDefinition) {
}
}