還是舉個例子,我有一個朋友小汪他遠赴南方某城市打工。然後安定下來後他的朋友很想來家裡玩,但是呢我這個朋友家裡搞的很亂,所以他不好意思請朋友來家裡玩。這時我的另一個朋友說那請一個保姆把家裡好好整理一下就可以了,然後給他介紹了一個保姆大S(PS:本文無意指向任何人,因為Spring的字首是S)然後就把家裡整理得井井有條,就請朋友來家裡玩了。
好了引入正文,很早很早的Java開發者應該熟悉,最早的時候我們前端訪問後端都是需要自己寫Servlet的,大概是一個介面寫一個Servlet。Java開發又是物件導向的程式設計,我們程式裡面寫了new 了很多的物件。寫了很多個Servlet,物件很難管理造成我們的程式很亂,都看不下去。後面Spring來了物件都交給了Spring管理,Servlet相關的也都交給了SpringMVC,這樣我們開發就順利多了。好了這下懂我上面舉的例子了吧,懂得保姆是什麼意思了吧【Spring就像一個管家,一個保姆】。所以多瞭解Spring相關知識我們提高開發效率有很大的幫助。既然我們的物件交給了Spring管理,那我們的物件怎麼生成的呢,就讓我們一起看下。
我們在使用Spring的時候,容器中的Bean在我們專案啟動的時候都已經給我們生成了,直接使用就行了。容器啟動的時候會呼叫這個方法:
AbstractApplicationContext.refresh()
然後就會呼叫下面這個方法:
// Instantiate all remaining (non-lazy-init) singletons. // 翻譯一下就是 例項化所有非懶載入的Bean finishBeanFactoryInitialization(beanFactory);
如圖refresh中的方法,它再次呼叫的每個方法都很重要,例項化所有單例Bean的方法在這個方法的最後呼叫
我們寫的物件基本都在這個方法內進行例項化。【PS方法只講一些很重要的,具體的更詳細方法呼叫我會在文章後面的流程圖中展示出來。】
DefaultListableBeanFactory.preInstantiateSingletons()。 @Override public void preInstantiateSingletons() throws BeansException { // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. // 獲取所有的要例項化的Bean的名稱 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... // 開始初始化單例的Bean for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // Bean 不是抽象的,是單例的,不是懶載入的進入如下分支 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 如果是FactoryBean進入此分支。本次只聊自己開發寫的非FactoryBean // 所以聊else下面的分支。 if (isFactoryBean(beanName)) { // FactoryBean的名稱很特別 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { // 非 FactoryBean進入此分支 getBean(beanName); } } } }
然後會進入如下方法。
AbstractBeanFactory.doGetBean() 的方法。 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // 這個方法主要是獲取Bean的名稱,一些Bean的名稱可能命名的比較特別 // 需要進行轉換。 final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. // 首先先從容器的快取中獲取Bean,如果容器中已經存在,直接返回。 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. // 先檢查這個Bean是否在建立中,如果在建立中丟擲異常 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 標記Bean為正在建立中。 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // Create bean instance. // 如果Bean是單例開始建立Bean . // 後面判斷還有Prototype(多例)不是要講的重點,程式碼刪除了。 if (mbd.isSingleton()) { // 這個方法是Java 8的 lambda 寫法,這個方法裡面會把建立好的 // Bean放到Spring容器中,後面再獲取這個Bean直接從容器中獲取了。 sharedInstance = getSingleton(beanName, () -> { try { // 正式開始建立Bean 。 return createBean(beanName, mbd, args); } catch (BeansException ex) { // 建立過程出現異常,銷燬Bean destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } return (T) bean; }
然後是正式真正的建立Bean的方法如下:
AbstractAutowireCapableBeanFactory.createBean() 的方法。 @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { RootBeanDefinition mbdToUse = mbd; try { // doCreateBean 是Spring正在做事的方法。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); } protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. // 例項化Bean 先建立一個BeanWrapper .這個方法裡面Spring 一般為預設 // 無參的構造方法建立物件,所以大家如果重寫物件的構造方法的時候,一定 // 要把無參構造方法也寫出來。要不然某些情況下啟動Spring容器可能會報錯。 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // Initialize the bean instance. Object exposedObject = bean; try { // 為Bean的屬性賦值。 populateBean(beanName, mbd, instanceWrapper); // 初始化Bean 。 exposedObject = initializeBean(beanName, exposedObject, mbd); } return exposedObject; } // 初始化Bean。 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { // 如果你的Bean實現了Spring內建的Aware方法,會在這裡執行 invokeAwareMethods(beanName, bean); Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // 執行Bean的初始化前置處理器,很重要也就是Spring的鉤子函式 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // 執行Bean的初始化方法 invokeInitMethods(beanName, wrappedBean, mbd); } if (mbd == null || !mbd.isSynthetic()) { // 執行Bean的後置處理器,也很重要。 // 很多寫底層架構的人都會對此鉤子方法靈活應用 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
PS:下面是Bean例項化的詳細的流程圖,由於畫好後的整個流程圖無法完全儲存,只有一張一張的截圖了。圖片一張一張往下看就是整個完整的流程,自己可以找著圖片一步一步看,就會對Bean的整個流程很清楚了。
讀完熟悉了Spring例項化的流程你能做些什麼呢?
1:比如實現BeanPostProcessor。A初始化前和後分別會執行下面2個方法。
@Component public class A implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName); return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName); return null; } }
2:實現InitializingBeanA初始化的時候會執行以下方法。
@Component public class A implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("執行InitializingBean的afterPropertiesSet方法"); } }
上面實現的方法都會在A例項化的時候執行,如果你寫的業務邏輯有需要在A例項化時候執行的就可以使用上面的方法完成。
歡迎跟著圖中走一遍原始碼,相信你會對Spring建立Bean的流程更加了解。看一些原始碼你的思路會更清晰,寫程式碼也更得心應手,寫程式碼的時候你可能不自己覺的就按照這些大神寫程式碼的思路去完成高質量的程式碼。