Spring的IOC的源码解析(一)

8 篇文章 0 订阅
7 篇文章 0 订阅
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配置文件中,每一个标签都需要引入标签命名空间才能使用,例如<tx>标签的命名空间是 http://www.springframework.org/schema/tx
(b) 获取handle
readerContext是 XmlReaderContext对象;
默认命名空间解析类是: DefaultNamespaceHandlerResolver
默认handle映射配置文件位置是在 META-INF/spring.handlers文件中,例如标签<aop>,在 spring-aop-*.jar包中的 META-INF/ spring.handlers文件的内容如下:
所以<aopg>标签的handle是AopNamespaceHandler类,下边看看AopNamespaceHandler类是如何解析<aop>标签的。

第二部分:handle的parse过程
DefaultNamespaceHandlerResolver在解析得到handle之后,会执行handle的init方法,初始化handle;
例如标签<aop:config>,参看(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个处理类
  1. ConfigBeanDefinitionParser处理<aop:config></aop:config>标签;
  2. AspectJAutoProxyBeanDefinitionParser处理<aop:aspectj-autoproxy></aop:aspectj-autoproxy>标签;
  3. ScopedProxyBeanDefinitionDecorator处理<aop:scoped-proxy>标签
  4. SpringConfiguredBeanDefinitionParser处理<aop: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<Element> 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属性,所以这两个属性受<tx:annotation-driven> <aop:config> <aop:aspectj-autoproxy>标签共同控制。
下边默认的优先级顺序,可见AnnotationAwareAspectJAutoProxyCreator.class优先级最高。
static {
    APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
以上BPP分别在<tx:annotation-driven> <aop:config> <aop:aspectj-autoproxy>标签解析的过程中会尝试去注册。

(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需要指定依赖的切点,有两种方式:
  1. pointcut-ref 指定依赖的pointcut的名字
  2. 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<Element> 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<String, BeanDefinition> 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) {
}
这里调用的事件监听器是EmptyReaderEventListener,里面没有任何操作,所模块注册事件并不会做什么;



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值