曹工說Spring Boot源碼系列開講了(1)-- Bean Definition究竟是什麼,附spring思惟導圖分享

寫在前面的話&&About me

網上寫spring的文章多如牛毛,爲何還要寫呢,由於,很簡單,那是人家寫的;網上都鼓勵你不要造輪子,爲何你還要造呢,由於,那不是你造的。java

我不是要造spring,我只是想把本身學習spring的一些感想,一些心得說出來,但願你們看到有不對的地方,請必定不吝賜教。git

說說我本身,13年小本畢業,軟件工程專業,校招去了最近瘋傳的牢廠總部裏待了2年,15年越獄出來,某落魄互聯網公司(PC時代風頭無兩)待了1年,慨嘆深圳買房之艱難,遂於16年末回蓉。趁着熱血未冷,去了一家創業公司,9個月後,欠薪3月,靠刷信用卡還貸,不得不含淚辭職;17年投奔國企,目前從事公共安全相關工做,趁着對技術還有一腔熱情,沒事寫寫文章,目前主要興趣是:分佈式、微服務等後端技術;對k8s等新技術保持關注;沒事參加一些線下技術活動,歡迎你們和我交流。github

本系列的源碼講解思路

原本,我是想分享一些 spring cloud 的東西,但後來發現,我本身在讀spring cloud的過程當中,有些東西也不是理解得很透徹,好比各類@Enable註解實際上是用到了spring boot的東西,而後我以爲應該倒回去先看看spring boot,而後呢,看spring boot的過程當中,發現spring 和 spring boot實際上是一個深度融合,「你中有我,我中有你」的關係,好比spring boot啓動時,不是會連帶啓動spring 容器嗎,等等。spring

我就想着,乾脆把spring boot系統研究一把算了,我在github上找了spring boot的工程,克隆到了碼雲上(速度要快得多),而後本身回退到了spring boot的第一個版本,時間大概是2013年4月,其實這第一個版本,基本的代碼也已經成型了,我就拿這個版本的源碼在本地idea裏面看,配套的spring 版本是4.0.0,也還行。bootstrap

我本身加了很多註釋在工程裏,工程地址在這:後端

https://gitee.com/ckl111/spring-boot-first-version-learnapi

工程結構以下:安全

這個工程我也會一直維護着,我以爲,spring 4.0.0的版本,暫時對我閱讀代碼來講,足夠了,若是你們大概瞭解spring 每一個版本的新特性的話,能夠發現,spring 4.0開始,各類註解已經很完善了,如今雖然已經出到5.2版本了,但核心的東西也仍是沒有變化,因此,對咱們研讀源碼,影響不大。若是真的把這個版本能讀得差很少了,那想必對spring /spring boot的核心也理解差很少了,到時候再讀新版本的源碼也不遲,是吧。app

整體來講:框架

spring boot 版本,2013年4月,first version。

配套的spring版本,4.0.0.BOOTSTRAP-SNAPSHOT

那時候的spring boot長什麼樣子,我這邊給個地址(我這已經克隆到碼雲了)

https://gitee.com/ckl111/spring-boot/tree/fb6b2244707dd5dfad12d62cb6a3c396555270d1/

spring思惟導圖(bean definition部分)

由於我這個系列,大概會按照思惟導圖的流程來走,而後思惟導圖太大了,我這裏先直接貼前面這部分:

思惟導圖完整連接:https://www.processon.com/view/link/5deeefdee4b0e2c298aa5596

我大概的講解思路也會是上面那樣,從上到下,每一個點細細地講。

正文

bean definition是什麼

閒言少敘,進入正題。第一講,先說說bean definition吧,這個東西,實在過重要了,核心的存儲結構啊。

你們能夠再想想,spring 當初剛出來的時候,主打的是ioc容器,容器裏裝了啥呢,bean啊!bean是什麼呢?

恩。。。我也不知道是啥,反正spring裏拿出來的就是bean。

行,那bean有什麼特徵嗎?

哦,bean是一個對象,有名字,有class類型,有scope(單例、prototype那些),有role(屬於應用的bean、仍是spring框架的bean),有是否延遲初始化(lazy-init),有它依賴的其餘bean,若是這個bean很差造(不能直接反射生成的話),可能還有個工廠方法和工廠bean呢,哎,好像還說漏了,反正挺多的。

那是否是每一個bean都有這些屬性呢?

仔細想一想,好像是的吧。

img

既然都有這些東西,那這個東西感受像是個模板了,就像是最近寫了年終總結,hr小姐姐就給咱們發了模板,上面姓名啊、部門啊、職位啊、述職的基本格式啊,都是固定的,咱們只要拿來,填上本身的信息就好了,那咱們是否是能夠抽象一下,搞個class啊,好比下面這樣:

