(一)Spring - beans 的两个核心类
准备工作
-
安装gradle
-
拉取Spring源码:
git clone git://github.com/SpringSource/Spring-framework.git
容器基本用法
- bean
public class HelloWorldTest {
public String hello = "Hello World";
public String getHello() {
return hello;
}
public void setHello(String hello) {
this.hello = hello;
}
}
- 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloWorldBean" class="test.HelloWorldBean"></bean>
</beans>
- App逻辑关联代码
@SuppressWarnings("deprecation")
public void testLoadProcedure(){
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
HelloWorldBean helloWorld = (HelloWorldBean) beanFactory.getBean("helloWorldBean");
System.out.println(helloWorld.getHello());
}
首先了解 beans 的两个核心类
1. 核心类一:DefaultListableBeanFactory
DefaultListableBeanFactory容器加载相关类图-
AliasRegistry 定义对alias的简单增删改等操作
void registerAlias(String name, String alias); void removeAlias(String alias); boolean isAlias(String name); String[] getAliases(String name);
-
BeanDefinitionRegistry 定义对BeanDefinition的各种增删改等操作
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition); void removeBeanDefinition(String beanName); BeanDefinition getBeanDefinition(String beanName); boolean containsBeanDefinition(String beanName); String[] getBeanDefinitionNames(); int getBeanDefinitionCount(); boolean isBeanNameInUse(String beanName);
-
SimpleAliasRegistry 使用map作为alias的缓存,实现 AliasRegistry
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
-
SingletonBeanRegistry 定义对单例的注册及获取
void registerSingleton(String beanName, Object singletonObject); Object getSingleton(String beanName); boolean containsSingleton(String beanName); String[] getSingletonNames(); int getSingletonCount(); Object getSingletonMutex(); //返回此注册表使用的单例互斥锁(适用于外部协作者)
-
DefaultSingletonBeanRegistry 实现SingletonBeanRegistry各方法
-
FactoryBeanRegistrySupport 在DefaultSingletonBeanRegistry 基础上新增对FactoryBean的处理
-
注意区分FactoryBean和BeanFactory
FactoryBean: Interface to be implemented by objects used within a BeanFactory which are themselves factories for individual objects.If a bean implements this interface(FactoryBean<T>), it is used as a factory for an object to expose, not directly as a bean instance that will be exposed itself.
NB: A bean that implements this interface cannot be used as a normal bean.A FactoryBean is defined in a bean style, but the object exposed for bean references ({getObject()}) is always the object that it creates// <T> the bean type T getObject() throws Exception;
-
BeanFactory即这个bean是工厂,作用就是配置、新建、管理各种Bean
-
FactoryBean即这个bean是工厂类型的,我们通过 getBean(xxxFactoryBean) 获得是该工厂xxxFactoryBean所产生的 T 的实例,而不是 xxxFactoryBean 自身的实例。想要获取到 xxxFactoryBean 本身的实例,在名称前加“&”
-
-
BeanFactory 定义获取bean及bean的各种属性
The root interface for accessing a Spring bean container.This interface is implemented by objects that hold a number of bean definitions, each uniquely identified by a String name. -
HierarchicalBeanFactory 定义在BeanFactory基础上增加对parentFactory的支持
//Return the parent bean factory, or {@code null} if there is none. BeanFactory getParentBeanFactory();
-
ConfigurableBeanFactory 定义提供配置Factory的各个方法
-
ListableBeanFactory 定义提供根据各种条件获取bean的配置清单
boolean containsBeanDefinition(String beanName); int getBeanDefinitionCount(); String[] getBeanDefinitionNames(); String[] getBeanNamesForType(ResolvableType type); String[] getBeanNamesForType(@Nullable Class<?> type); String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit); <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException; String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType); Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType); <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType);
-
AbstractBeanFactory 综合ConfigurableBeanFactory和FactoryBeanRegistrySupport
-
AutowireCapableBeanFactory 定义 提供创建bean、自动注入、初始化及应用bean后的处理器
<T> T createBean(Class<T> beanClass); void autowireBean(Object existingBean); Object configureBean(Object existingBean, String beanName); Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck); Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck); void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck); void applyBeanPropertyValues(Object existingBean, String beanName); Object initializeBean(Object existingBean, String beanName); Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName); Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName); void destroyBean(Object existingBean); <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType); Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName); Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter);
-
AbstractAutowireCapableBeanFactory综合AbstractBeanFactory并对AutowireCapableBeanFactory进行实现。
-
ConfigurableListableBeanFactory 定义 BeanFactory配置清单,指定忽略类型及接口等
void ignoreDependencyType(Class<?> type); void ignoreDependencyInterface(Class<?> ifc); void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue); boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor); BeanDefinition getBeanDefinition(String beanName); Iterator<String> getBeanNamesIterator(); void clearMetadataCache(); void freezeConfiguration(); // 不可修改 Configuration boolean isConfigurationFrozen(); void preInstantiateSingletons(); // 确认非延迟的已实例化,同样也要考虑到FactoryBeans。
-
DefaultListableBeanFactory 综合上述所有功能,主要是对bean注册后的处理。
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {...} public boolean isAllowBeanDefinitionOverriding() {...} public void setAllowEagerClassLoading(boolean allowEagerClassLoading) {...} public void setDependencyComparator(@Nullable Comparator<Object> dependencyComparator) {...} public Comparator<Object> getDependencyComparator() {...} public AutowireCandidateResolver getAutowireCandidateResolver() {...}
XmlBeanFactory (Deprecated) 对DefaultListableBeanFactory进行拓展,新增个性化实现:XmlBeanDefinitionReader类型的reader属性,主要用于从XML文档中读取BeanDefinition,对于注册和获取bean依然使用父类方法。
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
2.核心类二:XmlBeanDefinitionReader
XmlBeanDefinitionReader配置文件读取相关类图.pngXmlBeanDefinitionReader中包含DocumentLoader、BeanDefinitionDocumentReader 对应属性值:
private DocumentLoader documentLoader = new DefaultDocumentLoader();
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
DefaultBeanDefinitionDocumentReader.class;
AbstractBeanDefinitionReader中包含ResourceLoader resourceLoader属性:
@Nullable
private ResourceLoader resourceLoader;
-
ResourceLoader 定义资源加载器。根据给定的资源文件地址返回对应的Resource
Resource getResource(String location); @Nullable ClassLoader getClassLoader(); // 可以直接通过ResourceLoader获取到ClassLoader
-
BeanDefinitionReader 定义资源文件读取并转换为BeanDefinition的各个功能
BeanDefinitionRegistry getRegistry(); @Nullable ResourceLoader getResourceLoader(); @Nullable ClassLoader getBeanClassLoader(); BeanNameGenerator getBeanNameGenerator(); int loadBeanDefinitions(Resource resource); int loadBeanDefinitions(Resource... resources); int loadBeanDefinitions(String location); int loadBeanDefinitions(String... locations);
-
EnvironmentCapable 定义获取Environment方法
Environment getEnvironment();
-
AbstractBeanDefinitionReader 对 BeanDefinitionReader 和 EnvironmentCapable 的实现
-
DocumentLoader 定义从Resource转换为Document的功能
Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
-
BeanDefinitionDocumentReader 定义读取Document并注册BeanDefinition,例如默认实现DefaultBeanDefinitionDocumentReader:
@Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); }
-
BeanDefinitionParserDelegate 解析Element的各种方法
简言之,XmlBeanDefinitionReader
- 通过继承 AbstractBeanDefinitionReader 中的方法,使用 ResourceLoader 读取资源对象Resource
- 通过 DocumentLoader 将Resource文件转换为Document文件
- 通过 DefaultBeanDefinitionDocumentReader 类对Document进行解析,通过使用BeanDefinitionParserDelegate
那么来捋一下BeanFactory初始化过程,
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
BeanFactory初始化时序图.png
Resource资源封装
根据时序图步骤1,可以看到是通过 ClassPathResource 进行Resource资源封装。Java 将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来实现不同的资源读取逻辑,一般handler的类型通过不同前缀识别(file: http: jar: .etc)。而Spring对内部使用到的资源实现了抽象结构:Resource接口封装底层资源。
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
boolean exists(); // 存在性
boolean isReadable();// 可读性
boolean isOpen(); // 当前是否打开状态
URL getURL() throws IOException;// 资源→URL
URI getURI() throws IOException;// 资源→URI
File getFile() throws IOException;// 资源→File
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String var1) throws IOException; // 基于当前资源创建一个相对资源
String getFilename(); // 不带路径信息的文件名
String getDescription();// 详细打印出错的资源文件信息
}
对于不同来源的资源文件都有对应的Resource实现,文件、ClassPath资源、URL资源、InputStream资源、Byte数组资源等:
Resource资源文件关系图.png通过Resource相关类对配置文件封装后(完成时序图步骤2),读取工作就交由XmlBeanDefinitionReader处理。进行时序图步骤3:XmlBeanFactory初始化。可以看出来真正执行的是第二个构造器。
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);资源加载的真正实现,在分析资源加载之前,需要关注一点super(parentBeanFactory); 跟踪 super() 至 AbstractAutowireCapableBeanFactory 的 public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) 构造器:
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);
}
public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
this();
this.setParentBeanFactory(parentBeanFactory);
}
这里的ignoreDependencyInterface方法能够直接忽略给定接口的自动装配功能,为什么要忽略呢?
官方解释:
This will typically be used by application contexts to register dependencies that are resolved in other ways, like IOjbectFactory through IObjectFactoryAware or IApplicationContext through IApplicationContextAware. By default, IObjectFactoryAware and IObjectName interfaces are ignored. For further types to ignore, invoke this method for each type.
A{B},当Spring获取A的Bean时,如果B还未初始化,Spring会自动初始化B。但是如果B实现了BeanNameAware接口,B将不会被初始化。自动装配时忽略给定的依赖接口吗,通常被用来以其他方式解析Application上下文依赖,类似于 BeanFactory 通过 BeanFactoryAware 注入,ApplicationContext 通过 ApplicationContextAware注入。
我们继续关注 reader 如何加载bean。
加载Bean
通过 XmlBeanDefinitionReader 的reader属性,loadBeanDefinitions方法,加载整个资源resource,如下
1.
this.reader.loadBeanDefinitions(resource);
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
1.1 1.2
public EncodedResource(Resource resource) {
this(resource, null, null);
}
1.3
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
// 通过属性来记录已经加载的资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
...
try {
1.3.1 ~ 1.3.4 // 获取到Resource的InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
1.3.5 ~ 1.3.6 // InputSource 并不是Spring的 包名:package org.xml.sax;
InputSource inputSource = new InputSource(inputStream);
...
1.3.7 // 逻辑核心
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
...
}
...
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource); // 关注【获取Document】
int count = registerBeanDefinitions(doc, resource); // 关注 【解析并注册BeanDefinitions】分析
...
1.3.8
return count;
}
...
}
loadBeanDefinitions时序图.png
- 用 EncodedResource 对 Resource 进行封装。顾名思义,和 Resource 编码处理相关。主要体现在getReader(),构造相应的编码属性。
private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) {
super();
Assert.notNull(resource, "Resource must not be null");
this.resource = resource;
this.encoding = encoding;
this.charset = charset;
}
public Reader getReader() throws IOException {
if (this.charset != null) {
return new InputStreamReader(this.resource.getInputStream(), this.charset);
}
else if (this.encoding != null) {
return new InputStreamReader(this.resource.getInputStream(), this.encoding);
}
else {
return new InputStreamReader(this.resource.getInputStream());
}
}
- 获取输入流。根据封装好的 EncodedResource 对象获取Resource 的 InputStream,并构造为 InputSource。
public class InputSource {
private String publicId;
private String systemId;
private InputStream byteStream;
private String encoding;
private Reader characterStream;
... //省略 get set方法
}
- 调用 doLoadBeanDefinitions 。实际是做了三件事情,且必不可少。
① 获取对XML文件的验证模式
② 加载XML文件,并得到对应的Document
③ 根据返回的Document注册Bean信息
1
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
return VALIDATION_XSD;
}
2
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
3
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
简述过程就是:先对传入的resource做 EncodedResource 封装(是考虑到Resource存在编码要求),然后通过SAX读取XML文件(InputStream)准备InputSource对象,最后数据传入核心逻辑doLoadBeanDefinitions。继续学习这三个过程:
① 获取XML文件的验证模式
DTD(Document Type Definition)
- 即文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。
- 比较XML文件和DTD文件看XML文档是否符合规范,元素和标签使用是否正确。
- DTD包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体/符号规则。
- 使用DTD验证模式需要在XML文件中添加头部声明:
<?xml version = "1.0" encoding="GB2312" standalone = "no"?>
<!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN2.0//EN" "http://www.Springframework.org/dtd/Spring-beans-2.0.dtd">
<!-- 引用语法 -->
<!DOCTYPE 根元素名 PUBLIC “DTD名称” "DTD文件的URL">
Spring-beans-2.0.dtd 如下:
<!ELEMENT beans (
description?,
(import | alias | bean)*
)>
<!ATTLIST beans default-lazy-init (true | false) "false">
<!ATTLIST beans default-merge (true | false) "false">
<!ATTLIST beans default-autowire (no | byName | byType | constructor | autodetect) "no">
<!ATTLIST beans default-init-method CDATA #IMPLIED>
<!ATTLIST beans default-destroy-method CDATA #IMPLIED>
...
XSD(XML Schemas Definition)
- 即XML 模式定义。XML Schemas描述XML文档的结构
- 通过XML Schema指定一个XML文档所允许的结构和内容(可以说是检验文档的有效性)。
- XML Schema本身就是XML文档
- 使用XML Schema:
- 需要声明命名空间 (xsd:schema xmlns="http://www.springframework.org/schema/beans")
- 指定该命名空间所对应的XML Schema文档的存储位置(xsi:schemaLocation 由URI+URL组成)
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
Spring-beans-3.0.xsd 如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.springframework.org/schema/beans"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.springframework.org/schema/beans">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
...
]]></xsd:documentation>
</xsd:annotation>
<!-- base types -->
<xsd:complexType name="identifiedType" abstract="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a bean. The scope of the identifier
is the enclosing bean factory.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a bean. A bean id may not be used more than once
within the same <beans> element.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
...
</xsd:schema>
读取验证模式
protected int getValidationModeForResource(Resource resource) {
// 如果手动指定验证模式就使用
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
// 如果未指定则自动检测resource 如果存在DOCTYPE则使用VALIDATION_DTD,否则VALIDATION_AUTO
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
return VALIDATION_XSD;
}
public int getValidationMode() {
return this.validationMode;
}
protected int detectValidationMode(Resource resource) {
...
InputStream inputStream;
try {
inputStream = resource.getInputStream();
return this.validationModeDetector.detectValidationMode(inputStream);
}
...
}
自动检测转交至专门处理类 XmlValidationModeDetector (Detects whether an XML stream is using DTD- or XSD-based validation.):
public int detectValidationMode(InputStream inputStream) throws IOException {
// Peek into the file to look for DOCTYPE.
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
boolean isDtdValidated = false;
String content;
while ((content = reader.readLine()) != null) {
content = consumeCommentTokens(content);
if (this.inComment || !StringUtils.hasText(content)) { // 跳过注释行或空行
continue;
}
if (hasDoctype(content)) { // 检测包含DOCTYPE字样
isDtdValidated = true;
break;
}
if (hasOpeningTag(content)) { // 存在‘<’ 并且有内容时就无须继续查数据,验证模式DOCTYPE关键字在校验开始符号之前便会出现。
// End of meaningful data...
break;
}
}
return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
}
catch (CharConversionException ex) {
return VALIDATION_AUTO;
}
finally {
reader.close();
}
}
private boolean hasDoctype(String content) {
return content.contains(DOCTYPE);
}
private boolean hasOpeningTag(String content) {
if (this.inComment) {
return false;
}
int openTagIndex = content.indexOf('<');
return (openTagIndex > -1 && (content.length() > openTagIndex + 1) &&
Character.isLetter(content.charAt(openTagIndex + 1)));
}
private static final String DOCTYPE = "DOCTYPE";
② 获取Document
获取XML验证模式后,即可进行 Document 加载,XMLBeanDefinitionReader 转交给 DocumentLoader 的实现类 DefaultDocumentLoader来完成
XMLBeanDefinitionReader :
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
// Determine default EntityResolver to use.
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
} else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
DefaultDocumentLoader :
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
- 解析一个XML,SAX首先读取该XML文档上的声明,根据声明寻找相应的DTD。
- EntityResolver 作用:项目本身就可以提供一个如何寻找DTD声明的方法,而不必去网络上下载。
EntityResolver
public abstract InputSource resolveEntity (String publicId, String systemId)
- 接收 publicId 和 systemId 返回 InputSource 对象,例如验证模式为 XSD 的 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
...
</beans>
- 读取到的两个参数:
- publicId:null
- systemId :http://www.springframework.org/schema/beans/spring-beans.xsd
- 再看验证模式为 DTD 的 xml 文件配置:
<?xml version = "1.0" encoding="GB2312" standalone = "no"?>
<!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN2.0//EN" "http://www.Springframework.org/dtd/Spring-beans-2.0.dtd">
- 读取到的两个参数:
- publicId:-//Spring//DTD BEAN2.0//EN
- systemId :http://www.Springframework.org/dtd/Spring-beans-2.0.dtd
验证文件的默认加载方式是从网络下载,用户体验不好,一般都将验证文件放置在自己的项目之中。将URL转换为自己工程里对应的地址文件,而 DelegatingEntityResolver 作为 EntityResolver 实现类:
public InputSource resolveEntity(String publicId, @Nullable String systemId) throws SAXException, IOException {
if (systemId != null) {
if (systemId.endsWith(DTD_SUFFIX)) {
return this.dtdResolver.resolveEntity(publicId, systemId);
}
else if (systemId.endsWith(XSD_SUFFIX)) {
return this.schemaResolver.resolveEntity(publicId, systemId);
}
}
return null;
}
根据不同验证模式有不同的解析器解析。
- 加载DTD类型的BeansDtdResolver的resolveEntity方法是直接截取到systemId的xxx.dtd去当前路径下寻找
if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
int lastPathSeparator = systemId.lastIndexOf('/');
int dtdNameStart = systemId.indexOf(DTD_NAME, lastPathSeparator);
if (dtdNameStart != -1) {
String dtdFile = DTD_NAME + DTD_EXTENSION;
try {
Resource resource = new ClassPathResource(dtdFile, getClass());
InputSource source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
if (logger.isTraceEnabled()) {
logger.trace("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
}
return source;
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", ex);
}
}
}
}
- 加载XSD类型的PluggableSchemaResolver的resolveEntity方法是默认到"META-INF/spring.schemas"文件中寻找systemId对应的XSD文件并加载
public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
若未声明,则去默认地址"META-INF/spring.schemas"下找寻
if (systemId != null) {
String resourceLocation = getSchemaMappings().get(systemId);
if (resourceLocation != null) {
Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
try {
InputSource source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
if (logger.isTraceEnabled()) {
logger.trace("Found XML schema [" + systemId + "] in classpath: " + resourceLocation);
}
return source;
}
catch (FileNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find XML schema [" + systemId + "]: " + resource, ex);
}
}
}
}
③ 解析并注册BeanDefinitions
根据②中获取到的Document,接下来重点:提取并注册bean
// XmlBeanDefinitionReader :
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 1. 实例化DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 2. 记录注册前 BeanDefinition 的个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 3. 加载及注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 4. 返回本次注册beanDefinition数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
1.
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
DefaultBeanDefinitionDocumentReader.class;
// DefaultBeanDefinitionDocumentReader:
3.
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
// 专门处理解析类
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
// 处理 profile 属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 解析前处理(留给子类实现)
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
// 解析前处理(留给子类实现)
postProcessXml(root);
this.delegate = parent;
}
- 对 profile 进行处理
- 根据 BeanDefinitionParserDelegate (delegate) 对 Element (root) 进行解析及注册
profile 属性使用
...
<beans profile="dev">
...
</beans>
<beans profile="product">
...
</beans>
集成到Web环境中,在web.xml加入:
<context-param>
<param-name>Spring.profiles.active</param-name>
<param-name>dev</param-name>
</context-param>
有了这个特性我们可以在配置文件中部署两套配置分别适用于开发、生产环境。
程序获取beans节点并检测是否定义了profile属性,如果存在则去环境变量中寻找(environment),并对profiles进行拆分,解析每个profile。不定义则不会浪费性能去解析。
解析并注册BeanDefinition
处理完 profile 就可以进行 XML 读取
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
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)) {
// 解析默认命名空间bean
parseDefaultElement(ele, delegate);
} else {
// 解析自定义命名空间bean
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
bean的声明分为两大类(根据getNameSpaceURI()获取命名空间,并与http://www.Springframework.org/schema/beans 进行比对):
- 默认:
<bean id="test" class="test.TestBean"/>
- 自定义:
<tx:annotation-driven>
详细见 (二)XML标签解析 -- 默认标签解析 parseDefaultElement
《Spring源码深度解析》 学习笔记