????我們程序中對象的運行有兩個目標(biāo)?第一要解決業(yè)務(wù)問題,第二要高效低耗的優(yōu)化業(yè)務(wù)的執(zhí)行。那么如何才能達(dá)成這樣的目標(biāo)?第一充分分析業(yè)務(wù),確定其對象,第二要思考解決業(yè)務(wù)的這些對象的運行性能。
????在Spring框架中不會幫我們解決業(yè)務(wù)問題,但可以從對象運行時的性能方面給予一定的設(shè)計槽畔。例如Spring為我們的對象賦予了很多個更加科學(xué)的特性,例如延遲加載膛薛,Bean的作用域迷捧,生命周期方法以及運行時的自動依賴注入(降低耦合,提高程序的可維護性)幸乒。
1. 延遲加載(懶加載 @Lazy)
? ? 當(dāng)一個對象占用內(nèi)存較大時懦底,或者創(chuàng)建之后暫時用不到的時候,可以配置延遲加載罕扎。延遲加載并不是延遲對類進行加載聚唐,而是在啟動時,暫時不創(chuàng)建類的實例腔召。如果想看一下內(nèi)存中的類是否被加載了杆查,可以通過JVM參數(shù)進行檢測,參數(shù)為-XX:+TraceClassLoading臀蛛。
2. 對象作用域分析(@Scope)
? ? 在實際的項目中內(nèi)存中的對象有一些可能要反復(fù)應(yīng)用很多次亲桦,有一些可能用完以后再也不用了或者說應(yīng)用次數(shù)很少了。對于經(jīng)常要重復(fù)使用的對象我們可以考慮存儲到池中(例如交給spring框架進行管理)浊仆,應(yīng)用次數(shù)很少的對象那就沒必要放到池中了客峭,用完以后讓它自己銷毀就可以了。在Spring項目工程中為了對這樣的對象進行設(shè)計和管理抡柿,提供了作用域特性的支持桃笙。
? ? 使用@Scope注解對類進行描述,用于指定類的實例作用域沙绝。不寫@Scope默認(rèn)就是單例(singleton)作用域搏明,這個作用域會配合延遲加載(@Lazy)特性使用,表示此類的實例在需要時可以創(chuàng)建一份并且將其存儲到spring的容器中(Bean池),需要的時候從池中取闪檬,以實現(xiàn)對象的可重用星著。假如一些對象應(yīng)用次數(shù)非常少,可以考慮不放入池中粗悯,進而使用@Scope("prototype")作用域?qū)︻愡M行描述,讓此類的對象何時需要何時創(chuàng)建虚循,用完以后,當(dāng)此對象不可達(dá)了样傍,則可以直接被GC系統(tǒng)銷毀横缔。
3. 對象生命周期方法
????程序中的每個對象都有生命周期丙曙,對象創(chuàng)建必盖,初始化化撕,應(yīng)用福侈,銷毀的這個過程稱之為對象的生命周期耸黑。在對象創(chuàng)建以后要初始化颜屠,應(yīng)用完成以后要銷毀時執(zhí)行的一些方法搀罢,我們可以稱之為生命周期方法艘绍。但不見得每個對象都會定義生命周期方法。在實際項目中往往一些池對象通常會定義這樣的一些生命周期方法(例如連接池)初狰。那這樣的方法在spring工程中如何進行標(biāo)識呢莫杈?通常要借助@PostConstruct和@PreDestroy注解對特定方法進行描述,例如:
package com.zhf.common.pool;
@Scope("singleton")
@Lazy
@Component
public class ObjectPool{//假設(shè)此對象為一個對象池
? ? public ObjectPool(){
? ? ? Systemd.out.println("ObjectPool()...")
? ? }
? ? @PostConstruct
? ? public void init(){?????//生命周期初始化方法
? ? ? System.out.println("init()");
? ? }
? ? @PreDestroy
? ? public void destory(){? ? // 生命周期銷毀方法
? ? System.out.println("destory()");
? ? }
}
其中:
1)@PostConstruct 注解描述的方法為生命周期初始化方法,在對象構(gòu)建以后執(zhí)行;
2)@PreDestroy 注解描述的方法為生命周期銷毀方法,此方法所在的對象,假如存儲到了spring容器,那這個對象在從spring容器移除之前會先執(zhí)行這個生命周期銷毀方法(prototype作用域?qū)ο蟛粓?zhí)行此方法);
4. SpringBoot 項目中的依賴注入過程分析
在這個案例中單元測試類CacheTests中定義一個Cache接口類型的屬性奢入,然后由Spring框架完成對cache類型屬性值的注入筝闹。
代碼編寫及測試分析
第一步:定義Cache接口,代碼如下:
package com.zhf.common.cache;
public interface Cache {
}
第二步:定義Cache接口實現(xiàn)類SoftCache腥光,代碼如下:
package com.zhf.common.cache;
@Component
public class SoftCache implements Cache{
}
第三步:定義Cache接口實現(xiàn)類WeakCache关顷,代碼如下:
package com.zhf.common.cache;
@Component
public class WeakCache implements Cache{
}
第四步:定義CacheTests單元測試類,代碼如下:
package com.zhf.common.cache;
import org.junit.jupiter.api.Test;
?
@SpringBootTest ? ?
public class CacheTests {
?? @Autowired
?? @Qualifier("softCache")
?? private Cache cache;
?? @Test
?? public void testCache() {
? ? ?? System.out.println(cache);
?? }
}
????其中柴我,@Autowired由spring框架定義解寝,用于描述類中屬性或相關(guān)方法(例如構(gòu)造方法)。Spring框架在項目運行時假如發(fā)現(xiàn)由他管理的Bean對象中有使用@Autowired注解描述的屬性或方法艘儒,可以按照指定規(guī)則為屬性賦值(DI)聋伦。其基本規(guī)則是:首先要檢測容器中是否有與屬性或方法參數(shù)類型相匹配的對象,假如有并且只有一個則直接注入界睁。其次觉增,假如檢測到有多個,還會按照@Autowired描述的屬性或方法參數(shù)名查找是否有名字匹配的對象翻斟,有則直接注入逾礁,沒有則拋出異常。最后访惜,假如我們有明確要求嘹履,必須要注入類型為指定類型,名字為指定名字的對象還可以使用@Qualifier注解對其屬性或參數(shù)進行描述(此注解必須配合@Autowired注解使用)债热。
第五步:運行CacheTests檢測輸出結(jié)果砾嫉,基于結(jié)果理解其注入規(guī)則。
5.?依賴注入方式(DI)
public class CacheService {
? ?@Autowired
? ?@Qualifier("softCache")
? ? private Cache cache;
//1. 通過構(gòu)造函數(shù)實現(xiàn)值的注入
//? ? @Autowired//可以描述構(gòu)造方法,只有這一個構(gòu)造函數(shù)時窒篱,可以省略
//? ? public CacheService(@Qualifier("weakCache") Cache cache){ //@Qualifier可以描述參數(shù)
//? ? ? ? this.cache=cache;
//? ? }
? ? //當(dāng)類中有多個構(gòu)造函數(shù)時焕刮,但是構(gòu)造函數(shù)又沒有使用@Autowired注解修飾,優(yōu)先使用無參構(gòu)造
//? ? public CacheService(){}
? ? //2. 通過set方法為屬性賦值
//? ? @Autowired
//? ? public void setCache(@Qualifier("weakCache") Cache cache){
//? ? ? ? System.out.println("===setCache()===");
//? ? ? ? this.cache=cache;
//? ? }
? ? public Cache getCache() {
return cache;
}
}