package com.learn;

import lombok.Data;

@Data
public class SpringDefinition {

    /**
     * bean class名
     */
    String beanClassName;

    /**
     * 工廠bean的名稱
     */
    String factoryBeanName;

    /**
     * 工廠方法的名稱
     */
    String factoryMethodName;

    /**
     * singleton/prototype等
     */
    String scope;

    /**
     * 是否延遲初始化
     */
    boolean isLazyInit;

    /**
     * 依賴的bean
     */
    String[] dependsOn;

    /**
     * bean的角色,好比:1:框架;2:應用
     */
    int role;

    /**
     * 是否爲主候選bean
     */
    boolean primary;

    ...其餘屬性
}

實話說,是能夠的,一些簡單的,輕量級ioc容器就是這麼玩的,可是spring做爲優秀代碼的表明,確定不能這麼low,接口的抽象性要好得多,方便咱們替換不一樣的實現,該用接口來抽象,確定要抽象爲接口。

下邊咱們就看看該接口,先看接口的描述:

* A BeanDefinition describes a bean instance, which has property values,
* constructor argument values, and further information supplied by
* concrete implementations.
*
* <p>This is just a minimal interface: The main intention is to allow a
* {@link BeanFactoryPostProcessor} such as {@link PropertyPlaceholderConfigurer}
* to introspect and modify property values and other bean metadata.
* @author Juergen Hoeller
* @author Rob Harrop
* @since 19.03.2004

這裏說的是,bean definition描述一個bean實例的各類屬性,尤爲聲明瞭:這是一個最小化接口,主要目的是容許bean factory後置處理器,對bean property和其餘元數據進行修改。並且,這是2004年的接口,可想而知,是多麼核心的api了。

再看看具體定義的方法,更好理解:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    /**
     * Return the name of the parent definition of this bean definition, if any.
     */
    String getParentName();

    /**
     * Set the name of the parent definition of this bean definition, if any.
     */
    void setParentName(String parentName);

    /**
     * Return the current bean class name of this bean definition.
     * <p>Note that this does not have to be the actual class name used at runtime, in
     * case of a child definition overriding/inheriting the class name from its parent.
     * Hence, do <i>not</i> consider this to be the definitive bean type at runtime but
     * rather only use it for parsing purposes at the individual bean definition level.
     */
    String getBeanClassName();

    /**
     * Override the bean class name of this bean definition.
     * <p>The class name can be modified during bean factory post-processing,
     * typically replacing the original class name with a parsed variant of it.
     */
    void setBeanClassName(String beanClassName);

    /**
     * Return the factory bean name, if any.
     */
    String getFactoryBeanName();

    /**
     * Specify the factory bean to use, if any.
     */
    void setFactoryBeanName(String factoryBeanName);

    /**
     * Return a factory method, if any.
     */
    String getFactoryMethodName();

    /**
     * Specify a factory method, if any. This method will be invoked with
     * constructor arguments, or with no arguments if none are specified.
     * The method will be invoked on the specified factory bean, if any,
     * or otherwise as a static method on the local bean class.
     * @param factoryMethodName static factory method name,
     * or {@code null} if normal constructor creation should be used
     * @see #getBeanClassName()
     */
    void setFactoryMethodName(String factoryMethodName);

    /**
     * Return the name of the current target scope for this bean,
     * or {@code null} if not known yet.
     */
    String getScope();

    /**
     * Override the target scope of this bean, specifying a new scope name.
     * @see #SCOPE_SINGLETON
     * @see #SCOPE_PROTOTYPE
     */
    void setScope(String scope);

    /**
     * Return whether this bean should be lazily initialized, i.e. not
     * eagerly instantiated on startup. Only applicable to a singleton bean.
     */
    boolean isLazyInit();

    /**
     * Set whether this bean should be lazily initialized.
     * <p>If {@code false}, the bean will get instantiated on startup by bean
     * factories that perform eager initialization of singletons.
     */
    void setLazyInit(boolean lazyInit);

    /**
     * Return the bean names that this bean depends on.
     */
    String[] getDependsOn();

    /**
     * Set the names of the beans that this bean depends on being initialized.
     * The bean factory will guarantee that these beans get initialized first.
     */
    void setDependsOn(String[] dependsOn);

    /**
     * Return whether this bean is a candidate for getting autowired into some other bean.
     */
    boolean isAutowireCandidate();

    /**
     * Set whether this bean is a candidate for getting autowired into some other bean.
     */
    void setAutowireCandidate(boolean autowireCandidate);

    /**
     * Return whether this bean is a primary autowire candidate.
     * If this value is true for exactly one bean among multiple
     * matching candidates, it will serve as a tie-breaker.
     */
    boolean isPrimary();

    /**
     * Set whether this bean is a primary autowire candidate.
     * <p>If this value is true for exactly one bean among multiple
     * matching candidates, it will serve as a tie-breaker.
     */
    void setPrimary(boolean primary);


    /**
     * Return the constructor argument values for this bean.
     * <p>The returned instance can be modified during bean factory post-processing.
     * @return the ConstructorArgumentValues object (never {@code null})
     */
    ConstructorArgumentValues getConstructorArgumentValues();

    /**
     * Return the property values to be applied to a new instance of the bean.
     * <p>The returned instance can be modified during bean factory post-processing.
     * @return the MutablePropertyValues object (never {@code null})
     */
    MutablePropertyValues getPropertyValues();


    /**
     * Return whether this a <b>Singleton</b>, with a single, shared instance
     * returned on all calls.
     * @see #SCOPE_SINGLETON
     */
    boolean isSingleton();

    /**
     * Return whether this a <b>Prototype</b>, with an independent instance
     * returned for each call.
     * @see #SCOPE_PROTOTYPE
     */
    boolean isPrototype();

    /**
     * Return whether this bean is "abstract", that is, not meant to be instantiated.
     */
    boolean isAbstract();

    /**
     * Get the role hint for this {@code BeanDefinition}. The role hint
     * provides tools with an indication of the importance of a particular
     * {@code BeanDefinition}.
     * @see #ROLE_APPLICATION
     * @see #ROLE_INFRASTRUCTURE
     * @see #ROLE_SUPPORT
     */
    int getRole();

    /**
     * Return a human-readable description of this bean definition.
     */
    String getDescription();

    /**
     * Return a description of the resource that this bean definition
     * came from (for the purpose of showing context in case of errors).
     */
    String getResourceDescription();

    /**
     * Return the originating BeanDefinition, or {@code null} if none.
     * Allows for retrieving the decorated bean definition, if any.
     * <p>Note that this method returns the immediate originator. Iterate through the
     * originator chain to find the original BeanDefinition as defined by the user.
     */
    BeanDefinition getOriginatingBeanDefinition();

}

