Spring容器初始化的过程,主要是由AbstractApplicationContext类的refresh方法完成,这一篇文章主要讲BeanDefinition的解析和加载
所有的开始就是这句代码:
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
第一部分:获取解析标签的handle
obtainFreshBeanFactory调用的refreshBeanFactory方法
在AbstractRefreshableApplicationContext子类中实现;
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
//如果bean工厂已经存在,就先销毁bean再关闭工厂
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
//返回一个DefaultListableBeanFactory对象
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
//bean的加载与bean的定义有关,所以在子类中实现;参看(1)
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
(1) AbstractXmlApplicationContext.
loadBeanDefinitions()方法
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//xml资源加载器
// Configure the bean definition reader with this context's
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
//加载bean;参看(2)
}
(2) 加载bean定义过程
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources); //这个分支直接循环调用loadBeanDefinitions(Resource);参看(3)
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);//这个分支最终还是会调用loadBeanDefinitions(Resource);参看(3)
}
}
说明:
ClassPathXmlApplicationContext类重载的
getConfigResources如下:
protected Resource[] getConfigResources() {
return this.configResources;
}
返回的是指定资源加载类的构造器方法设置Resource数组;
ClassPathXmlApplicationContext的构造方法之一如下:
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
允许设置资源路径,资源路径被保存到字符串数组中;
getConfigLocations()
方法就是获取这字符串数组对象的;
FileSystemXmlApplicationContext的构造方法之一如下:
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
允许设置资源路径,资源路径被保存到字符串数组中;getConfigLocations()方法就是获取这字符串数组对象的;
(3)
XmlBeanDefinitionReader的loadBeanDefinitions(Resource)方法会调用loadBeanDefinitions(EncodeResource),后者的核心逻辑是调用doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法,这个方法的核心逻辑是:
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
//参见(4)
(4) XmlBeanDefinitionReader的注册bean方法registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//返回DefaultBeanDefinitionDocumentReader对象
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//传入XmlReaderContext对象,参见(5)
return getRegistry().getBeanDefinitionCount() - countBefore;
//返回新增的bean数量
}
(5) DefaultBeanDefinitionDocumentReader的
registerBeanDefinitions
方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);//参见(6)
}
(6)
DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions(Element root)方法
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//返回一个BeanDefinitionParserDelegate对象
//other code
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
//最终调用BeanDefinitionParserDelegate.parseCustomElement方法,参见(7)
postProcessXml(root);
this.delegate = parent;
}
(7) BeanDefinitionParserDelegate的parseCustomElement方法
逻辑:寻找每个bean的代理空间句柄handle,并使用handle.parse方法完成解析
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);//参见(a)
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);//解析并获取命名空间的处理类,参见(b)
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));//参看(9)
}
(a) 获取标签命名空间URL
bean配置文件中,每一个标签都需要引入标签命名空间才能使用,例如标签的命名空间是 http://www.springframework.org/schema/tx
(b) 获取handle
readerContext是
XmlReaderContext对象;
默认命名空间解析类是:
DefaultNamespaceHandlerResolver
默认handle映射配置文件位置是在
META-INF/spring.handlers文件中,例如标签
,在
spring-aop-*.jar包中的
META-INF/
spring.handlers文件的内容如下:
http\:// www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
所以
标签的handle是AopNamespaceHandler类,下边看看AopNamespaceHandler类是如何解析标签的。
第二部分:handle的parse过程
DefaultNamespaceHandlerResolver在解析得到handle之后,会执行handle的init方法,初始化handle;
例如标签
,参看(8)的代码可知对应的解析类是
ConfigBeanDefinitionParser
(8) AopNamespaceHandler的初始化方法
public class AopNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
AopNamespaceHandler的初始化方法注册了4个处理类
- ConfigBeanDefinitionParser处理标签;
- AspectJAutoProxyBeanDefinitionParser处理标签;
- ScopedProxyBeanDefinitionDecorator处理scoped-proxy>标签
- SpringConfiguredBeanDefinitionParser处理spring-configured>标签
(9)
AopNamespaceHandler的parse方法在父类中实现
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);//初始化方法把parser类放到一个map中,这里实现了根据标签获取对应的parser
return (parser != null ? parser.parse(element, parserContext) : null);//调用parser的解析方法,参看(10)
}
(10)
ConfigBeanDefinitionParser的parse方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element);//尝试注册自动代理BPP ;参见(11)
List childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) {
//只配置切点的解析;参见(a)
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
//包含切点和通知;参见(b)
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
//完整的切面解析,包含切点,通知;参见(c)
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
(11) 关于自动代理的BPP注册
这里尝试注册的Bean情况如下:
id="org.springframework.aop.config.internalAutoProxyCreator"
class=AspectJAwareAdvisorAutoProxyCreator
使用AopConfigUtils尝试注册。
这里为什么要说尝试呢?看看AopConfigUtils的实现就明白的。
对于id="org.springframework.aop.config.internalAutoProxyCreator"的bean,默认系统加了3个,每个类的索引分别是0-1-2,这个索引值代表优先级,值越大优先级越高。当尝试注册这个bean的时候,如果bean已经注册了,那就比较请求注册的bean和当前注册的bean的指定类的优先级,并最终注册优先级高的那个类!无论每次尝试注入bean的class是否成功,都会更新proxy-target-class和expose-proxy属性,所以这两个属性受 标签共同控制。
下边默认的优先级顺序,可见AnnotationAwareAspectJAutoProxyCreator.class优先级最高。
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
(a)解析PointCut
定义Pointcut的beanDefinition,设置目标类是:AspectJExpressionPointcut.class
(b)解析Advisor标签
定义Advisor的beanDefinition,设置目标类是:DefaultBeanFactoryPointcutAdvisor.class
如果标签没有设置advice-ref属性,抛出异常;否则,在beanDefinition中设置adviceBeanName属性,值就是标签的advice-ref属性的值
如果标签设置了order属性,则在beanDefinition中设置order属性,值就是标签order属性的值
(c) 完整切面的解析过程,
首先解析advice标签,advice标签的判断逻辑是:
private boolean isAdviceNode(Node aNode, ParserContext parserContext) {
if (!(aNode instanceof Element)) {
return false;
}
else {
String name = parserContext.getDelegate().getLocalName(aNode);
return (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) ||
AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name));
}
}
每个advice bean 需要定义两个参数Bean:
方法工厂bean,用于获取设置的方法对象,定义如下:
// create the method factory bean
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
methodDefinition.setSynthetic(true);
还有一个是aspect factory bean ,用于指定advice是属于那个aspect的,即哪个切面;定义如下:
// create instance factory definition
RootBeanDefinition aspectFactoryDef =new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
aspectFactoryDef.setSynthetic(true);
advice最终是定义成advisor再注入到容器中的,定义方法如下:
// configure the advisor
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
//指定目标类
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
//把advice的beanDefinition设置到advisorDefinition中
if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
//把aspect的order属性设置为advisor的order属性
advisorDefinition.getPropertyValues().add(ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
}
// register the final advisor
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
//注册advisor
advice需要指定依赖的切点,有两种方式:
- pointcut-ref 指定依赖的pointcut的名字
- pointcut 定义一个pointcut
这两种方式只能设置一个,不能同时设置;如果是定义一个pointcut,那么会把pointcut解析成一个beanDefinition,并作为切面依赖bean的一部分;看解析过程:
private Object parsePointcutProperty(Element element, ParserContext parserContext) {
if (element.hasAttribute(POINTCUT)) {
//定义一个pointcut
// Create a pointcut for the anonymous pc and register it.
String expression = element.getAttribute(POINTCUT);
AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
pointcutDefinition.setSource(parserContext.extractSource(element));
return pointcutDefinition;
}
else if (element.hasAttribute(POINTCUT_REF)) {
//指定一个pointcut的引用
String pointcutRef = element.getAttribute(POINTCUT_REF);
return pointcutRef;
}
}
构建advice bean定义的方法核心代码如下:
RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
//获取目标类,参见【1】
adviceDefinition.setSource(parserContext.extractSource(adviceElement));
adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
//设置所属aspect的名字
adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
//advice在aspect标签子标签中的序号
if (adviceElement.hasAttribute(RETURNING)) {
adviceDefinition.getPropertyValues().add(RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
//returning属性
}
if (adviceElement.hasAttribute(THROWING)) {
adviceDefinition.getPropertyValues().add(THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
//throwing属性
}
if (adviceElement.hasAttribute(ARG_NAMES)) {
adviceDefinition.getPropertyValues().add(ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
//arg-names 属性
}
【1】 advice的目标类的获取逻辑是:
private Class> getAdviceClass(Element adviceElement, ParserContext parserContext) {
String elementName = parserContext.getDelegate().getLocalName(adviceElement);
if (BEFORE.equals(elementName)) {
return AspectJMethodBeforeAdvice.class;//before advice
}
else if (AFTER.equals(elementName)) {
return AspectJAfterAdvice.class;//after advice
}
else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
return AspectJAfterReturningAdvice.class;//after-returning advice
}
else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
return AspectJAfterThrowingAdvice.class;//after-throwing advice
}
else if (AROUND.equals(elementName)) {
return AspectJAroundAdvice.class;//around advice
}
else {
throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
}
}
解析切面
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);//创建aspect的beanDefinition
parserContext.pushContainingComponent(aspectComponentDefinition);//添加
parserContext.popAndRegisterContainingComponent();
//注册到bean工厂中,参见【2】
解析切点
List pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
parsePointcut(pointcutElement, parserContext);
}
pointcutDefinition指定
beanClass为AspectJExpressionPointcut;
【2】如果定义了id,就以id注册;这个
parserContext.getRegistry()返回的是beanFactory,所以调用的是DefaultListableBeanFactory的registerBeanDefinition将bean的定义对象存到Map中;
private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);
如果没有定义id属性,就按继承名注册,这里的"继承名"是怎样的呢?请看,XmlReaderContext.
registerWithGeneratedName
public String registerWithGeneratedName(BeanDefinition beanDefinition) {
String generatedName = generateBeanName(beanDefinition);
getRegistry().registerBeanDefinition(generatedName, beanDefinition);
return generatedName;
}
默认名的获得,
String generatedBeanName = definition.getBeanClassName();
也就是类的完全限定名的字符串。
最后看模块注册事件处理,
public void fireComponentRegistered(ComponentDefinition componentDefinition) {
this.eventListener.componentRegistered(componentDefinition);
}
这里调用的事件监听器是EmptyReaderEventListener,里面没有任何操作,所模块注册事件并不会做什么;