《深入理解Spring原理》 02-Bean定义详解

    Spring前言中已经介绍了Bean组件在Spring中的重要性,本文将详细讲解Bean组件。

    Bean组件在Spring中的实现无外乎 : Bean的定义、Bean的解析、Bean的创建

     所以本文目录如下:

  •      Bean的定义
  •      Bean的解析
  •      Bean的创建
  •      Bean组件设计精髓总结
  •      Bean组件优秀编码学习

 

1. Bean的定义

     如何知道 Spring是如何定义Bean的呢?

     以XML配置注入Bean方式为例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean  id="person" class="com.ssm.DemoApplication.Person"/>
</beans>

     如上图所示,在applicationContext.xml配置一个Bean,当启动Spring IOC容器时会将 person 这个bean注入到容器中。

     既然可以将XML中的配置Bean注入的IOC中,那么这个过程中肯定会将XML转为Bean。那么从 XML----> Bean 这个过程肯定会出现Bean的定义。

     经过翻阅源码找到 xml ----> Bean 转换过程代码:

//入口方法,只传入xml中定义的bean数据
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    //调用目标解析方法
    return parseBeanDefinitionElement(ele, null);
}

//目标解析方法
@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.isDebugEnabled()) {
            logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
        }
    }

    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }

    //将XML转为IOC可以识别的Bean
    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.isDebugEnabled()) {
                    logger.debug("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;
}

   如上图代码所示,

    真正将xml中配置的bean转为 IOC可以识别的Bean方法为 : parseBeanDefinitionElement(ele, beanName, containingBean);

    而该方法最终会调用  BeanDefinitionReaderUtils.createBeanDefinition(parentName, className,classLoader) 方法:

​

public static AbstractBeanDefinition createBeanDefinition(@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
    //创建新的IOC Bean对象
    GenericBeanDefinition bd = new GenericBeanDefinition();
    bd.setParentName(parentName);
    if (className != null) {
        if (classLoader != null) {
            bd.setBeanClass(ClassUtils.forName(className, classLoader));
        } else {
            bd.setBeanClassName(className);
        }
    }
    return bd;
}

​

  如上图所示,Spring最终创建了 GenericBeanDefinition 对象来接收XML中配置的 person bean 对象 。

  到这一步总算找到了Spring中Bean定义的实现类。那么关于Bean的定义,就以GenericBeanDefinition 为源头查找顶级类。

   以GenericBeanDefinition 为源头查找到的UML图如下:

    根据UML图可以知道 在Spring中是以  BeanDefinition 来定义Bean的。前面已经知道将 xml转为Bean过程中是实例化了一个GenericBeanDefinition 对象,所以GenericBeanDefinition 是BeanDefinition的一个默认实现类 。

//Spring 官方文档:关于BeanDefinition默认实现类
GenericBeanDefinition 是一站式Bean定义组件,可以满足任何要求的Bean定义。
读取配置后生成的就是GenericBeanDefinition。而Spring在创建Bean的时候用的其RootBeanDefinition。
RootBeanDifinition主要由 GenericBeanDefinition 以及 父BeanDefinition合并来的。
可以看到 GenericBeanDefinition 的 getParentName()方法会返回 parentBeanName,而 RootBeanDefinition 的 getParentName始终返回的是 null。

同时上文也提到了,如果提前知道父子BeanDefinition,就直接使用 RootBeanDefinition和ChildBeanDefinition(自定义标签解析的时候)

 

2. Bean的解析

      Bean的定义中已经引用了部分Bean解析的代码。以XML配置Bean方式为例,Bean的解析主要是将 :

      XML Bean ----> BeanDefinition

      以XML解析Bean为例,解析入口为:

@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException,
IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    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的解析是从实例化一个 XmlBeanDefinitionReader 对象开始的。那么以 XmlBeanDefinitionReader对象为源头找顶级类。

    以 XmlBeanDefinitionReader对象为源头找顶级类UML图如下:

    Bean解析过程非常复杂,以XML配置Bean方式为例,其核心思想就是:

    定义好XML配置规则  ---->   配置 XML ----->   将XML中配置Bean转为Spring中对于的Bean包装对象。

  

3. Bean的创建

 public static void main(String[] args) throws IOException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Person person = (Person) applicationContext.getBean("person");
    }

     如上图所示,通过 applicationContext.getBean 获得 person对象。首先会到 AbstractApplicationContext中

@Override
public Object getBean(String name) throws BeansException {
	assertBeanFactoryActive();
	return getBeanFactory().getBean(name);
}

   getBeanFactiry()入口方法会到 AbstractRefreshableApplicationContext 中:

@Override public final ConfigurableListableBeanFactory getBeanFactory() {
    synchronized(this.beanFactoryMonitor) {
        if (this.beanFactory == null) {
            throw new IllegalStateException("BeanFactory not initialized or already closed - " + "call 'refresh' before accessing beans via the ApplicationContext");
        }
        return this.beanFactory;
    }
}

   如上图所示,要获得 person对象首先要获得一个 ConfigurableListableBeanFactory 对象(实际上获取的是其实现类DefaultListableBeanFactory) ,然后调用该对象的 getBean()方法获得 person 对象。

   以 ConfigurableListableBeanFactory 接口为源头向上寻找顶级类,UML如下图所示:

       如上图所示,Bean的创建是典型的工厂模式,顶级接口是BeanFactory ,并且 DefaultListableBeanFactory 是 BeanFactory的默认实现类。

       BeanFactory顾名思义是一个工厂类,是用来生产 Bean的,定义如下:

       既然BeanFactory是用来创建Bean的,那么就创建一个试试看:

  public static void main(String[] args) throws IOException {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        Person person = (Person) beanFactory.getBean("person");
        System.out.println(person);
    }

    Spring 中使用BeanFactory获取Bean的体现:

 

  4. Bean组件设计精髓总结

        1.  Spring的设计基本上都是以 顶级接口为依托,根据不同功能划分接口。当一个实现需要集合多个功能时,Spring会使用接口继承。Spring在规划好接口之后一般都会有 Abstract 抽象类 穿插在具体实现类之上。

         具体步骤:

         设计顶级接口(细粒度)  ----->  根据功能组合接口 ------>  Abstract 抽象 -----> 具体实现

 

   2. 我们平时开发习惯常量维护在一个常量类中或者枚举中,但是在Spring中并没有这样做。Spring将常量定义在各自的接口中或者抽象类中,在真的实现类中就很少有常量出现了。在Java中一个接口代表了一个标准,Spring将常量定义在接口中应该也是使常量具有标准。

 

 5. Bean组件优秀编码学习

       建造者设计模式

建设者设计模式是属于创建对象模式三剑客的第一种模式。该模式用于简化复杂对象的构造。
在Spring中,我们可以在
org.springframework.beans.factory.support.BeanDefinitionBuilder
类中检索这个逻辑,这是一个允许我们以编程方式定义bean的类。

优点:

    使用建造者模式可以使客户端不必知道产品内部组成的细节。
    具体的建造者类之间是相互独立的,这有利于系统的扩展。
    具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响。

缺点:

    建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,
    则不适合使用建造者模式,因此其使用范围受到一定的限制。
    如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

       单例模式 + 工厂模式 创建Singleton对象

Spring通过 单例模式 + 工厂模式 方式创建IOC中的Bean对象

其中单例模式使用 双端检索 + ConcurrentHashMap 方式实现单例模式,
在单例模式中需要创建Bean时,直接调用 ObjectFactory 生成Bean。

Spring源码如下:
 
public Object getSingleton(String beanName, ObjectFactory < ?>singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized(this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet < >();
            }
            try {
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            } catch(IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            } catch(BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException: this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            } finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

    

以上就是本文 《深入理解Spring原理》 02-Bean核心组件详解 的全部内容,

上述内容如有不妥之处,还请读者指出,共同探讨,共同进步!

@author : jackcheng1117@163.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值