上篇大致拆解了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方法,解析內部實現。
感謝閱讀,如有錯誤,請不吝指正