你們仔細看看,是否是其實和咱們定義的class差很少呢,主要都是一些get/set方法。裏面的字段呢,下一講咱們詳細講解一下,會結合一些融會貫通的地方。

bean definition接口的實現有哪些

而後咱們看看這個接口有哪些實現吧?

能夠看到,這裏有兩個是我標紅了,由於他們特殊,特殊在他們不屬於spring-beans包,而是在spring-context包裏。後邊遇到了咱們再單說,這裏存疑。

再來看看這個接口的繼承圖:

能夠獲取註解信息的子接口AnnotatedBeanDefinition

咱們看到,這個接口有一個子接口,是AnnotatedBeanDefinition。這個接口定義以下:

/**
 * Extended {@link org.springframework.beans.factory.config.BeanDefinition}
 * interface that exposes {@link org.springframework.core.type.AnnotationMetadata}
 * about its bean class - without requiring the class to be loaded yet.
 * 這個接口擴展了BeanDefinition,能夠得到bean definition中的bean class上的註解元數據。
 * 舉個例子,假設咱們用@controller標註了某個類,那這裏就能獲取到@controller這個註解裏面的信息
 *
 * @author Juergen Hoeller
 * @since 2.5
 * @see AnnotatedGenericBeanDefinition
 * @see org.springframework.core.type.AnnotationMetadata
 */
public interface AnnotatedBeanDefinition extends BeanDefinition {

    /**
     * Obtain the annotation metadata (as well as basic class metadata)
     * for this bean definition's bean class.
     * @return the annotation metadata object (never {@code null})
     */
    AnnotationMetadata getMetadata();

}

能夠想想有什麼用,這個接口能取到bean definition中對應bean class上標註的註解元數據。

好比下面的controller舉例:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any
     */
    String value() default "";

}

那這個AnnotatedBeanDefinition就能取到controller中的value字段的值。

我這裏也寫了個簡單的例子,以下:

@Component("testService")
public class HelloWorldService {

}
@Autowired
    private ApplicationContext applicationContext;

    @Override
    public void run(String... args) {
        DefaultListableBeanFactory beanFactory =
                (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
         // 獲取bean definition,而後獲取其註解元數據 
        AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanFactory.getBeanDefinition("testService");
        AnnotationMetadata metadata = annotatedBeanDefinition.getMetadata();
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes("org.springframework.stereotype.Component");
        log.info("annotationAttributes:{}",annotationAttributes);
    }

我這邊打印出來就是:

信息: annotationAttributes:{value=testService}

代碼在

https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-bootstrap-bean-definition-demo

接口下的實現類

仔細看了兩個接口 AnnotatedBeanDefinitionBeanDefinition,其實實現類都是差很少那幾個。

基本上org.springframework.beans.factory.support.AbstractBeanDefinition充當了基本的實現,基本上,該實現的方法都實現了,除了一個:

/**
     * Clone this bean definition.
     * To be implemented by concrete subclasses.
     * @return the cloned bean definition object
     */
    public abstract AbstractBeanDefinition cloneBeanDefinition();

趕忙這個方法,對咱們分析也沒多大幫助,暫時跳過便可。

再看看org.springframework.beans.factory.support.GenericBeanDefinition,感受很重要,咱們看看:

public class GenericBeanDefinition extends AbstractBeanDefinition {

    private String parentName;


    /**
     * 這裏有點意思,相似於builder模式,先生成一個實例,再本身各類set方法設置相關屬性
     * 
     * Create a new GenericBeanDefinition, to be configured through its bean
     * properties and configuration methods.
     * @see #setBeanClass
     * @see #setBeanClassName
     * @see #setScope
     * @see #setAutowireMode
     * @see #setDependencyCheck
     * @see #setConstructorArgumentValues
     * @see #setPropertyValues
     */
    public GenericBeanDefinition() {
        super();
    }
    ...

    @Override
    public AbstractBeanDefinition cloneBeanDefinition() {
        return new GenericBeanDefinition(this);
    }

}

ok,都這麼簡單的話,就再看兩個,spring beans包中的org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition:

public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

    private final AnnotationMetadata metadata;


    /**
     * Create a new AnnotatedGenericBeanDefinition for the given bean class.
     * @param beanClass the loaded bean class
     */
    public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
        setBeanClass(beanClass);
        this.metadata = new StandardAnnotationMetadata(beanClass, true);
    }

    /**
     * Create a new AnnotatedGenericBeanDefinition for the given annotation metadata,
     * allowing for ASM-based processing and avoidance of early loading of the bean class.
     * Note that this constructor is functionally equivalent to
     * {@link org.springframework.context.annotation.ScannedGenericBeanDefinition
     * ScannedGenericBeanDefinition}, however the semantics of the latter indicate that
     * a bean was discovered specifically via component-scanning as opposed to other
     * means.
     * @param metadata the annotation metadata for the bean class in question
     * @since 3.1.1
     */
    public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata) {
        Assert.notNull(metadata, "AnnotationMetadata must not be null");
        setBeanClassName(metadata.getClassName());
        this.metadata = metadata;
    }


    public final AnnotationMetadata getMetadata() {
         return this.metadata;
    }

}

很簡單,就是多了獲取bean class的註解的功能。

再看這個呢,

/**
 * Extension of the {@link GenericBeanDefinition}
 * class, based on an ASM ClassReader, with support for annotation metadata exposed
 * through the {@link AnnotatedBeanDefinition} interface.
 *
 * <p>This class does <i>not</i> load the bean {@code Class} early.
 * It rather retrieves all relevant metadata from the ".class" file itself,
 * parsed with the ASM ClassReader. It is functionally equivalent to
 * {@link AnnotatedGenericBeanDefinition#AnnotatedGenericBeanDefinition(AnnotationMetadata)}
 * but distinguishes by type beans that have been <em>scanned</em> vs those that have
 * been otherwise registered or detected by other means.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 2.5
 * @see #getMetadata()
 * @see #getBeanClassName()
 * @see org.springframework.core.type.classreading.MetadataReaderFactory
 * @see AnnotatedGenericBeanDefinition
 */
@SuppressWarnings("serial")
public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

    private final AnnotationMetadata metadata;


    /**
     * Create a new ScannedGenericBeanDefinition for the class that the
     * given MetadataReader describes.
     * @param metadataReader the MetadataReader for the scanned target class
     */
    public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
        Assert.notNull(metadataReader, "MetadataReader must not be null");
        this.metadata = metadataReader.getAnnotationMetadata();
        setBeanClassName(this.metadata.getClassName());
    }


    public final AnnotationMetadata getMetadata() {
        return this.metadata;
    }

}

我一開始,一眼看過去,感受眼花了,差很少啊,但這個是在spring context包裏,而後,能夠看上面的註釋,說是使用asm去獲取註解信息。因此,這個和上面那個的差異是:

org.springframework.context.annotation.ScannedGenericBeanDefinition 位於spring-context,使用asm

org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition 位於spring-beans,使用反射

再看看org.springframework.beans.factory.support.RootBeanDefinition(位於spring-beans),這個類下面只有一個子類,位於spring-context的

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition

這兩個類,一看就比較特別,能看出來,和@configuration註解有莫大關係,這個咱們放後面講。

總結

本篇就先到這裏,留了一些問題,放到後面(有些我也要查一下,哈哈)。下一講繼續。

相關文章
相關標籤/搜索