標題中的咖啡罐指的是Spring容器,容器裏裝的固然就是被稱做Bean的豆子。本文咱們會以一個最基本的例子來熟悉Spring的容器管理和擴展點。
閱讀PDF版本java
首先咱們來聊聊這個問題,爲何咱們要用Spring來管理對象(的生命週期和對象之間的關係)而不是本身new一個對象呢?你們可能會回答是方便,爲了解耦。我我的以爲除了這兩個緣由以外,還有就是給予了咱們更多可能性。若是咱們以容器爲依託來管理全部的框架、業務對象,那麼不只僅咱們能夠無侵入調整對象的關係,還有可能無侵入隨時調整對象的屬性甚至悄悄進行對象的替換。這就給了咱們無限多的可能性,大大方便了框架的開發者在程序背後實現一些擴展。不只僅Spring Core自己以及Spring Boot大量依賴Spring這套容器體系,一些外部框架也由於這個緣由能夠和Spring進行無縫整合。
Spring能夠有三種方式來配置Bean,分別是最先期的XML方式、後來的註解方式以及如今最流行的Java代碼配置方式。spring
在前文parent模塊(空的一個SpringBoot應用程序)的基礎上,咱們先來建立一個beans模塊:apache
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>me.josephzhu</groupId> <artifactId>spring101-beans</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring101-beans</name> <description></description> <parent> <groupId>me.josephzhu</groupId> <artifactId>spring101</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
而後來建立咱們的豆子:c#
package me.josephzhu.spring101beans; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Component public class MyService implements InitializingBean, DisposableBean { public int increaseCounter() { this.counter++; return counter; } public int getCounter() { return counter; } public void setCounter(int counter) { this.counter = counter; } private int counter=0; public MyService(){ counter++; System.out.println(this + "#constructor:" + counter); } public String hello(){ return this + "#hello:" + counter; } @PreDestroy public void preDestroy() { System.out.println(this + "#preDestroy:" + counter); } @Override public void afterPropertiesSet() { counter++; System.out.println(this + "#afterPropertiesSet:" + counter); } @PostConstruct public void postConstruct(){ counter++; System.out.println(this + "#postConstruct:" + counter); } @Override public void destroy() { System.out.println(this + "#destroy:" + counter); } }
這裏能夠看到,咱們的服務中有一個counter字段,默認是0。這個類咱們實現了InitializingBean接口和DisposableBean接口,同時還建立了兩個方法分別加上了@PostConstruct和@PreDestroy註解。這兩套實現方式均可以在對象的額外初始化功能和釋放功能,註解的實現不依賴Spring的接口,侵入性弱一點。
接下去,咱們建立一個Main類來測試一下:app
package me.josephzhu.spring101beans; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import javax.annotation.Resource; @SpringBootApplication public class Spring101BeansApplication implements CommandLineRunner { @Autowired private ApplicationContext applicationContext; @Resource private MyService helloService; @Autowired private MyService service; public static void main(String[] args) { SpringApplication.run(Spring101BeansApplication.class, args); } @Override public void run(String... args) throws Exception { System.out.println("===================="); applicationContext.getBeansOfType(MyService.class).forEach((name, service)->{ System.out.println(name + ":" + service); }); System.out.println("===================="); System.out.println(helloService.hello()); System.out.println(service.hello()); } }
ApplicationContext直接注入便可,不必定須要用ApplicationContextAware方式來獲取。執行程序後能夠看到輸出以下:框架
me.josephzhu.spring101beans.MyService@7fb4f2a9#constructor:1 me.josephzhu.spring101beans.MyService@7fb4f2a9#postConstruct:2 me.josephzhu.spring101beans.MyService@7fb4f2a9#afterPropertiesSet:3 ==================== myService:me.josephzhu.spring101beans.MyService@7fb4f2a9 ==================== me.josephzhu.spring101beans.MyService@7fb4f2a9#hello:3 me.josephzhu.spring101beans.MyService@7fb4f2a9#hello:3 me.josephzhu.spring101beans.MyService@7fb4f2a9#preDestroy:3 me.josephzhu.spring101beans.MyService@7fb4f2a9#destroy:3
這裏咱們使用@Resource註解和@Autowired註解分別引用了兩次對象,能夠看到因爲Bean默認配置爲singleton單例,因此容器中MyService類型的對象只有一份,代碼輸出也能夠證實這點。此外,咱們也經過輸出看到了構造方法以及兩套Bean回調的次序是:maven
從剛纔的輸出中能夠看到,在剛纔的例子中,咱們爲Bean打上了@Component註解,容器爲咱們建立了名爲myService的MyService類型的Bean。如今咱們再來用Java代碼方式來建立相同類型的Bean,建立以下的文件:ide
package me.josephzhu.spring101beans; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration public class ApplicationConfig { @Bean(initMethod = "init") public MyService helloService(){ MyService myService = new MyService(); myService.increaseCounter(); return myService; } }
這裏能夠看到在定義Bean的時候咱們關聯了一個initMethod,所以咱們須要修改Bean加上這個方法:spring-boot
public void init() { counter++; System.out.println(this + "#init:" + counter); }
如今咱們運行代碼看看結果,獲得了以下錯誤:post
Field service in me.josephzhu.spring101beans.Spring101BeansApplication required a single bean, but 2 were found: - myService: defined in file [/Users/zyhome/IdeaProjects/spring101/spring101-beans/target/classes/me/josephzhu/spring101beans/MyService.class] - helloService: defined by method 'helloService' in class path resource [me/josephzhu/spring101beans/ApplicationConfig.class]
出現錯誤的緣由是@Autowired了一個MyService,@Resource註解由於使用Bean的名稱來查找Bean,因此並不會出錯,而@Autowired由於根據Bean的類型來查抄Bean找到了兩個匹配全部出錯了,解決方式很簡單,咱們在多個Bean裏選一個做爲主Bean。咱們修改一下MyService加上註解:
@Component @Primary public class MyService implements InitializingBean, DisposableBean
這樣,咱們的@Resource根據名字匹配到的是咱們@Configuration出來的Bean,而@Autowired根據類型+Primary匹配到了@Component註解定義的Bean,從新運行代碼來看看是否是這樣:
me.josephzhu.spring101beans.MyService@6cd24612#constructor:1 me.josephzhu.spring101beans.MyService@6cd24612#postConstruct:3 me.josephzhu.spring101beans.MyService@6cd24612#afterPropertiesSet:4 me.josephzhu.spring101beans.MyService@6cd24612#init:5 me.josephzhu.spring101beans.MyService@7486b455#constructor:1 me.josephzhu.spring101beans.MyService@7486b455#postConstruct:2 me.josephzhu.spring101beans.MyService@7486b455#afterPropertiesSet:3 ==================== myService:me.josephzhu.spring101beans.MyService@7486b455 helloService:me.josephzhu.spring101beans.MyService@6cd24612 ==================== me.josephzhu.spring101beans.MyService@6cd24612#hello:5 me.josephzhu.spring101beans.MyService@7486b455#hello:3 me.josephzhu.spring101beans.MyService@7486b455#preDestroy:3 me.josephzhu.spring101beans.MyService@7486b455#destroy:3 me.josephzhu.spring101beans.MyService@6cd24612#preDestroy:5 me.josephzhu.spring101beans.MyService@6cd24612#destroy:5
從輸出中咱們注意到幾點:
@Autowired @Qualifier("helloService") private MyService service;
咱們來繼續探索Spring容器提供給咱們的兩個有關Bean的重要擴展點。
好,咱們如今來實現這兩種類型的處理器,首先是用於修改Bean定義的處理器:
package me.josephzhu.spring101beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; @Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("helloService"); if (beanDefinition != null) { beanDefinition.setScope("prototype"); beanDefinition.getPropertyValues().add("counter", 10); } System.out.println("MyBeanFactoryPostProcessor"); } }
這裏,咱們首先找到了咱們的helloService(Java代碼配置的那個Bean),而後修改了它的屬性和Scope(還記得嗎,在以前的圖中咱們能夠看到,這兩項都是Bean的定義,定義至關於類描述,實例固然就是類實例了)。
而後,咱們再來建立一個修改Bean實例的處理器:
package me.josephzhu.spring101beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyService) { System.out.println(bean + "#postProcessAfterInitialization:" + ((MyService)bean).increaseCounter()); } return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyService) { System.out.println(bean + "#postProcessBeforeInitialization:" + ((MyService)bean).increaseCounter()); } return bean; } }
實現比較簡單,在這個處理器的兩個接口咱們都調用了一次增長計數器的操做。咱們運行代碼來看一下這兩個處理器執行的順序是否符合剛纔那個圖的預期:
MyBeanFactoryPostProcessor me.josephzhu.spring101beans.MyService@41330d4f#constructor:1 me.josephzhu.spring101beans.MyService@41330d4f#postProcessBeforeInitialization:11 me.josephzhu.spring101beans.MyService@41330d4f#postConstruct:12 me.josephzhu.spring101beans.MyService@41330d4f#afterPropertiesSet:13 me.josephzhu.spring101beans.MyService@41330d4f#init:14 me.josephzhu.spring101beans.MyService@41330d4f#postProcessAfterInitialization:15 me.josephzhu.spring101beans.MyService@6f36c2f0#constructor:1 me.josephzhu.spring101beans.MyService@6f36c2f0#postProcessBeforeInitialization:11 me.josephzhu.spring101beans.MyService@6f36c2f0#postConstruct:12 me.josephzhu.spring101beans.MyService@6f36c2f0#afterPropertiesSet:13 me.josephzhu.spring101beans.MyService@6f36c2f0#init:14 me.josephzhu.spring101beans.MyService@6f36c2f0#postProcessAfterInitialization:15 me.josephzhu.spring101beans.MyService@3b35a229#constructor:1 me.josephzhu.spring101beans.MyService@3b35a229#postProcessBeforeInitialization:2 me.josephzhu.spring101beans.MyService@3b35a229#postConstruct:3 me.josephzhu.spring101beans.MyService@3b35a229#afterPropertiesSet:4 me.josephzhu.spring101beans.MyService@3b35a229#postProcessAfterInitialization:5 ==================== me.josephzhu.spring101beans.MyService@6692b6c6#constructor:1 me.josephzhu.spring101beans.MyService@6692b6c6#postProcessBeforeInitialization:11 me.josephzhu.spring101beans.MyService@6692b6c6#postConstruct:12 me.josephzhu.spring101beans.MyService@6692b6c6#afterPropertiesSet:13 me.josephzhu.spring101beans.MyService@6692b6c6#init:14 me.josephzhu.spring101beans.MyService@6692b6c6#postProcessAfterInitialization:15 myService:me.josephzhu.spring101beans.MyService@3b35a229 helloService:me.josephzhu.spring101beans.MyService@6692b6c6 ==================== me.josephzhu.spring101beans.MyService@41330d4f#hello:15 me.josephzhu.spring101beans.MyService@6f36c2f0#hello:15 me.josephzhu.spring101beans.MyService@3b35a229#preDestroy:5 me.josephzhu.spring101beans.MyService@3b35a229#destroy:5
這個輸出結果有點長,第一行就輸出了MyBeanFactoryPostProcessor這是預料之中,Bean定義的修改確定是最早發生的。咱們看下輸出的規律,一、十一、十二、1三、1四、15出現了三次,之因此從1跳到了11是由於咱們的BeanFactoryPostProcessor修改了其中的counter屬性的值爲10。這說明了,咱們的helloService的初始化進行了三次:
這裏的輸出說明了幾點:
最後,咱們能夠修改BeanFactoryPostProcessor中的代碼把prototype修改成singleton看看是否咱們的helloService這個Bean恢復爲了單例:
MyBeanFactoryPostProcessor me.josephzhu.spring101beans.MyService@51891008#constructor:1 me.josephzhu.spring101beans.MyService@51891008#postProcessBeforeInitialization:11 me.josephzhu.spring101beans.MyService@51891008#postConstruct:12 me.josephzhu.spring101beans.MyService@51891008#afterPropertiesSet:13 me.josephzhu.spring101beans.MyService@51891008#init:14 me.josephzhu.spring101beans.MyService@51891008#postProcessAfterInitialization:15 me.josephzhu.spring101beans.MyService@49c90a9c#constructor:1 me.josephzhu.spring101beans.MyService@49c90a9c#postProcessBeforeInitialization:2 me.josephzhu.spring101beans.MyService@49c90a9c#postConstruct:3 me.josephzhu.spring101beans.MyService@49c90a9c#afterPropertiesSet:4 me.josephzhu.spring101beans.MyService@49c90a9c#postProcessAfterInitialization:5 ==================== myService:me.josephzhu.spring101beans.MyService@49c90a9c helloService:me.josephzhu.spring101beans.MyService@51891008 ==================== me.josephzhu.spring101beans.MyService@51891008#hello:15 me.josephzhu.spring101beans.MyService@51891008#hello:15 me.josephzhu.spring101beans.MyService@49c90a9c#preDestroy:5 me.josephzhu.spring101beans.MyService@49c90a9c#destroy:5 me.josephzhu.spring101beans.MyService@51891008#preDestroy:15 me.josephzhu.spring101beans.MyService@51891008#destroy:15
本次輸出結果的hello()方法明顯是同一個bean,結果中也沒出現三次一、十一、十二、1三、1四、15。
本文以探索的形式討論了下面的一些知識點: