前言
學(xué)習(xí)過Spring框架的人一定都會(huì)聽過Spring的IOC(控制反轉(zhuǎn)) 這個(gè)概念,對(duì)于初學(xué)Spring的人來說,總覺得IOC是模糊不清的,是很難理解的,今天和大家分享網(wǎng)上的一些技術(shù)大牛們對(duì)Spring框架的IOC的理解以及談?wù)勎覍?duì)Spring IOC的理解。
讀者福利:私信回復(fù)【111】獲取整理好的spring全家桶學(xué)習(xí)筆記和面試題資料(1184頁(yè)P(yáng)DF文檔)
IOC是什么
IOC——Inversion of Control,即“控制反轉(zhuǎn)”,不是什么技術(shù),而是一種設(shè)計(jì)思想。在JAVA開發(fā)中,IOC意味著將你設(shè)計(jì)好的對(duì)象交給容器控制,而不是傳統(tǒng)的在你的對(duì)象內(nèi)部直接控制。如何理解好IOC呢?理解好IOC的關(guān)鍵是要明確“誰控制誰,控制什么,為何是反轉(zhuǎn)(有反轉(zhuǎn)就應(yīng)該有正轉(zhuǎn)了),哪些方面反轉(zhuǎn)了”,那我們來深入分析一下:
(1)誰控制誰,控制什么:傳統(tǒng)Java SE程序設(shè)計(jì),我們直接在對(duì)象內(nèi)部通過new進(jìn)行創(chuàng)建對(duì)象,是程序主動(dòng)去創(chuàng)建依賴對(duì)象;而IoC是有專門一個(gè)容器來創(chuàng)建這些對(duì)象,即由Ioc容器來控制對(duì) 象的創(chuàng)建;誰控制誰?當(dāng)然是IoC 容器控制了對(duì)象;控制什么?那就是主要控制了外部資源獲取(不只是對(duì)象包括比如文件等)。
(2)為何是反轉(zhuǎn),哪些方面反轉(zhuǎn)了:有反轉(zhuǎn)就有正轉(zhuǎn),傳統(tǒng)應(yīng)用程序是由我們自己在對(duì)象中主動(dòng)控制去直接獲取依賴對(duì)象,也就是正轉(zhuǎn);而反轉(zhuǎn)則是由容器來幫忙創(chuàng)建及注入依賴對(duì)象;為何是反轉(zhuǎn)?因?yàn)橛扇萜鲙臀覀儾檎壹白⑷胍蕾噷?duì)象,對(duì)象只是被動(dòng)的接受依賴對(duì)象,所以是反轉(zhuǎn);哪些方面反轉(zhuǎn)了?依賴對(duì)象的獲取被反轉(zhuǎn)了。
用圖例說明一下,傳統(tǒng)程序設(shè)計(jì)如圖,都是主動(dòng)去創(chuàng)建相關(guān)對(duì)象然后再組合起來:
當(dāng)有了IOC的容器后,在客戶端類中不再主動(dòng)去創(chuàng)建這些對(duì)象了,如圖所示:
接下來我們說一下IOC的4個(gè)特性
1. lazy-Init延遲加載
Bean對(duì)象的延遲加載(延遲創(chuàng)建)
ApplicationContext 容器的默認(rèn)?為是在啟動(dòng)服務(wù)器時(shí)將所有 singleton bean 提前進(jìn)?實(shí)例化。提前實(shí)例化意味著作為初始化過程的?部分,ApplicationContext實(shí)例會(huì)創(chuàng)建并配置所有的singleton bean。
1.1 XML方式開啟延遲加載:
lazy-init="" 配置bean對(duì)象的延遲加載 ,true或者false false就是立即加載
<bean id="lazyResult" class="com.lagou.edu.pojo.Result" lazy-init="false"></bean>
我們先來看一下當(dāng)lazy-init="false" 也就是立即加載的時(shí)候:
可以看到,在容器啟動(dòng)后,getBean之前,lazyResult這個(gè)bean已經(jīng)存在了。
然后我們把lazy-init="true",設(shè)置為true
然后我們F8往下走一步:
發(fā)現(xiàn)出現(xiàn)了lazyResult
1.2 注解開啟延遲加載:
@Lazy:
1.3全局配置——default-lazy-init="":
在bean的根標(biāo)簽中:
應(yīng)用場(chǎng)景:
(1)開啟延遲加載?定程度提?容器啟動(dòng)和運(yùn)轉(zhuǎn)性能
(2)對(duì)于不常使?的 Bean 設(shè)置延遲加載,這樣偶爾使?的時(shí)候再加載,不必要從?開始該 Bean 就占?資源
2. FactoryBean和BeanFactory
2.1 BeanFactory
容器的頂級(jí)接口,定義了容器的一些基礎(chǔ)行為,負(fù)責(zé)生產(chǎn)和管理Bean的一個(gè)工廠,具體使用它下面的子接口類型,比如ApplicationContext
2.2 FactoryBean
spring中的bean有兩種
- 普通bean
- 工廠bean(FactoryBean)
可以生產(chǎn)某一個(gè)類型的bean實(shí)例(返回給我們),也就是說我們可以借助于它自定義bean的創(chuàng)建過程。
Bean創(chuàng)建的三種?式中的靜態(tài)?法和實(shí)例化?法和FactoryBean作?類似,F(xiàn)actoryBean使?較多,尤其在Spring框架?些組件中會(huì)使?,還有其他框架和Spring框架整合時(shí)使?
//可以讓我們自定義Bean的創(chuàng)建過程,完成復(fù)雜bean定義
public interface FactoryBean<T> {
//返回FactoryBean創(chuàng)建的實(shí)例,如果isSingleton返回true,則該實(shí)例會(huì)放到Spring容器的單例緩存池中Map
@Nullable
T getObject() throws Exception;
//返回FactoryBean創(chuàng)建的bean類型
@Nullable
Class<?> getObjectType();
//返回作用域是否單例
default boolean isSingleton() {
return true;
}
}
2.2.1 新建類CompanyFactoryBean,實(shí)現(xiàn)FactoryBean接口,并重寫方法:
public class CompanyFactoryBean implements FactoryBean<Company> {
private String companyInfo;//注入公司名稱,地址,規(guī)模 以逗號(hào)分隔
public void setCompanyInfo(String companyInfo) {
this.companyInfo = companyInfo;
}
@Override
public Company getObject() throws Exception {
//創(chuàng)建復(fù)雜對(duì)象Company
Company company=new Company();
String[] split = companyInfo.split(",");
company.setName(split[0]);
company.setAddress(split[1]);
company.setScale(Integer.parseInt(split[2]));
return company;
}
@Override
public Class<?> getObjectType() {
//返回bean的類型
return Company.class;
}
@Override
public boolean isSingleton() {
//是否是單例
return true;
}
}
public class Company {
private String name;
private String address;
private int scale;
//省略getset 和toString
}
2.2.2 xml文件中配置bean
<bean id="companyBean" class="com.lagou.edu.factory.CompanyFactoryBean">
<property name="companyInfo" value="拉鉤,中關(guān)村,500"></property>
</bean>
2.2.3 測(cè)試
@org.junit.Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Object companyBean = applicationContext.getBean("companyBean");
System.out.println(companyBean);
}
//結(jié)果返回的是 Company{name='拉鉤', address='中關(guān)村', scale=500}
雖然在xml配置文件中配置的bean的class="com.lagou.edu.factory.CompanyFactoryBean" 但是返回的Company類型。
如何返回CompanyFactoryBean類型呢?
打印結(jié)果為:com.lagou.edu.factory.CompanyFactoryBean@545b995e
3. 后置處理器
Spring提供了兩種后處理bean的擴(kuò)展接?,分別為 BeanPostProcessor 和BeanFactoryPostProcessor,兩者在使?上是有所區(qū)別的。
??初始化(BeanFactory)—> Bean對(duì)象
在BeanFactory初始化之后可以使?BeanFactoryPostProcessor進(jìn)?后置處理做?些事情
在Bean對(duì)象實(shí)例化(并不是Bean的整個(gè)?命周期完成)之后可以使?BeanPostProcessor進(jìn)?后置處理做?些事情
注意:對(duì)象不?定是springbean,?springbean?定是個(gè)對(duì)象
3.1 SpringBean生命周期圖
按照上述描述的打印一下。看看是否一致:
//實(shí)現(xiàn)了BeanNameAware、BeanFactoryAware、ApplicationContextAware、InitializingBean,DisposableBean接口
public class Result implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {
private String status;
private String message;
//省略getset toString方法
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("4.BeanFactoryAware:"+beanFactory);
}
@Override
public void setBeanName(String name) {
System.out.println("3.BeanNameAware:"+name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("5.ApplicationContextAware:"+applicationContext);
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("7.InitializingBean");
}
public void initMethodTest(){
System.out.println("8.initMethod");
}
@PostConstruct
public void postCoustrcut(){
System.out.println("postCoustrcut");
}
//銷毀之前執(zhí)行
@PreDestroy
public void preDestroy(){
System.out.println("銷毀之前執(zhí)行");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean");
}
}
/**
攔截實(shí)例化之后的對(duì)象(實(shí)例化了 并且屬性注入了)
攔截所有的
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("lazyResult".equalsIgnoreCase(beanName)){
System.out.println("MyBeanPostProcessor before");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("lazyResult".equalsIgnoreCase(beanName)){
System.out.println("MyBeanPostProcessor After");
}
return bean;
}
}
//XML配置文件中:
<bean id="lazyResult" class="com.lagou.edu.pojo.Result" init-method="initMethodTest"></bean>
//測(cè)試:
@org.junit.Test
public void testBeanLazy(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Object lazyResult = applicationContext.getBean("lazyResult");
System.out.println(lazyResult);
applicationContext.close();
}
打印出:
4. 其他: