Spring IOC 特性有哪些,不會讀不懂源碼!

做者:小傅哥
博客:https://bugstack.cnhtml

沉澱、分享、成長,讓本身和他人都能有所收穫!😄

1、前言

多線程、鎖、JVM調優,都背出花啦,怎麼一寫代碼仍是亂糟糟?java

爲何這些不管從書本、課堂、面試都顯得很是重要的知識,可是在實際的編程中沒有提高你的編碼能力呢?程序員

首先這些這些知識在實際的互聯網業務開發中,幾乎是不經常使用的,幾乎有鎖和多線程的場景,爲了性能的提高也基本都是採用分佈式設計和實現了。而這些看上去頗有技術含量的知識多數都被包裝在非業務邏輯功能的組件中,而程序員在作業務開發時候幾乎是關心不到。因此會了這些也幾乎不太可能就把你的編碼能提高起來,多數提高的是你在查複雜bug時候有一臂之力。面試

就像會漢字就能寫出詩詞歌賦嗎?懂RGB就能繪出山河大川嗎?能蹦跳就能夠舞出搖曳生姿嗎?那都是不可能的,不要想着屁股圍噶布就說會武術!算法

若是真的想把代碼寫好,就要一點點從積累數據結構和算法邏輯(不僅是機械式的刷幾道題就算了。你不理解爲何,刷再多也只是徒勞),接下來要作的是對設計模式和架構設計的理解,最終是不斷的運用和總結。在這個過程你會接觸到業務、產品、運營,編碼只是最後的具體實現,並非全流程中最重要的一部分,與編碼相比更重要的是邏輯設計。spring

2、面試題

謝飛機,小記!,此次放假一遍擼串一遍被Spring,嘿嘿,檢驗成果面試去!編程

面試官:飛機,今天準備咋樣,上次問你的都學會了嗎?設計模式

謝飛機:@Resource 是 JDK javax.annotation.Resource 提供的註解,哈哈哈哈哈,另外也學習了Bean的注入。緩存

面試官:挺好記住了一些,那你在作 Bean 注入學習的時候,有注意到 Spring IOC 的特性嗎,你都用到了什麼?數據結構

謝飛機:嗯,用到 Bean 的配置、BeanDefinitionRegistryPostProcessor 對 Bean 的定義、還有 FactoryBean

面試官:好,那今天再和你聊聊,alias、autowire、depends-on、factory-method、lookup-method等,實踐驗證下看看它們是怎麼應用的。

3、SpringIOC 特性

IOC(Inversion of Control),控制反轉的核心思想在於,資源的使用不禁使用各自管理,而是交給不使用資源的第三方進行管理。這樣的好處是資源是集中管理的,可配置、易維護,同時也下降了雙方的依賴度作到了低耦合。

早在1988年,Ralph E. Johnson & Brian Foote在論文《Designing Reusable Classes》

One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user's application code.
The framework often plays the role of the main program in coordinating and sequencing application activity.
This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.

接下來就給你們介紹一下 IOC 的一些核心特性,由於這些內容不只是面試考點,也是在開發中間件或者小組件時須要用到的功能類,歸納以下:

1. xml 配置

1.1 alias

測試類

public class UserService {

    private UserDao userDao;

    public UserService() {
        System.out.println("我被初始化了,UserService");
    }

    // ...get/set

}

xml配置

<bean id="userService" class="org.itstack.interview.UserService"/>
<!-- 起個別名 -->
<alias name="userService" alias="userService-alias01"/>
<!-- 別名的別名 -->
<alias name="userService-alias01" alias="userService-alias02"/>

單元測試

@Test
public void test_alias() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-alias.xml");
    logger.info("獲取 Bean:{}", beanFactory.getBean("userService"));
    logger.info("獲取 Bean 經過別名:{}", beanFactory.getBean("userService-alias01"));
    logger.info("獲取 Bean 經過別名的別名:{}", beanFactory.getBean("userService-alias02"));
}

測試結果

23:01:29.872 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean:org.itstack.interview.UserService@2a40cd94
23:01:29.872 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userService'
23:01:29.872 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean 經過別名:org.itstack.interview.UserService@2a40cd94
23:01:29.872 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userService'
23:01:29.872 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean 經過別名的別名:org.itstack.interview.UserService@2a40cd94

  • 目的:用於給 Bean 起別名
  • 使用:在 xml 配置裏咱們能夠給一個 Bean 起個別名,還能夠給別名起一個新的別名。

