本文主要参考芋道源码关于springframework的精品解析,如有侵权,即刻删除。
最近阅读了 s p r i n g f r a m e w o r k spring framework springframework的部分源码,了解了其中的工作流程,于是来做个总结。 i o c ioc ioc的原理想必大家都很清楚,所以就不再阐述与其相关的内容。而在我个人理解来看, s p r i n g f r a m e w o r k springframework springframework源码中的工作流程较为复杂,本文仅谈一谈资源加载和 b e a n bean bean解析方面的内容
单从 s p r i n g f r a m e w o r k springframework springframework的使用来说,我们可以在 x m l xml xml文件中配置,也可以在代码中进行配置。所以初始化首先需要定位到 x m l xml xml配置文件。才能够进行接下来的一些操作。再次,将容器初始化分为如下几步
- 资源定位与加载
- B e a n D e f i n i t i o n BeanDefinition BeanDefinition载入和解析
- B e a n D e f i n i t i o n BeanDefinition BeanDefinition注册
资源定位与加载
s p r i n g f r a m e w o r k springframework springframework提供了 R e s o u r c e Resource Resource和 R e s o u r c e L o a d e r ResourceLoader ResourceLoader两个接口来完成对资源文件的定位和加载。
Resource
/**
* Interface for a resource descriptor that abstracts from the actual
* type of underlying resource, such as a file or class path resource.
*
* An InputStream can be opened for every resource if it exists in
* physical form, but a URL or File handle can just be returned for
* certain resources. The actual behavior is implementation-specific.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see #getInputStream()
* @see #getURL()
* @see #getURI()
* @see #getFile()
* @see WritableResource
* @see ContextResource
* @see UrlResource
* @see ClassPathResource
* @see FileSystemResource
* @see PathResource
* @see ByteArrayResource
* @see InputStreamResource
*/
public interface Resource extends InputStreamSource {
......
可以看到
R
e
s
o
u
r
c
e
Resource
Resource接口的注释中列出了多种类型的资源,如
u
r
l
url
url,
f
i
l
e
file
file,
b
y
t
e
A
r
r
a
y
byteArray
byteArray等等。
R
e
s
o
u
r
c
e
Resource
Resource接口的实现类众多,选取其中的
C
l
a
s
s
P
a
t
h
R
e
s
o
u
r
c
e
ClassPathResource
ClassPathResource来作为例子,可以看看它的继承关系。
可以看到
C
l
a
s
s
P
a
t
h
R
e
s
o
u
r
c
e
ClassPathResource
ClassPathResource间接继承了
A
b
s
t
r
a
c
t
R
e
s
o
u
r
c
e
AbstractResource
AbstractResource抽象类,查看该类可以发现其中定义了一些共有的方法。
/**
* Convenience base class for {@link Resource} implementations,
* pre-implementing typical behavior.
*
* <p>The "exists" method will check whether a File or InputStream can
* be opened; "isOpen" will always return false; "getURL" and "getFile"
* throw an exception; and "toString" will return the description.
*
* @author Juergen Hoeller
* @since 28.12.2003
*/
public abstract class AbstractResource implements Resource {
@Override
public boolean exists() {
// Try file existence: can we find the file in the file system?
try {
return getFile().exists();
}
catch (IOException ex) {
// Fall back to stream existence: can we open the stream?
try {
getInputStream().close();
return true;
}
catch (Throwable isEx) {
return false;
}
}
}
@Override
public boolean isReadable() {
return exists();
}
@Override
public boolean isOpen() {
return false;
}
@Override
public boolean isFile() {
return false;
}
@Override
public URI getURI() throws IOException {
URL url = getURL();
try {
return ResourceUtils.toURI(url);
}
catch (URISyntaxException ex) {
throw new NestedIOException("Invalid URI [" + url + "]", ex);
}
}
@Override
public ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
@Override
public long contentLength() throws IOException {
InputStream is = getInputStream();
try {
long size = 0;
byte[] buf = new byte[255];
int read;
while ((read = is.read(buf)) != -1) {
size += read;
}
return size;
}
finally {
try {
is.close();
}
catch (IOException ex) {
}
}
}
@Override
public long lastModified() throws IOException {
long lastModified = getFileForLastModifiedCheck().lastModified();
if (lastModified == 0L) {
throw new FileNotFoundException(getDescription() +
" cannot be resolved in the file system for resolving its last-modified timestamp");
}
return lastModified;
}
protected File getFileForLastModifiedCheck() throws IOException {
return getFile();
}
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof Resource &&
((Resource) other).getDescription().equals(getDescription())));
}
}
可以看到其中定义了很多方便操作资源的方法。 C l a s s P a t h R e s o u r c e ClassPathResource ClassPathResource类间接继承了该类,并且在其中定义了 C l a s s Class Class和 C l a s s L o a d e r ClassLoader ClassLoader字段
public class ClassPathResource extends AbstractFileResolvingResource {
private final String path;
@Nullable
private ClassLoader classLoader;
@Nullable
private Class<?> clazz;
当需要获取资源对应的
I
O
IO
IO时,可直接调用getInputStream()
方法获取到对应的输入流
/**
* This implementation opens an InputStream for the given class path resource.
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see java.lang.Class#getResourceAsStream(String)
*/
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
该方法首先用class.getResourceAsStream()
方法进行查找,然后再使用classLoader.getResourceAsStream()
方法查找,最后使用classLoader.getSystemResourceAsStream()
方法进行查找。
ResourceLoader
R e s o u r c e L o a d e r ResourceLoader ResourceLoader接口就较为简单,代码如下。
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:". */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
@Nullable
ClassLoader getClassLoader();
}
其中定义了默认的路径
c
l
a
s
s
p
a
t
h
classpath
classpath。
R
e
s
o
u
r
c
e
L
o
a
d
e
r
ResourceLoader
ResourceLoader接口有一个默认实现
D
e
f
a
u
l
t
R
e
s
o
u
r
c
e
L
o
a
d
e
r
DefaultResourceLoader
DefaultResourceLoader。
public class DefaultResourceLoader implements ResourceLoader {
@Nullable
private ClassLoader classLoader;
private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);
private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);
该实现类增加了两个字段。 r e s o u r c e C a c h e s resourceCaches resourceCaches明显是用来缓存资源文件。 p r o t o c o l R e s o l v e r s protocolResolvers protocolResolvers是用来存储解析协议的。 P r o t o c o l R e s o l v e r ProtocolResolver ProtocolResolver接口可通过 S P I SPI SPI方式扩展。获取资源的方法如下,代码较为简单。
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
我看到当前的接口只能返回单个资源,所以增加了 R e s o u r c e P a t t e r n R e s o l v e r ResourcePatternResolver ResourcePatternResolver接口支持多个资源的返回。
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
该接口的实现类 P a t h M a t c h i n g R e s o u r c e P a t t e r n R e s o l v e r PathMatchingResourcePatternResolver PathMatchingResourcePatternResolver支持单个 R e s o u r c e Resource Resource或者多个 R e s o u r c e Resource Resource的返回。
@Override
public Resource getResource(String location) {
return getResourceLoader().getResource(location);
}
@Override
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Generally only look for a pattern after a prefix here,
// and on Tomcat only after the "*/" separator for its "war:" protocol.
int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
locationPattern.indexOf(':') + 1);
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
该方法的主要处理逻辑如图
获取多个
R
e
s
o
u
r
c
e
Resource
Resource时,主要调用了findPathMatchingResources()
和findAllClassPathResources()
方法。findAllClassPathResources()
方法较为简单,与默认实现中获取
R
e
s
o
u
r
c
e
Resource
Resource的过程类似。而findPathMatchingResources()
方法代码如下。
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
if (resolvedUrl != null) {
rootDirUrl = resolvedUrl;
}
rootDirResource = new UrlResource(rootDirUrl);
}
if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
}
else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isTraceEnabled()) {
logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}
该方法获取到所有资源以后,分别按照不同的协议或者资源类型对资源进行解析,分别调用VfsResourceMatchingDelegate.findMatchingResources()
,doFindPathMatchingJarResources()
,doFindPathMatchingFileResources()
等方法进行解析然后返回。以
v
f
s
vfs
vfs开头的资源委托给
V
f
s
R
e
s
o
u
r
c
e
M
a
t
c
h
i
n
g
D
e
l
e
g
a
t
e
VfsResourceMatchingDelegate
VfsResourceMatchingDelegate类进行处理,代码如下。
/**
* Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.
*/
private static class VfsResourceMatchingDelegate {
public static Set<Resource> findMatchingResources(
URL rootDirURL, String locationPattern, PathMatcher pathMatcher) throws IOException {
Object root = VfsPatternUtils.findRoot(rootDirURL);
PatternVirtualFileVisitor visitor =
new PatternVirtualFileVisitor(VfsPatternUtils.getPath(root), locationPattern, pathMatcher);
VfsPatternUtils.visit(root, visitor);
return visitor.getResources();
}
}
从注释中可以看到采用委托模式是为了避免在运行时依赖 J B o s s JBoss JBoss的 V F S VFS VFS A P I API API。上述代码中的关键类为 P a t t e r n V i r t u a l F i l e V i s i t o r PatternVirtualFileVisitor PatternVirtualFileVisitor。查看该类定义可以发现是通过访问者模式和动态代理来实现资源的获取。
/**
* VFS visitor for path matching purposes.
*/
@SuppressWarnings("unused")
private static class PatternVirtualFileVisitor implements InvocationHandler {
private final String subPattern;
private final PathMatcher pathMatcher;
private final String rootPath;
private final Set<Resource> resources = new LinkedHashSet<>();
public PatternVirtualFileVisitor(String rootPath, String subPattern, PathMatcher pathMatcher) {
this.subPattern = subPattern;
this.pathMatcher = pathMatcher;
this.rootPath = (rootPath.isEmpty() || rootPath.endsWith("/") ? rootPath : rootPath + "/");
}
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (Object.class == method.getDeclaringClass()) {
if (methodName.equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (methodName.equals("hashCode")) {
return System.identityHashCode(proxy);
}
}
else if ("getAttributes".equals(methodName)) {
return getAttributes();
}
else if ("visit".equals(methodName)) {
visit(args[0]);
return null;
}
else if ("toString".equals(methodName)) {
return toString();
}
throw new IllegalStateException("Unexpected method invocation: " + method);
}
public void visit(Object vfsResource) {
if (this.pathMatcher.match(this.subPattern,
VfsPatternUtils.getPath(vfsResource).substring(this.rootPath.length()))) {
this.resources.add(new VfsResource(vfsResource));
}
}
@Nullable
public Object getAttributes() {
return VfsPatternUtils.getVisitorAttributes();
}
public Set<Resource> getResources() {
return this.resources;
}
public int size() {
return this.resources.size();
}
@Override
public String toString() {
return "sub-pattern: " + this.subPattern + ", resources: " + this.resources;
}
}
至此, s p r i n g f r a m e w o r k springframework springframework就完成了资源定位和加载。
BeanDefinition载入,解析
众所周知, i o c ioc ioc容器管理着所有的 b e a n bean bean,而 b e a n bean bean在 s p r i n g f r a m e w o r k springframework springframework中对应的数据结构就是 B e a n D e f i n i t i o n BeanDefinition BeanDefinition。来看看 B e a n D e f i n i t i o n BeanDefinition 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;
}
BeanDefinitionReader
其中定义了
b
e
a
n
bean
bean的创建模式以及角色类型。
要将获取到的资源文件
R
e
s
o
u
r
c
e
Resource
Resource转换成对应的
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition,
s
p
r
i
n
g
spring
spring提供了
B
e
a
n
D
e
f
i
n
i
t
i
o
n
R
e
a
d
e
r
BeanDefinitionReader
BeanDefinitionReader接口来完成这个工作。在
B
e
a
n
D
e
f
i
n
i
t
i
o
n
R
e
a
d
e
r
BeanDefinitionReader
BeanDefinitionReader中的核心方法为loadBeanDefinitions()
。我们来看看
B
e
a
n
D
e
f
i
n
i
t
i
o
n
R
e
a
d
e
r
BeanDefinitionReader
BeanDefinitionReader的子类
X
m
l
B
e
a
n
D
e
f
i
n
i
t
i
o
n
R
e
a
d
e
r
XmlBeanDefinitionReader
XmlBeanDefinitionReader中loadBeanDefinitions()
的实现。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
其中
E
n
c
o
d
e
d
R
e
s
o
u
r
c
e
EncodedResource
EncodedResource是对
R
e
s
o
u
r
c
e
Resource
Resource添加了编码信息的包装类。从代码中可以看出,首先从正在加载的
r
e
s
o
u
r
c
e
s
C
u
r
r
e
n
t
l
y
B
e
i
n
g
L
o
a
d
e
d
resourcesCurrentlyBeingLoaded
resourcesCurrentlyBeingLoaded中查看当前的
E
n
c
o
d
e
d
R
e
s
o
u
r
c
e
EncodedResource
EncodedResource是否已经正在加载,避免重复加载造成死循环。如果未加载,则调用真正的加载方法doLoadBeanDefinitions()
来进行加载。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
从代码可以看到首先会将
R
e
s
o
u
r
c
e
Resource
Resource转换成
D
o
c
u
m
e
n
t
Document
Document对象。查看转换方法doLoadDocument()
可以发现在真正转换时还会进行校验。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
查看校验方法getValidationModeForResource()
。
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
return VALIDATION_XSD;
}
该方法会检查
x
m
l
xml
xml文件基于
D
T
D
DTD
DTD模式还是
X
S
D
XSD
XSD模式。继续深入查看detectValidationMode()
方法。
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)) {
isDtdValidated = true;
break;
}
if (hasOpeningTag(content)) {
// End of meaningful data...
break;
}
}
return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
}
catch (CharConversionException ex) {
// Choked on some character encoding...
// Leave the decision up to the caller.
return VALIDATION_AUTO;
}
finally {
reader.close();
}
}
可以看到对文件内容进行了解析,跳过注释验证了文件类型。如果对如何跳过注释解析内容有兴趣可以参考 X m l V a l i d a t i o n M o d e D e t e c t o r XmlValidationModeDetector XmlValidationModeDetector类中的代码。
BeanDefinition注册
上面说到已经将 R e s o u r c e Resource Resource转换成 D o c u m e n t Document Document对象并且验证过格式。接下来就进行注册啦。由于资源文件格式已经发生了转换所以接下来需要 B e a n D e f i n i t i o n D o c u m e n t R e a d e r BeanDefinitionDocumentReader BeanDefinitionDocumentReader接口来完成注册流程。 B e a n D e f i n i t i o n D o c u m e n t R e a d e r BeanDefinitionDocumentReader BeanDefinitionDocumentReader有一个默认实现类 D e f a u l t B e a n D e f i n i t i o n D o c u m e n t R e a d e r DefaultBeanDefinitionDocumentReader DefaultBeanDefinitionDocumentReader。
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
public static final String NESTED_BEANS_ELEMENT = "beans";
public static final String ALIAS_ELEMENT = "alias";
public static final String NAME_ATTRIBUTE = "name";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String IMPORT_ELEMENT = "import";
public static final String RESOURCE_ATTRIBUTE = "resource";
public static final String PROFILE_ATTRIBUTE = "profile";
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private XmlReaderContext readerContext;
@Nullable
private BeanDefinitionParserDelegate delegate;
.......
查看字段可以发现其中定义了很多熟悉的字段,它们都是为接来下在注册过程中进行的
x
m
l
xml
xml文件解析做准备。注册方法registerBeanDefinitions()
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
可以看出真正进行注册的方法是doRegisterBeanDefinitions()
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
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;
}
阅读注释可以发现,代码中的委托类是用来避免陷入死循环的。由于该方法可以被递归调用,每次进入该方法都创建一个新的
B
e
a
n
D
e
f
i
n
i
t
i
o
n
P
a
r
s
e
r
D
e
l
e
g
a
t
e
BeanDefinitionParserDelegate
BeanDefinitionParserDelegate对象来记录每次调用该方法的信息。通过对比该信息来结束递归。接下来会对
x
m
l
xml
xml进行预处理和结束处理,跳过这些我们来看关键方法,对
x
m
l
D
o
c
u
m
e
n
t
xmlDocument
xmlDocument对象的解析过程parseBeanDefinitions()
方法。
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
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)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
查看注释就可以发现对元素的解析分为两类,默认元素调用parseDefaultElement()
方法,其他的调用parseCustomElement()
方法,我们重点来看看解析默认元素得方法。
默认解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
查看代码可以发现其中分别对 i m p o r t import import, a l i a s alias alias, b e a n bean bean, b e a n s beans beans做了解析。选取其中最为复杂和重要的 b e a n bean bean类型来查看其代码。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
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()
方法获取
B
e
a
n
D
e
f
i
n
i
t
i
o
n
H
o
l
d
e
r
BeanDefinitionHolder
BeanDefinitionHolder对象。
B
e
a
n
D
e
f
i
n
i
t
i
o
n
H
o
l
d
e
r
BeanDefinitionHolder
BeanDefinitionHolder对象其实就是添加了别名的
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition对象。如果解析成功,看情况对
B
e
a
n
D
e
f
i
n
i
t
i
o
n
H
o
l
d
e
r
BeanDefinitionHolder
BeanDefinitionHolder进行解码。然后执行注册流程。parseBeanDefinitionElement()
方法代码如下
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
该方法首先获取
b
e
a
n
N
a
m
e
beanName
beanName,然后调用checkNameUniqueness()
方法检查是否唯一。具体检查过程较为简单可自行查看。接下来会调用parseBeanDefinitionElement()
方法对元素进行解析。
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
第一句保存解析状态跳过,然后就开始获取
c
l
a
s
s
class
class,
p
a
r
e
n
t
parent
parent属性值。然后创建一个
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition等待解析其他属性来填充这个新创建的
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition。首先调用parseBeanDefinitionAttributes()
方法解析属性。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (DEFAULT_VALUE.equals(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
可以看到解析的属性内容都是我们经常在使用 s p r i n g spring spring时添加的。余下步骤
- 调用
parseMetaElements()
方法解析 m e t a meta meta元数据 - 调用
parseLookupOverrideSubElements()
方法解析 l o o k u p − m e t h o d lookup-method lookup−method属性 - 调用
parseReplacedMethodSubElements()
方法解析 r e p l a c e − m e t h o d replace-method replace−method属性 - 调用
parseConstructorArgElements()
方法解析 c o n s t r u c t o r − a r g constructor-arg constructor−arg构造参数 - 调用
parsePropertyElements()
方法解析 p r o p e r t y property property子元素 - 调用
parseQualifierElements()
方法解析 q u a l i f i e r qualifier qualifier子元素
选取其中常用的构造参数解析方法,代码如下
/**
* Parse a constructor-arg element.
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
可以看到首先解析了该元素的属性
i
n
d
e
x
index
index,
n
a
m
e
name
name,
t
y
p
e
type
type。然后判断
i
n
d
e
x
index
index是否为空,调用parsePropertyValue()
方法获取属性值。
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
parsePropertyValue()
方法中会分别检查
r
e
f
ref
ref和
v
a
l
u
e
value
value属性。并且还会查看是否还有子元素。
- 如果有 r e f ref ref属性,则直接在之前保存的解析数据中通过 r e f N a m e refName refName查找对象并返回
- 如果有 v a l u e value value属性,则添加类型信息包装成 T y p e d S t r i n g V a l u e TypedStringValue TypedStringValue返回
- 如果有子元素则调用
parsePropertySubElement()
方法对子元素进行解析
解析子元素
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
从parsePropertySubElement()
方法的源码中可以看到对各种类型的元素都进行了判断。选取其中常用的
l
i
s
t
list
list元素查看parseListElement()
方法
/**
* Parse a list element.
*/
public List<Object> parseListElement(Element collectionEle, @Nullable BeanDefinition bd) {
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
NodeList nl = collectionEle.getChildNodes();
ManagedList<Object> target = new ManagedList<>(nl.getLength());
target.setSource(extractSource(collectionEle));
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
该方法对
l
i
s
t
list
list元素各属性分别解析,然后再对
l
i
s
t
list
list子元素调用parseCollectionElements()
方法进行解析
protected void parseCollectionElements(
NodeList elementNodes, Collection<Object> target, @Nullable BeanDefinition bd, String defaultElementType) {
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}
该方法又会递归调用parsePropertySubElement()
方法对其子元素进行解析。到这里对
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition注册前的解析流程已经较为清楚啦。整个流程就是对
x
m
l
xml
xml文件中的各个元素进行递归下降解析,返回一个完全解析的
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition进行注册操作。注册操作一般会调用默认实现类
D
e
f
a
u
l
t
L
i
s
t
a
b
l
e
B
e
a
n
F
a
c
t
o
r
y
DefaultListableBeanFactory
DefaultListableBeanFactory中的registerBeanDefinition()
方法
实际注册方法
@Override
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) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
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 {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
该方法首先查看待注册对象是否为
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition对象。然后查看其是否已经注册过。如果注册过查看是否能够覆盖之前注册过的对象。注册的过程其实就是将
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition对象放入
M
a
p
<
S
t
r
i
n
g
,
B
e
a
n
D
e
f
i
n
i
t
i
o
n
>
Map<String, BeanDefinition>
Map<String,BeanDefinition>的过程。如果之前未注册过对象,则还需要判断是否已经开始创建
B
e
a
n
Bean
Bean。来决定是否需要保证线程安全。注册完
B
e
a
n
D
e
f
i
n
i
t
i
o
n
BeanDefinition
BeanDefinition还未结束,还需要将
a
l
i
a
s
alias
alias和
b
e
a
n
N
a
m
e
beanName
beanName绑定,绑定过程在simpleAliasRegistry.registerAlias()
方法
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
该方法较为简单,值得一提的是checkForAliasCircle()
方法对是否成环进行了检测,避免
b
e
a
n
N
a
m
e
beanName
beanName与
a
l
i
a
s
N
a
m
e
aliasName
aliasName成环。有兴趣可自行查看。至此,就完成了ioc容器的初始化过程。
未完待续…