上篇大致拆解了spring读取配置文件到document对象的操作,本篇紧接上篇说一下spring注册bean定义的操作。
/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
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;
}
/**
* Create the {@link BeanDefinitionDocumentReader} to use for actually
* reading bean definitions from an XML document.
* <p>The default implementation instantiates the specified "documentReaderClass".
* @see #setDocumentReaderClass
*/
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
DefaultBeanDefinitionDocumentReader.class;
/**
* Instantiate a class using its 'primary' constructor (for Kotlin classes,
* potentially having default arguments declared) or its default constructor
* (for regular Java classes, expecting a standard no-arg setup).
* <p>Note that this method tries to set the constructor accessible
* if given a non-accessible (that is, non-public) constructor.
* @param clazz the class to instantiate
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated.
* The cause may notably indicate a {@link NoSuchMethodException} if no
* primary/default constructor was found, a {@link NoClassDefFoundError}
* or other {@link LinkageError} in case of an unresolvable class definition
* (e.g. due to a missing dependency at runtime), or an exception thrown
* from the constructor invocation itself.
* @see Constructor#newInstance
*/
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
return instantiateClass(clazz.getDeclaredConstructor());
}
catch (NoSuchMethodException ex) {
Constructor<T> ctor = findPrimaryConstructor(clazz);
if (ctor != null) {
return instantiateClass(ctor);
}
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
catch (LinkageError err) {
throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
}
}
第一步先创建一个DefaultBeanDefinitionDocumentReader的实例,
第二步实际上是计算当前bean工厂有多少实例bean定义,追踪源码可以看到最后到了DefaultListbeanFactory的成员变量beanDefinitionMap的大小。
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
/**
* Create a new AbstractBeanDefinitionReader for the given bean factory.
* <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
* interface but also the ResourceLoader interface, it will be used as default
* ResourceLoader as well. This will usually be the case for
* {@link org.springframework.context.ApplicationContext} implementations.
* <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
* {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
* <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
* environment will be used by this reader. Otherwise, the reader will initialize and
* use a {@link StandardEnvironment}. All ApplicationContext implementations are
* EnvironmentCapable, while normal BeanFactory implementations are not.
* @param registry the BeanFactory to load bean definitions into,
* in the form of a BeanDefinitionRegistry
* @see #setResourceLoader
* @see #setEnvironment
*/
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// Determine ResourceLoader to use.
if (this.registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader) this.registry;
}
else {
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
// Inherit Environment if possible
if (this.registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
}
else {
this.environment = new StandardEnvironment();
}
}
@Override
public final BeanDefinitionRegistry getRegistry() {
return this.registry;
}
}
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
@Override
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
}
第三步用创建的DefaultBeanDefinitionDocumentReader实例注册bean定义。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
/**
* Create the {@link XmlReaderContext} to pass over to the document reader.
* 这边有个创建xml读取上下文的操作,不是核心代码,先放一下
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
}
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
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";
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
* 可以看到DefaultBeanDefinitionDocumentReader 里面有好多定义bean需要的标签,注释大意是这个实现方法通过spring-beans的XSD解析bean定义,打开一个dom对象,然后初始化定义在<Beans>中的默认设置,然后解析包含的bean定义。
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
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;
}
}
看方法注释:注册在beans标签里面的每个bean定义。,我们先看一下方法内的注释:
所有嵌套的beans元素会在这个方法中递归,为了正确的保留beans的默认属性,跟踪当前的(父)委托,这个复委托可能是空的,创建新的(子)代理,包含父委托的一个引用用于回调。然后最终将这个委托重置到原始的(父)引用。这种行为模拟一个委托栈,而不实际需要一个委托。
这段话有点拗口,本来就比较难理解,加上英语水平有限,翻译出的就更是四不像了,我们先带着问题看源码,看完回来再看这段话。
第一行代码:
BeanDefinitionParserDelegate parent = this.delegate;
这是把当前对象的一个成员变量delegate赋值给parent,BeanDefinitionParserDelegate 这个类很重要,顾名思义,bean定义的解析是委托给他来完成的
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {
//根据readerContext创建bean定义解析代理类
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//初始化一些默认值
delegate.initDefaults(root, parentDelegate);
return delegate;
}
/**
* Initialize the default lazy-init, autowire, dependency check settings,
* init-method, destroy-method and merge settings. Support nested 'beans'
* element use cases by falling back to the given parent in case the
* defaults are not explicitly set locally.
* @see #populateDefaults(DocumentDefaultsDefinition, DocumentDefaultsDefinition, org.w3c.dom.Element)
* @see #getDefaults()
*/
public void initDefaults(Element root, @Nullable BeanDefinitionParserDelegate parent) {
populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
this.readerContext.fireDefaultsRegistered(this.defaults);
}
/**
* Populate the given DocumentDefaultsDefinition instance with the default lazy-init,
* autowire, dependency check settings, init-method, destroy-method and merge settings.
* Support nested 'beans' element use cases by falling back to {@code parentDefaults}
* in case the defaults are not explicitly set locally.
*
* 大意是为给定的DocumentDefaultsDefinition 设置beans的属
* 性,如懒加载,初始化方法等,如果这些属性都没设置或者用的是默
* 认的,并且是嵌套的bean的话,可以用上一级代理(父代理)的相关
* 属性值
* 其中DocumentDefaultsDefinition ,这个类就是相关beans属
* 性封装的一个类,类注释如下:
* * Simple JavaBean that holds the defaults specified
* at the {@code <beans>}level in a standard Spring
* XML bean definition document:
*
* @param defaults the defaults to populate
* @param parentDefaults the parent BeanDefinitionParserDelegate (if any) defaults to fall back to
* @param root the root element of the current bean definition document (or nested beans element)
*/
protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {
String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to false.
lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
}
defaults.setLazyInit(lazyInit);
String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE);
if (isDefaultValue(merge)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to false.
merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE);
}
defaults.setMerge(merge);
String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE);
if (isDefaultValue(autowire)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to 'no'.
autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);
}
defaults.setAutowire(autowire);
if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
}
if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setInitMethod(parentDefaults.getInitMethod());
}
if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
}
defaults.setSource(this.readerContext.extractSource(root));
}
private boolean isDefaultValue(String value) {
return (DEFAULT_VALUE.equals(value) || "".equals(value));
}
设置完默认值后调用fireDefaultsRegistered方法,这个方法最后会调用EmptyReaderEventListener 监听器的defaultsRegistered方法,但是是空实现,猜测是为以后扩展方便预留的。
/**
* Fire an defaults-registered event.
*/
public void fireDefaultsRegistered(DefaultsDefinition defaultsDefinition) {
this.eventListener.defaultsRegistered(defaultsDefinition);
}
public class EmptyReaderEventListener implements ReaderEventListener {
@Override
public void defaultsRegistered(DefaultsDefinition defaultsDefinition) {
// no-op
}
}
至此创建新的代理和设置beans默认值操作完成。
上面这段是特殊处理,大致意思是满足上述条件后就跳出去,不在往下执行。暂时忽略这块代码。
接下来是解析bean内部一些具体的定义,可以看到
/**
* Allow the XML to be extensible by processing any custom element types first,
* before we start to process the bean definitions. This method is a natural
* extension point for any other custom pre-processing of the XML.
* <p>The default implementation is empty. Subclasses can override this method to
* convert custom elements into standard Spring bean definitions, for example.
* Implementors have access to the parser's bean definition reader and the
* underlying XML resource, through the corresponding accessors.
* @see #getReaderContext()
*/
protected void preProcessXml(Element root) {
}
protected void postProcessXml(Element root) {
}
真正的代码实现中,方法preProcessXml和postProcessXml是没有内容的,方法注释大意是,在我们开始处理bean定义前,可以在这边处理spring自定义的元素类型用于扩展,对于任何其他的xml处理前操作,这个方法是一个自然的扩展点。默认的实现是空的,子类可以实现这个方法转换自定义元素到标准的bean定义中,例如,实现者可以通过相应的访问器,访问这个解析器的bean定义读取器和底层依赖的xml资源。简单来说就是spring预留了两个方法,使用者如果想在解析bean的前后做一些自定义的操作,可以通过继承这个类,然后在子类中重写方法实现。
这是一种叫模板的设计模式,可细细体会。
这篇博客就到这边吧,下篇我们深入parseBeanDefinitions方法,解析内部实现。
感谢阅读,如有错误,请不吝指正