1.2 autowire

測試類

public class UserDao {
    public UserDao() {
        System.out.println("我被初始化了,UserDao");
    }
}

xml配置

<bean id="userDao" class="org.itstack.interview.UserDao"/>

<!-- 手動配置依賴 -->
<bean id="userService-by-property" class="org.itstack.interview.UserService">
    <property name="userDao" ref="userDao"/>
</bean>

<!-- 自動配置依賴 -->
<bean id="userService-by-autowire" class="org.itstack.interview.UserService" autowire="byName"/>

單元測試

@Test
public void test_autowire() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-autowire.xml");
    logger.info("獲取 Bean by 手動配置依賴:{}", beanFactory.getBean("userService-by-property"));
    logger.info("獲取 Bean by 自動配置依賴:{}", beanFactory.getBean("userService-by-autowire"));
}

測試結果

23:05:55.501 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean by 手動配置依賴:org.itstack.interview.UserService@679b62af
23:05:55.501 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userService-by-autowire'
23:05:55.501 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean by 自動配置依賴:org.itstack.interview.UserService@5cdd8682

  • 目的:autowire 用於把類中的屬性注入交給 Spring 管理
  • 使用:在 xml 配置中,有兩種方式分別是:手動配置依賴、自動配置依賴,手動的你們基本很經常使用,自動的配置通常可能更多的對於註解的使用。其實這裏的 autowire 和註解有同樣的做用,autowire 幾個可選項,byName、byType、constructor 等。

1.3 factory-method

測試類

public class StaticFactoryBean {

    static public UserDao getUserDaoByStatic(){
        return new UserDao();
    }

}

xml配置

<bean id="staticFactory-method" class="org.itstack.interview.StaticFactoryBean" factory-method="getUserDaoByStatic"/>

單元測試

@Test
public void test_factory_method() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-factory-method.xml");
    logger.info("獲取 Bean:{}", beanFactory.getBean("staticFactory-method"));
}

測試結果

23:15:28.950 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean:org.itstack.interview.UserDao@588df31b
23:15:28.950 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'staticFactory-bean'

  • 目的:標識靜態工廠的工廠方法(工廠方法是靜態的)
  • 使用:核心在於 xml 配置中添加 factory-method="getUserDaoByStatic",這樣就能夠在初始化時候調用對應靜態方法的實例化內容。

1.4 factory-bean

測試類

public class StaticFactoryBean {
    public UserDao getUserDao(){
        return new UserDao();
    }
}

xml配置

<bean id="staticFactory" class="org.itstack.interview.StaticFactoryBean"/>
<bean id="staticFactory-bean" factory-bean="staticFactory" factory-method="getUserDao"/>

單元測試

@Test
public void test_factory_bean_method() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-factory-method.xml");
    logger.info("獲取 Bean:{}", beanFactory.getBean("staticFactory-bean"));
}

測試結果

23:15:28.950 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean:org.itstack.interview.UserDao@33b37288

  • 目的:factory-bean,實例化工廠類
  • 使用:factory-bean、factory-method 須要配合使用,factory-method="getUserDao" 調用的是對應的費靜態方法返回實例化結果。

1.5 depends-on

xml配置

<bean id="userService" class="org.itstack.interview.UserService" depends-on="userDao"/>
<bean id="userDao" class="org.itstack.interview.UserDao"/>

單元測試

@Test
public void test_depends_on() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-depends-on.xml");
    logger.info("獲取 Bean:{}", beanFactory.getBean(UserService.class, "userService").getUserDao());
}

測試結果

我被初始化了,UserDao
我被初始化了,UserService
23:24:14.678 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean:org.itstack.interview.UserDao@45afc369

  • 目的:處理依賴初始化順序問題
  • 使用:若是不使用 depends-on="userDao",那麼按照 Spring 的配置最早初始化的是 UserService,當你有須要處理初始化依賴時則須要使用到這個配置。

1.6 lookup-method & ApplicationContextAware

測試類

