網上寫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/
由於我這個系列,大概會按照思惟導圖的流程來走,而後思惟導圖太大了,我這裏先直接貼前面這部分:
思惟導圖完整連接:https://www.processon.com/view/link/5deeefdee4b0e2c298aa5596
我大概的講解思路也會是上面那樣,從上到下,每一個點細細地講。
閒言少敘,進入正題。第一講,先說說bean definition吧,這個東西,實在過重要了,核心的存儲結構啊。
你們能夠再想想,spring 當初剛出來的時候,主打的是ioc容器,容器裏裝了啥呢,bean啊!bean是什麼呢?
恩。。。我也不知道是啥,反正spring裏拿出來的就是bean。
行,那bean有什麼特徵嗎?
哦,bean是一個對象,有名字,有class類型,有scope(單例、prototype那些),有role(屬於應用的bean、仍是spring框架的bean),有是否延遲初始化(lazy-init),有它依賴的其餘bean,若是這個bean很差造(不能直接反射生成的話),可能還有個工廠方法和工廠bean呢,哎,好像還說漏了,反正挺多的。
那是否是每一個bean都有這些屬性呢?
仔細想一想,好像是的吧。
既然都有這些東西,那這個東西感受像是個模板了,就像是最近寫了年終總結,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方法。裏面的字段呢,下一講咱們詳細講解一下,會結合一些融會貫通的地方。
而後咱們看看這個接口有哪些實現吧?
能夠看到,這裏有兩個是我標紅了,由於他們特殊,特殊在他們不屬於spring-beans包,而是在spring-context包裏。後邊遇到了咱們再單說,這裏存疑。
再來看看這個接口的繼承圖:
咱們看到,這個接口有一個子接口,是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}
代碼在
仔細看了兩個接口 AnnotatedBeanDefinition
和BeanDefinition
,其實實現類都是差很少那幾個。
基本上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註解有莫大關係,這個咱們放後面講。
本篇就先到這裏,留了一些問題,放到後面(有些我也要查一下,哈哈)。下一講繼續。