本文節(jié)選自《Spring 5核心原理》
Spring IoC容器還有一些高級特性,如使用lazy-init屬性對Bean預初始化、使用FactoryBean產(chǎn)生或者修飾Bean對象的生成、IoC容器在初始化Bean過程中使用BeanPostProcessor后置處理器對Bean聲明周期事件進行管理等。
1 關(guān)于延時加載
我們已經(jīng)知道,IoC容器的初始化過程就是對Bean定義資源的定位、載入和注冊,此時容器對Bean的依賴注入并沒有發(fā)生,依賴注入是在應用程序第一次向容器索取Bean時通過getBean()方法完成的。
當Bean定義資源的< bean>元素中配置了lazy-init=false屬性時,容器將會在初始化時對所配置的Bean進行預實例化,Bean的依賴注入在容器初始化時就已經(jīng)完成。這樣,當應用程序第一次向容器索取被管理的Bean時,就不用再初始化和對Bean進行依賴注入了,而是直接從容器中獲取已經(jīng)完成依賴注入的Bean,提高了應用程序第一次向容器獲取Bean的性能。
1.1. refresh()方法
IoC容器讀入已經(jīng)定位的Bean定義資源是從refresh()方法開始的,我們從AbstractApplicationContext類的refresh()方法入手分析,回顧一下源碼:
@Override
public void refresh() throws BeansException, IllegalStateException {
...
//子類的refreshBeanFactory()方法啟動
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
...
}
在refresh()方法中ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();啟動了Bean定義資源的載入、注冊過程。finishBeanFactoryInitialization()方法是對注冊后的Bean定義中的預實例化(lazy-init=false,Spring默認進行預實例化,即為true)的Bean進行處理的地方。
1.2. 使用finishBeanFactoryInitialization()處理預實例化的Bean
當Bean定義資源被載入IoC容器之后,容器將Bean定義資源解析為容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu)BeanDefinition,并注冊到容器中,AbstractApplicationContext類中的finishBeanFactoryInitialization()方法對配置了預實例化屬性的Bean進行預初始化,源碼如下:
//對配置了lazy-init屬性的Bean進行預實例化處理
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//這是Spring 3新加的代碼,為容器指定一個轉(zhuǎn)換服務(ConversionService)
//在對某些Bean屬性進行轉(zhuǎn)換時使用
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders (strVal));
}
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
//為了使類型匹配,停止使用臨時的類加載器
beanFactory.setTempClassLoader(null);
//緩存容器中所有注冊的BeanDefinition元數(shù)據(jù),以防被修改
beanFactory.freezeConfiguration();
//對配置了lazy-init屬性的單例模式的Bean進行預實例化處理
beanFactory.preInstantiateSingletons();
}
其中ConfigurableListableBeanFactory是一個接口,preInstantiateSingletons()方法由其子類DefaultListableBeanFactory提供。
1.3. 對配置了lazy-init屬性的單例模式的Bean的預實例化
對配置了lazy-init屬性的單例模式的Bean的預實例化相關(guān)源碼如下:
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
//獲取指定名稱的Bean定義
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//Bean不是抽象的,是單例模式的,且lazy-init屬性配置為false
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//如果指定名稱的Bean是創(chuàng)建容器的Bean
if (isFactoryBean(beanName)) {
//FACTORY_BEAN_PREFIX="&",當Bean名稱前面加"&"符號
//時,獲取的是容器對象本身,而不是容器產(chǎn)生的Bean
//調(diào)用getBean方法,觸發(fā)Bean實例化和依賴注入
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
//標識是否需要預實例化
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
//一個匿名內(nèi)部類
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
((SmartFactoryBean<?>) factory).isEagerInit(),
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
//調(diào)用getBean()方法,觸發(fā)Bean實例化和依賴注入
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
通過對lazy-init處理源碼的分析可以看出,如果設置了lazy-init屬性,則容器在完成Bean定義的注冊之后,會通過getBean()方法觸發(fā)指定Bean的初始化和依賴注入。如前所述,這樣當應用程序第一次向容器索取所需的Bean時,容器不再需要對Bean進行初始化和依賴注入,可直接從已經(jīng)完成實例化和依賴注入的Bean中取一個現(xiàn)成的Bean,提高了第一次獲取Bean的性能。
2 關(guān)于FactoryBean和BeanFactory
Spring中,有兩個很容易混淆的類:BeanFactory和FactoryBean。
BeanFactory:Bean工廠,是一個工廠(Factory),Spring IoC容器的最高層接口就是BeanFactory,它的作用是管理Bean,即實例化、定位、配置應用程序中的對象及建立這些對象之間的依賴。
FactoryBean:工廠Bean,是一個Bean,作用是產(chǎn)生其他Bean實例。這種Bean沒有什么特別的要求,僅需要提供一個工廠方法,該方法用來返回其他Bean實例。在通常情況下,Bean無須自己實現(xiàn)工廠模式,Spring容器擔任工廠的角色;在少數(shù)情況下,容器中的Bean本身就是工廠,其作用是產(chǎn)生其他Bean實例。
當用戶使用容器時,可以使用轉(zhuǎn)義字符“&”來得到FactoryBean本身,以區(qū)別通過FactoryBean產(chǎn)生的實例對象和FactoryBean對象本身。在BeanFactory中通過如下代碼定義了該轉(zhuǎn)義字符:
String FACTORY_BEAN_PREFIX = "&";
如果myJndiObject是一個FactoryBean,則使用&myJndiObject得到的是myJndiObject對象,而不是myJndiObject產(chǎn)生的對象。
2.1. FactoryBean源碼
//工廠Bean,用于產(chǎn)生其他對象
public interface FactoryBean<T> {
//獲取容器管理的對象實例
@Nullable
T getObject() throws Exception;
//獲取Bean工廠創(chuàng)建的對象的類型
@Nullable
Class<?> getObjectType();
//Bean工廠創(chuàng)建的對象是否是單例模式的,如果是,
//則整個容器中只有一個實例對象,每次請求都返回同一個實例對象
default boolean isSingleton() {
return true;
}
}
2.2. AbstractBeanFactory的getBean()方法
在分析Spring IoC容器實例化Bean并進行依賴注入的源碼時,提到在getBean()方法觸發(fā)容器實例化Bean時會調(diào)用AbstractBeanFactory的doGetBean()方法,其重要源碼如下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
...
BeanFactory parentBeanFactory = getParentBeanFactory();
//當前容器的父容器存在,且當前容器中不存在指定名稱的Bean
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//解析指定Bean名稱的原始名稱
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
//委派父容器根據(jù)指定名稱和顯式的參數(shù)查找
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
//委派父容器根據(jù)指定名稱和類型查找
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
...
return (T) bean;
}
//獲取給定Bean的實例對象,主要完成FactoryBean的相關(guān)處理
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
//容器已經(jīng)得到了Bean實例對象,這個實例對象可能是一個普通的Bean,
//也可能是一個工廠Bean,如果是一個工廠Bean,則使用它創(chuàng)建一個Bean實例對象,
//如果調(diào)用本身就想獲得一個容器的引用,則返回這個工廠Bean實例對象
//如果指定的名稱是容器的解引用(dereference,即對象本身而非內(nèi)存地址)
//且Bean實例也不是創(chuàng)建Bean實例對象的工廠Bean
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
//如果Bean實例不是工廠Bean,或者指定名稱是容器的解引用
//調(diào)用者獲取對容器的引用時,直接返回當前的Bean實例
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
//處理指定名稱不是容器的解引用,或者根據(jù)名稱獲取的Bean實例對象是一個工廠Bean
//使用工廠Bean創(chuàng)建一個Bean的實例對象
Object object = null;
if (mbd == null) {
//從Bean工廠緩存中獲取指定名稱的Bean實例對象
object = getCachedObjectForFactoryBean(beanName);
}
//讓Bean工廠生產(chǎn)指定名稱的Bean實例對象
if (object == null) {
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
//如果從Bean工廠生產(chǎn)的Bean是單例模式的,則緩存
if (mbd == null && containsBeanDefinition(beanName)) {
//從容器中獲取指定名稱的Bean定義,如果繼承了基類,則合并基類的相關(guān)屬性
mbd = getMergedLocalBeanDefinition(beanName);
}
//如果從容器得到了Bean定義信息,并且Bean定義信息不是虛構(gòu)的,
//則讓工廠Bean生產(chǎn)Bean實例對象
boolean synthetic = (mbd != null && mbd.isSynthetic());
//調(diào)用FactoryBeanRegistrySupport類的getObjectFromFactoryBean()方法
//實現(xiàn)工廠Bean生產(chǎn)Bean實例對象的過程
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
在上面獲取給定Bean的實例對象的getObjectForBeanInstance()方法中,會調(diào)用FactoryBean- RegistrySupport類的getObjectFromFactoryBean()方法,該方法實現(xiàn)了Bean工廠生產(chǎn)Bean實例對象。
2.3. AbstractBeanFactory生產(chǎn)Bean實例對象
AbstractBeanFactory類中生產(chǎn)Bean實例對象的主要源碼如下:
//Bean工廠生產(chǎn)Bean實例對象
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
//Bean工廠是單例模式,并且Bean工廠緩存中存在指定名稱的Bean實例對象
if (factory.isSingleton() && containsSingleton(beanName)) {
//多線程同步,以防止數(shù)據(jù)不一致
synchronized (getSingletonMutex()) {
//直接從Bean工廠的緩存中獲取指定名稱的Bean實例對象
Object object = this.factoryBeanObjectCache.get(beanName);
//如果Bean工廠緩存中沒有指定名稱的實例對象,則生產(chǎn)該實例對象
if (object == null) {
//調(diào)用Bean工廠的獲取對象的方法生產(chǎn)指定Bean的實例對象
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
}
//將生產(chǎn)的實例對象添加到Bean工廠的緩存中
this.factoryBeanObjectCache.put(beanName, object);
}
}
return object;
}
}
//調(diào)用Bean工廠的獲取對象的方法生產(chǎn)指定Bean的實例對象
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
//調(diào)用Bean工廠的方法生產(chǎn)指定Bean的實例對象
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
//實現(xiàn)PrivilegedExceptionAction接口的匿名內(nèi)部類
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
factory.getObject(), acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//調(diào)用BeanFactory接口實現(xiàn)類的創(chuàng)建對象方法
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
//創(chuàng)建出來的實例對象為null,或者因為單例對象正在創(chuàng)建而返回null
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}
從上面的源碼分析中可以看出,BeanFactory接口調(diào)用其實現(xiàn)類的獲取對象的方法來實現(xiàn)創(chuàng)建Bean實例對象的功能。
2.4. FactoryBean實現(xiàn)類的獲取對象的方法
FactoryBean接口的實現(xiàn)類非常多,比如Proxy、RMI、JNDI、ServletContextFactoryBean等。FactoryBean接口為Spring容器提供了一個很好的封裝機制,具體的獲取對象的方法由不同的實現(xiàn)類根據(jù)不同的實現(xiàn)策略來提供,我們分析一下最簡單的AnnotationTestFactoryBean類的源碼:
public class AnnotationTestBeanFactory implements FactoryBean<FactoryCreatedAnnotationTestBean> {
private final FactoryCreatedAnnotationTestBean instance = new FactoryCreatedAnnotationTestBean();
public AnnotationTestBeanFactory() {
this.instance.setName("FACTORY");
}
@Override
public FactoryCreatedAnnotationTestBean getObject() throws Exception {
return this.instance;
}
//AnnotationTestBeanFactory產(chǎn)生Bean實例對象的實現(xiàn)
@Override
public Class<? extends IJmxTestBean> getObjectType() {
return FactoryCreatedAnnotationTestBean.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
Proxy、RMI、JNDI等其他實現(xiàn)類都根據(jù)相應的策略提供方法,這里不做一一分析,這已經(jīng)不是Spring的核心功能,感興趣的“小伙伴”可以自行深入研究。
3 再述autowiring
Spring IoC容器提供了兩種管理Bean依賴關(guān)系的方式:
(1)顯式管理:通過BeanDefinition的屬性值和構(gòu)造方法實現(xiàn)Bean依賴關(guān)系管理。
(2)autowiring:Spring IoC容器有依賴自動裝配功能,不需要對Bean屬性的依賴關(guān)系做顯式的聲明,只需要配置好autowiring屬性,IoC容器會自動使用反射查找屬性的類型和名稱,然后基于屬性的類型或者名稱來自動匹配容器中的Bean,從而自動完成依賴注入。
容器對Bean的自動裝配發(fā)生在容器對Bean依賴注入的過程中。在對Spring IoC容器的依賴注入源碼進行分析時,我們已經(jīng)知道容器對Bean實例對象的依賴屬性注入發(fā)生在AbstractAutoWireCapableBeanFactory類的populateBean()方法中,下面通過程序流程分析autowiring的實現(xiàn)原理。
3.1. AbstractAutoWireCapableBeanFactory對Bean實例對象進行屬性依賴注入
應用程序第一次通過getBean()方法(配置了lazy-init預實例化屬性的除外)向IoC容器索取Bean時,容器創(chuàng)建Bean實例對象,并且對Bean實例對象進行屬性依賴注入,AbstractAutoWire- CapableBeanFactory的populateBean()方法就實現(xiàn)了屬性依賴注入的功能,其主要源碼如下:
//將Bean屬性設置到生成的實例對象上
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
…
//獲取容器在解析Bean定義時為BeanDefinition設置的屬性值
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
//處理依賴注入,首先處理autowiring自動裝配的依賴注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
//根據(jù)Bean名稱進行autowiring自動裝配處理
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
//根據(jù)Bean類型進行autowiring自動裝配處理
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
//對非autowiring的屬性進行依賴注入處理
...
}
3.2. Spring IoC容器根據(jù)Bean名稱或者類型進行autowiring自動屬性依賴注入
Spring IoC容器根據(jù)Bean名稱或者類型進行autowiring自動屬性依賴注入的重要代碼如下:
//根據(jù)類型對屬性進行自動依賴注入
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
//獲取用戶定義的類型轉(zhuǎn)換器
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
//存放解析的要注入的屬性
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
//對Bean對象中非簡單屬性(不是簡單繼承的對象,如8種原始類型、字符、URL等都是簡單屬性)進行處理
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
//獲取指定屬性名稱的屬性描述器
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
//不對Object類型的屬性進行autowiring自動依賴注入
if (Object.class != pd.getPropertyType()) {
//獲取屬性的賦值方法
MethodParameter MethodParam = BeanUtils.getWriteMethodParameter(pd);
//檢查指定類型是否可以被轉(zhuǎn)換為目標對象的類型
boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
//創(chuàng)建一個要被注入的依賴描述
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(MethodParam, eager);
//根據(jù)容器的Bean定義解析依賴關(guān)系,返回所有要被注入的Bean對象
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
//將屬性賦值為所引用的對象
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
//為指定名稱屬性注冊依賴Bean名稱,進行屬性的依賴注入
registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via property '"
+ propertyName + "' to bean named '" + autowiredBeanName + "'");
}
}
//釋放已自動注入的屬性
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
通過上面的源碼分析可以看出,通過屬性名進行自動依賴注入相比通過屬性類型進行自動依賴注入要稍微簡單一些。但是真正實現(xiàn)屬性注入的是DefaultSingletonBeanRegistry類的registerDependentBean()方法。
3.3. DefaultSingletonBeanRegistry的registerDependentBean()方法實現(xiàn)屬性依賴注入
DefaultSingletonBeanRegistry的registerDependentBean()方法實現(xiàn)屬性依賴注入的重要代碼如下:
//為指定的Bean注入依賴的Bean
public void registerDependentBean(String beanName, String dependentBeanName) {
//處理Bean名稱,將別名轉(zhuǎn)換為規(guī)范的Bean名稱
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) {
return;
}
//多線程同步,保證容器內(nèi)數(shù)據(jù)的一致性
//在容器中通過“Bean名稱→全部依賴Bean名稱集合”查找指定名稱Bean的依賴Bean
synchronized (this.dependentBeanMap) {
//獲取指定名稱Bean的所有依賴Bean名稱
dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
//為Bean設置依賴Bean信息
dependentBeans = new LinkedHashSet<>(8);
this.dependentBeanMap.put(canonicalName, dependentBeans);
}
//在向容器中通過“Bean名稱→全部依賴Bean名稱集合”添加Bean的依賴信息
//即,將Bean所依賴的Bean添加到容器的集合中
dependentBeans.add(dependentBeanName);
}
//在容器中通過“Bean名稱→指定名稱Bean的依賴Bean集合”查找指定名稱Bean的依賴Bean
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
if (dependenciesForBean == null) {
dependenciesForBean = new LinkedHashSet<>(8);
this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
}
//在容器中通過“Bean名稱→指定Bean的依賴Bean名稱集合”添加Bean的依賴信息
//即,將Bean所依賴的Bean添加到容器的集合中
dependenciesForBean.add(canonicalName);
}
}
可以看出,autowiring的實現(xiàn)過程如下:
(1)對Bean的屬性調(diào)用getBean()方法,完成依賴Bean的初始化和依賴注入。
(2)將依賴Bean的屬性引用設置到被依賴的Bean屬性上。
(3)將依賴Bean的名稱和被依賴Bean的名稱存儲在IoC容器的集合中。
Spring IoC容器的autowiring自動屬性依賴注入是一個很方便的特性,可以簡化開發(fā)配置,但是凡事都有兩面性,自動屬性依賴注入也有不足:首先,Bean的依賴關(guān)系在配置文件中無法很清楚地看出來,會給維護造成一定的困難;其次,由于自動屬性依賴注入是Spring容器自動執(zhí)行的,容器是不會智能判斷的,如果配置不當,將會帶來無法預料的后果。所以在使用自動屬性依賴注入時需要綜合考慮。
關(guān)注『 Tom彈架構(gòu) 』回復“Spring”可獲取完整源碼。
本文為“Tom彈架構(gòu)”原創(chuàng),轉(zhuǎn)載請注明出處。技術(shù)在于分享,我分享我快樂!如果您有任何建議也可留言評論或私信,您的支持是我堅持創(chuàng)作的動力。關(guān)注『 Tom彈架構(gòu) 』可獲取更多技術(shù)干貨!
原創(chuàng)不易,堅持很酷,都看到這里了,小伙伴記得點贊、收藏、在看,一鍵三連加關(guān)注!如果你覺得內(nèi)容太干,可以分享轉(zhuǎn)發(fā)給朋友滋潤滋潤!