public class UserDaoProvider implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public UserDao getUserDao() {
        return applicationContext.getBean("userDao", UserDao.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}

xml配置

<bean id="userDao" class="org.itstack.interview.UserDao" scope="prototype"/>
<bean id="provider" class="org.itstack.interview.UserDaoProvider"/>

單元測試

@Test
public void test_lookup_method() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-lookup-method.xml");
    logger.info("獲取 Bean:{}", beanFactory.getBean(UserDaoProvider.class, "provider").getUserDao());
    logger.info("獲取 Bean:{}", beanFactory.getBean(UserDaoProvider.class, "provider").getUserDao());
}

測試結果

我被初始化了,UserDao
16:29:25.813 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'userDao'
16:29:25.813 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean:org.itstack.interview.UserDao@1188e820
16:29:25.813 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'userDao'
我被初始化了,UserDao
16:29:25.814 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'userDao'
16:29:25.814 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean:org.itstack.interview.UserDao@2f490758

  • 目的:獲取單例下的原型模式,每次獲取都要有新的對象產生。
  • 使用:其實核心在於 ApplicationContextAware 的使用和 scope="prototype" 配置,Spring 內部實現爲使用 Cglib 方法,從新生成子類,重寫配置的方法和返回對象,達到動態改變的效果。

2. 接口類

2.1 FactoryBean

測試類

public class MyFactoryBean implements FactoryBean<UserDao> {

    @Override
    public UserDao getObject() throws Exception {
        return new UserDao();
    }

    @Override
    public Class<?> getObjectType() {
        return UserDao.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
    
}

xml配置

<bean id="userDao" class="org.itstack.interview.MyFactoryBean"/>

單元測試

@Test
public void test_factory_bean() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-factory-bean.xml");
    logger.info("獲取 Bean:{}", beanFactory.getBean("userDao"));
}

測試結果

23:36:19.339 [main] INFO  org.itstack.interview.test.ApiTest - 獲取 Bean:org.itstack.interview.UserDao@3bd94634

  • 目的:用於生成 Bean 的 Bean,叫 FactoryBean
  • 使用:其實這個使用在上一章節關於 Bean 如何注入到 Spring 已經提到過,在一些ORM框架、RPC-Starter等都有所應用。

2.2 BeanPostProcessor

測試類

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化前:" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化後:" + beanName);
        return bean;
    }
    
}

xml配置

<bean id="beanPostProcessor" class="org.itstack.interview.MyBeanPostProcessor"/>
<bean id="userDao" class="org.itstack.interview.UserDao"/>

單元測試

@Test
public void test_bean_post_processor() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-bean-post-processor.xml");
}

測試結果

初始化前:userDao
初始化後:userDao
16:38:32.686 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'userDao'

  • 目的:拿到 Bean 對象初始化先後的動做,作相應的處理
  • 使用:BeanPostProcessor 是 Spring 框架的擴展接口類,經過對這個接口的實現,就能夠在 Bean 實例化的過程當中作相關的動做,好比攔截之後發佈到註冊中心等。AOP 的操做也是經過 BeanPostProcessor 和 IOC 容器創建起聯繫。

2.3 BeanFactoryAware

測試類

public class MyBeanFactoryAware implements BeanFactoryAware {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        
    }
}

  • 目的:用於獲取運行時 Bean 的配置信息
  • 使用:BeanFactoryAware 的實現類能夠拿到 beanFactory,也就獲取到了bean的上下文信息,此時你想獲取一些對象的屬性就很是容易了。

4、總結

  • 以上咱們介紹了 Spring IOC 的經常使用配置特性和接口,雖然如今你們可能已經不多會使用 xml 配置對象,基本都是註解的方式。但在這些註解的背後依然會有相應的通用核心原理實現,只有把這部分知識總結清楚並學習源碼,才能更好的理解註解的使用是如何處理這些配置的。
  • 關於接口的類使用,FactoryBean、BeanPostProcessor、BeanFactoryAware、ApplicationContextAware,在平常的業務流程開發中幾乎接觸不到,但若是要作一些核心的組件設計或者是中間件的開發,就會使用的很是頻繁。若是對這部分知識的運用不了解,能夠參考:《SpringBoot 中間件設計和開發》
  • 後續會圍繞這些知識點來給你們介紹一些源碼的學習以及應用層的處理,Bean的建立、循環依賴的三級緩存解決方案等。也但願你們在學習的過程當中要多總結、思考、記錄,一點點的把知識棧建設完整。

5、系列推薦

相關文章
相關標籤/搜索