class Programer {
Computer computer = new Mac2015();
private void work() {
computer.help();
}
}
複製代碼
此時有一個問題就是computer和programer耦合在一塊兒,這個programer不具有擴展性(它只會用mac2015),若是此時公司換了一批電腦Mac2016,那麼須要從新建立一個新的程序員類,這顯然是不合理的。從設計的角度來說,類自己就是定義的一個模板亦或是一種抽象,若是抽象和具體的實現綁定或者耦合在一塊兒,那麼就不是純粹的抽象了。
這也違背了設計模式中的don't call me法則。因此這個時候要把computer和programer解耦,解耦的方法很簡單。 computer的具體實現由調用方指定就ok,而不是在類內部自行指定。那麼類就須要暴露一些接口供外界實現注入功能。
常見的方法有三種java
這就是IOC的基本思路,而對於咱們web開發來說通常不存在調用方(其實也有不過在容器層面),因此如何保證類中屬性的動態注入,這個就要由Spring登場了
接下來,選擇何種注入方式以及須要注入哪些屬性是怎麼通知到Spring的呢? 常見的方法有兩種
程序員
這裏多提一句,bean的實例化方式能夠是普通的構造器構造也能夠是工廠方法構造 下面這個是經過靜態工廠注入的方式web
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
複製代碼
class指向的是包含工廠方法的類,並不是工廠方法返回的類
下面這個是經過普通工廠注入的方式spring
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
複製代碼
BeanFactorysql
默認lazy-load,因此啓動較快 經常使用的一個實現類是DefaultListableBeanFactory,該類還實現了BeanDefinitionRegistry接口,該接口擔當Bean註冊管理的角色 registerBeanDefinition#BeanDefinitionRegistry bean會以beanDefinition的形式註冊在BeanDefinitionRegistry中 BeanFactory接口中只定義了一些關於bean的查詢方法,而真正的工做須要其子類實現的其餘接口定義~apache
ApplicationContext設計模式
構建於BeanFactory基礎之上,非lazy,啓動時間較長,除了BeanFactory的基礎功能還提供了一些額外的功能(事件發佈、國際化信息支持等)緩存
基於配置文件的注入 咱們稱他爲手動app
<bean> <name="propertyName" ref="otherBean">ide
基於註解的注入 咱們稱他爲全自動
@Conponet/@Autowire/@Resource + <componet-scan>
還有註解+配置的半自動
<bean> + @autowire/@resource
通常最頂層是beans標籤 beans標籤有幾個獨特的屬性,對包含的全部bean生效
default-lazy-init
全部bean是否懶加載
default-autowire
全部bean內部注入的方式no(默認)/byName/byType/constrctor/autodetect
這裏byName/byType ≈ @resource/@autowire
default-dependency-check
是否進行依賴檢查 none(默認)
default-init-method
全部bean的初始化方法好比init
default-destroy-method
全部bean的銷燬方法好比destroy
基本上beans的全部屬性均可以對應到bean中,這裏提一下bean的scope屬性 關於Singleton和Prototype singleton類型的bean的存活時間和容器同樣長 prototype類型的bean的生命週期不禁容器維護,容器再也不擁有當前對象的引用,任由其自生自滅
想起當初解決的一個問題,持有對象的引用(new/reflect),可是這個對象未進行注入,如何利用Spring對其進行注入?
applicationContext.getAutowireCapableBeanFactory().
autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_NO, true);
複製代碼
先放結論,後面有具體的代碼論證(圖文結合效果更佳)
實踐是檢驗真理的惟一標準,舉個例子public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
private String carName;
private BeanFactory beanFactory;
private String beanName;
public Car() {
System.out.println("bean的構造函數");
}
public void setCarName(String carName) {
System.out.println("屬性注入");
this.carName = carName;
}
// 這是BeanFactoryAware接口方法
public void setBeanFactory(BeanFactory arg0) throws BeansException {
System.out
.println("BeanFactoryAware.setBeanFactory()");
this.beanFactory = arg0;
}
// 這是BeanNameAware接口方法
public void setBeanName(String arg0) {
System.out.println("BeanNameAware.setBeanName()");
this.beanName = arg0;
}
// 這是InitializingBean接口方法
public void afterPropertiesSet() throws Exception {
System.out
.println("InitializingBean.afterPropertiesSet()");
}
// 這是DiposibleBean接口方法
public void destroy() throws Exception {
System.out.println("DiposibleBean.destory()");
}
// 經過<bean>的init-method屬性指定的初始化方法
public void myInit() {
System.out.println("<bean>的init-method屬性指定的初始化方法");
}
// 經過<bean>的destroy-method屬性指定的初始化方法
public void myDestory() {
System.out.println("<bean>的destroy-method屬性指定的初始化方法");
}
}
複製代碼
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor() {
super();
System.out.println("BeanPostProcessor構造函數");
}
public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException {
if (arg1.equals("car")) {
System.out
.println("BeanPostProcessor.postProcessAfterInitialization()");
}
return arg0;
}
public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException {
if (arg1.equals("car")) {
System.out
.println("BeanPostProcessor.postProcessBeforeInitialization()");
}
return arg0;
}
}
複製代碼
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public MyBeanFactoryPostProcessor() {
super();
System.out.println("BeanFactoryPostProcessor構造函數");
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
System.out.println("BeanFactoryPostProcessor.postProcessBeanFactory()");
System.out.println("我如今能夠修改beanDefination可是我這裏就不修改了");
}
}
複製代碼
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
public MyInstantiationAwareBeanPostProcessor() {
super();
System.out
.println("InstantiationAwareBeanPostProcessorAdapter構造函數");
}
// 接口方法、實例化Bean以前調用
@Override
public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()");
}
return null;
}
// 接口方法、實例化Bean以後調用
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()");
}
return true;
}
// 接口方法、設置某個屬性時調用
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
if(beanName.equals("car")) {
System.out
.println("InstantiationAwareBeanPostProcessor.postProcessPropertyValues()");
}
return pvs;
}
}
複製代碼
<bean id="beanPostProcessor" class="test.MyBeanPostProcessor"/>
<bean id="instantiationAwareBeanPostProcessor" class="test.MyInstantiationAwareBeanPostProcessor"/>
<bean id="beanFactoryPostProcessor" class="test.MyBeanFactoryPostProcessor"/>
<bean id="car" class="test.Car" init-method="myInit" destroy-method="myDestory" scope="singleton" p:carName="BMW"/>
複製代碼
下面來見證下整個流程
public static void main(String[] args) {
System.out.println("開始啓動容器");
ApplicationContext factory = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
System.out.println("容器初始化成功");
Car car = factory.getBean("car",Car.class);
System.out.println("開始關閉容器");
((ClassPathXmlApplicationContext)factory).registerShutdownHook();
}
複製代碼
最終的console輸出爲:
開始啓動容器
BeanFactoryPostProcessor構造函數
BeanFactoryPostProcessor.postProcessBeanFactory()
我如今能夠修改beanDefination可是我這裏就不修改了
InstantiationAwareBeanPostProcessorAdapter構造函數
BeanPostProcessor構造函數
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
bean的構造函數
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
InstantiationAwareBeanPostProcessor.postProcessPropertyValues()
屬性注入
BeanNameAware.setBeanName()
BeanFactoryAware.setBeanFactory()
BeanPostProcessor.postProcessBeforeInitialization()
InitializingBean.afterPropertiesSet()
<bean>的init-method屬性指定的初始化方法
BeanPostProcessor.postProcessAfterInitialization()
容器初始化成功
開始關閉容器
DiposibleBean.destory()
<bean>的destroy-method屬性指定的初始化方法
複製代碼
1)容器啓動階段
容器須要依賴某些工具類(BeanDefinitionReader)對加載的Configuration MetaData進行解析和分析,並將分析後的信息編組爲相應的BeanDefinition,最後把這些保存了bean定義必 要信息的BeanDefinition,註冊到相應的BeanDefinitionRegistry,這樣容器啓動工做就完成了。
BeanDefinitionReader
1)用來和配置文件打交道
2)負責從對應的配置文件中讀取內容並將bean映射到BeanDefinition
3)而後將BeanDefinition註冊到BeanDefinitionRegistry中
eg:PropertiesBeanDefinitionReader、XmlBeanDefinitionReader
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
reader.loadBeanDefinitions("classpath:xxx");
return (BeanFactory) beanDefinitionRegistry;
複製代碼
惟一能插手容器啓動階段的大佬-----BeanFactoryPostProcessor
在容器實例化對象以前,對註冊到容器的BeanDefinition信息進行修改
下面是幾個比較典型的BeanFactoryPostProcessor
在實例化bean前,將bean配置中的佔位符替換爲實際值
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
複製代碼
# jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
複製代碼
在運行時,佔位符內的內容會被替換爲屬性文件中的匹配的value,這個工做就是由PlaceholderConfiguer完成的,不難看出這個類應該實現了BeanFactoryPostProcessor
即容器從XML格式的文件中讀取的都是字符串形式,最終應用程序倒是由各類類型的對象所構成。 要想完成這種由字符串到具體對象的轉換(無論這個轉換工做最終由誰來作),都須要這種轉換規則 相關的信息,而CustomEditorConfigurer就是幫助咱們傳達相似信息的。
以下咱們須要將String類型的2018/07/27轉爲date
<bean id="dateFoo" class="...DateFoo">
<property name="date">
<value>2018/07/27</value>
</property>
</bean>
複製代碼
用戶能夠自定義CustomEditorConfigurer,來指定string到特定類型轉換到邏輯
以前項目中使用JUNIT進行單元測試,可是每次單元測試都要加載全部的bean,項目規模若是較大,那麼單元測試啓動須要很長時間,爲了解決這個問題開發了一個簡單的BeanFactoryPostProcessor,原理很簡單就是將全部的bean都設置爲懶加載模式,單元測試中用到哪一個實例化哪一個
public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory;
Map<String, AbstractBeanDefinition> map = (Map<String, AbstractBeanDefinition>) ReflectionTestUtils.getField(fac, "beanDefinitionMap");
for (Map.Entry<String, AbstractBeanDefinition> entry : map.entrySet()) {
entry.getValue().setLazyInit(true);
}
}
}
複製代碼
2)Bean實例化階段 1.InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(Class beanClass,String beanName)
在執行Bean的構造器以前,若是有InstantiationAwareBeanPostProcessor那麼會先執行它的postProcessBeforeInstantiation()若是此時返回的bean不爲null,那麼不會再繼續執行後續的Bean流程,只執行postProcessAfterInitialization(),形成了"短路" 有一點須要注意的在提到InstantiationAwareBeanPostProcessor的方法的時候最好指定入參,由於它有好幾個相同名字入參不一樣的方法,容易發生混淆
2.對bean進行實例化
容器採用策略模式來決定採用何種方式初始化bean實例(反射 or CGLIB默認) 若是配置的bean有look-up or replace此時須要使用CGLIB建立代理類
3.InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(Class beanClass,String beanName)
4.InstantiationAwareBeanPostProcessor.postProcessPropertiesValues(PropertyValues pvs,PropertyDescriptor[] pds, Object bean, String beanName)
5.對bean進行依賴注入
實例化後的產物是BeanWrapper實例,它是對bean的包裹,接下來對這個包裹設置屬性值 Ps藉助Wrapper能夠用統一的方式對對象屬性進行注入,BeanWrapper會使用PropertyEditor進行屬性的類型轉換
6.檢查是否實現Aware接口(BeanFactory獨有)
BeanNameAware:將BeanName設置到當前實例中 BeanClassLoaderAware:將加載當前bean的ClassLoader設置到當前實例中 BeanFactoryAware:將BeanFactory設置到當前實例中
7.BeanPostProcessor.postProcessBeforeInitialization
不少ApplicationContext獨有的Aware接口也是經過BeanPostPorcessor實現屬性注入的
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher
(this.applicationContext); }
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }
return bean;
}
複製代碼
8.InititalizingBean.afterPropertiesSet()
9.init-method
10.BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)
11.容器初始化成功,正常執行業務邏輯
12.DisposableBean.destroy()
13.destroy-method
Spring對於單例的bean之間的循環依賴會進行處理(Set注入),換言之對於單例構造器注入以及prototype類型的bean沒法處理循環依賴。假設A依賴B,B又依賴於A,在實例化A的時候會將半成品的A放到緩存中,再去實例化所依賴的B,而在實例化B過程當中又須要去實例化A,此時直接將半成品的A填充到B中,就完成了循環依賴Bean的實例化。