1.6,自定義bean的性質
Spring框架提供了許多介面,可用于自定義Bean的性質,本節將它們分組如下:
- 生命周期回呼Lifecycle Callbacks
- ApplicationContextAware 和 BeanNameAware
- 其他Aware
1.6.1,生命周期回呼
為了與參與容器對bean生命周期的管理,您可以實作Spring 的InitializingBean和DisposableBean介面,
容器初始化會呼叫 afterPropertiesSet()方法和銷毀會呼叫destroy()方法來執行某些操作,
通常,@PostConstruct和@PreDestroy注解被認為是在Spring應用程式中接收生命周期回呼的最佳實踐,
使用這些注解意味著您的bean沒有耦合到特定于Spring的介面,
如果你不希望使用JSR-250注解,但你仍然要洗掉的耦合,考慮init-method和destroy-methodbean定義元資料,
public class InitAndDestroyBean implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean init" );
}
@PostConstruct
public void init(){
System.out.println("PostConstruct init");
}
@PreDestroy
public void destroy2(){
System.out.println("PreDestroy destroy");
}
public void initMethod(){
System.out.println("Init-method init");
}
public void destroyMethod(){
System.out.println("Destroy-method destroy");
}
}
<bean id="initAndDestroyBean"
init-method="initMethod" destroy-method="destroyMethod">
控制臺列印:
PostConstruct init
InitializingBean init
Init-method init
.
.
.
PreDestroy destroy
DisposableBean destroy
Destroy-method destroy
在內部,Spring框架使用BeanPostProcessor實作來處理它可以找到的任何回呼介面并呼叫適當的方法,如果您需要自定義功能或其他Spring默認不提供生命周期行為,則您可以BeanPostProcessor自己實作,
除了初始化和銷毀回呼之外,spring管理的物件還可以實作Lifecycle 介面,以便這些物件可以參與啟動和關閉程序,這是由容器自己的生命周期驅動的,
上述的Lifecycle類上有這么一個注釋
* <p>Note that the present {@code Lifecycle} interface is only supported on
* <b>top-level singleton beans</b>. On any other component, the {@code Lifecycle}
* interface will remain undetected and hence ignored. Also, note that the extended
* {@link SmartLifecycle} interface provides sophisticated integration with the
* application context's startup and shutdown phases.
一開始看沒看懂top-level singleton beans的含義自己試了一下實作Lifecycle
@Component
public class CustomLifecycle implements Lifecycle {
@Override
public void start() {
System.out.println("start-----------");
}
@Override
public void stop() {
System.out.println("stop-----------");
}
@Override
public boolean isRunning() {
return false;
}
}
控制臺輸出
PostConstruct init
InitializingBean init
Init-method init
PreDestroy destroy
DisposableBean destroy
Destroy-method destroy
這些代碼并沒有起作用
除錯了原始碼發現在啟動關閉容器的時候,容器確實找到Lifecycle的所有實作類但是還有一個if操作
if (bean instanceof SmartLifecycle) {
.
.
.
}
更改了繼承關系
@Component
public class CustomSmartLifecycle implements SmartLifecycle {
@Override
public void start() {
System.out.println("start-----------");
}
@Override
public void stop() {
System.out.println("stop-----------");
}
@Override
public boolean isRunning() {
return false;
}
}
控制臺輸出
PostConstruct init
InitializingBean init
Init-method init
start-----------
PreDestroy destroy
DisposableBean destroy
Destroy-method destroy
這才稍微理解那段注釋,意思大概就是如果你直接繼承自Lifecycle介面,
那么容器不會呼叫你的自定義介面,只有繼承SmartLifecycle才行
本節介紹了生命周期回呼介面,
初始化回呼
org.springframework.beans.factory.InitializingBean介面允許容器在bean上設定了所有必要的屬性之后執行初始化作業,
InitializingBean介面指定了一個方法:
void afterPropertiesSet() throws Exception;
我們建議您不要使用該InitializingBean介面,因為它將您的代碼耦合到Spring,
另外,我們建議使用@PostConstruct注釋或指定POJO初始化方法,
對于基于XML的配置元資料,可以使用init-method屬性指定具有無效無引數簽名的方法的名稱,
通過Java配置,您可以使用的initMethod屬性@Bean,
請參閱接收生命周期回呼,考慮以下示例:
public class ExampleBean {
public void init() {
// do some initialization work
}
}
您可以使用xml
<bean id="exampleInitBean" init-method="init"/>
或者JAVA Bean配置
@Bean(initMethod = "init")
public void exampleBean(){
return new ExampleBean();
}
前面的示例與下面的示例幾乎具有完全相同的效果:
<bean id="exampleInitBean" />
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// do some initialization work
}
}
但是,前面兩個示例中的第一個示例并未將代碼耦合到Spring,
銷毀回呼
org.springframework.beans.factory.DisposableBean當包含該介面的容器被銷毀時,實作該介面可使Bean獲得回呼,
該DisposableBean介面指定一個方法:
void destroy() throws Exception;
我們建議您不要使用DisposableBean回呼介面,因為它將您的代碼耦合到Spring,
另外,我們建議使用@PreDestroy注釋或指定bean定義支持的通用方法,
使用基于XML的配置元資料時,您可以在destroy-method上使用屬性
通過Java配置,您可以使用的destroyMethod屬性@Bean,
考慮以下定義:
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
您可以使用xml
<bean id="exampleInitBean" destroy-method="cleanup"/>
或者JAVA Bean配置
@Bean(destroyMethod = "cleanup")
public void exampleBean(){
return new ExampleBean();
}
前面的定義與下面的定義幾乎具有完全相同的效果:
<bean id="exampleInitBean" />
public class AnotherExampleBean implements DisposableBean {
@Override
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
但是,前面兩個定義中的第一個沒有將代碼耦合到Spring,
您可以為<bean>元素的destroy-method屬性指定一個特殊的(inferred)值,
該值指示Spring自動檢測特定bean類上的公共close 或shutdown 方法(任何實作java.lang.AutoCloseable or java.io.Closeable也會匹配),
@Component
public class CustomAutoCloseable implements AutoCloseable {
@Override
public void close() {
System.out.println("AutoCloseable close");
}
}
@Component
public class CustomCloseable implements Closeable {
@Override
public void close() {
System.out.println("Closeable close");
}
}
控制臺輸出:
Closeable close
AutoCloseable close
您還可以在<beans>元素的default-destroy-method屬性上設定這個特殊的(inferred)值,
以便將此行為應用于整個bean集合,
<beans default-destroy-method="destroy" default-init-method="init"></beans>
默認初始化和銷毀方法
當您撰寫初始化和銷毀沒有使用特定的spring的InitializingBean和DisposableBean回呼介面的特定方法時,
您通常撰寫具有init()、initialize()、dispose()等名稱的方法,
理想情況下,這種生命周期回呼方法的名稱在整個專案中標準化,
以便所有開發人員使用相同的方法名稱并確保一致性,
您可以配置Spring容器來“查找”已命名的初始化,并銷毀每個bean上的回呼方法名稱,
這意味著,作為應用程式開發人員,您可以撰寫應用程式類并使用名為init()的初始化回呼,而不必為每個bean定義配置init-method="init"屬性,
Spring IoC容器在創建bean時呼叫該方法(并且與前面描述的標準生命周期回呼契約一致),該特性還強制對初始化和銷毀方法回呼執行一致的命名約定,
假設您的初始化回呼方法名為init(),而銷毀回呼方法名為destroy(),然后,您的類就像下面示例中的類:
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
然后,您可以在類似于以下內容的Bean中使用該類:
<beans default-init-method="init">
<bean id="blogService" >
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
在頂級
在創建和組裝bean時,如果bean類有這樣的方法,則會在適當的時候呼叫它,
您可以使用頂級
如果現有的bean類已經有了根據約定命名的回呼方法,那么您可以通過使用
Spring容器保證在為bean提供所有依賴項后立即呼叫已配置的初始化回呼,
因此,初始化回呼是在原始bean參考上呼叫的,這意味著AOP攔截器等還沒有應用到bean,
所以AOP執行順序是先創建一個目標bean,然后再應用一個帶有攔截器鏈的AOP代理(例如),
因此,將攔截器應用到init方法是不適用的,
組合生命周期機制
從Spring 2.5開始,您可以使用三個選項來控制Bean生命周期行為:
- InitializingBean和 DisposableBean回呼介面
- 自定義init()和destroy()方法
- 在@PostConstruct和@PreDestroy 注釋,
您可以結合使用這些機制來控制給定的bean,
如果為一個bean配置了多個生命周期機制,并且為每個機制配置了不同的方法名稱,則每個配置的方法都將按照此注釋后列出的順序運行,
但是,如果init()同時帶有@PostConstruct注解和xml的init-method方法,那么這個init()方法就會被執行一次,
使用不同的初始化方法,為同一個bean配置的多個生命周期機制如下所示:
初始化方法的呼叫順序:
- 用注釋的方法 @PostConstruct
- afterPropertiesSet()由InitializingBean回呼介面定義
- 定制配置的init()方法
銷毀方法的呼叫順序相同:
- 用注釋的方法 @PreDestroy
- destroy()由DisposableBean回呼介面定義
- 定制配置的destroy()方法
啟動和關機回呼
該Lifecycle介面為具有生命周期要求(例如啟動和停止某些后臺行程)的任何物件定義了基本方法:
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何Spring管理的物件都可以實作該Lifecycle介面,
然后,當 ApplicationContext自身接收到啟動和停止信號時(例如,對于運行時的停止/重新啟動場景),它將這些呼叫層疊到Lifecycle該背景關系中定義的所有實作中,
它通過委派給LifecycleProcessor,如下面的清單所示:
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
請注意,LifecycleProcessor本身是Lifecycle 介面的擴展,它還添加了兩種其他方法來回應正在重繪和關閉的背景關系,
請注意,常規org.springframework.context.Lifecycle介面是用于顯式啟動和停止通知的普通協議,并不意味著在背景關系重繪時自動啟動,為了對特定bean的自動啟動進行精細控制(包括啟動階段),請考慮實施org.springframework.context.SmartLifecycle,
另外,請注意,不能保證會在銷毀之前發出停止通知,在常規關閉時,Lifecycle在傳播常規銷毀回呼之前,所有Bean首先都會收到停止通知,但是,在背景關系生命周期中進行熱重繪或停止重繪嘗試時,僅呼叫destroy方法,
啟動和關閉呼叫的順序可能很重要,
如果任何兩個物件之間存在“依賴”關系,則依賴方在其依賴之后開始,而在依賴之前停止,
但是,有時直接依賴項是未知的,
您可能只知道某種型別的物件應該先于另一種型別的物件開始,
在這些情況下,SmartLifecycle介面定義了另一個選項,即在其介面Phased上定義的getPhase()方法,
以下清單顯示了Phased介面的定義:
public interface Phased {
int getPhase();
}
以下清單顯示了SmartLifecycle介面的定義:
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
啟動時,相位最低的物件首先啟動,
停止時,遵循相反的順序,
因此,實作SmartLifecycle并getPhase()回傳其方法的物件Integer.MIN_VALUE將是第一個開始且最后一個停止的物件,
在頻譜的另一端,相位值Integer.MAX_VALUE表示該物件應最后啟動并首先停止(可能因為該物件取決于正在運行的其他行程),
當考慮相位值,同樣重要的是要知道,對于任何“正常”的默認階段 Lifecycle目標沒有實作SmartLifecycle的0,
因此,任何負相位值都表明物件應在這些標準組件之前開始(并在它們之后停止),對于任何正相位值,反之亦然,
SmartLifecycle的stop方法接受Runnable callback作為方法入參,
default void stop(Runnable callback) {
stop();
callback.run();
}
在該stop()的子類的實作執行完成之后,呼叫該callback.run()的方法,
由于LifecycleProcessor介面的默認實作DefaultLifecycleProcessor會在每個階段內的物件組等待其超時值,以呼叫該回呼,因此可以在必要時啟用異步關閉,
默認的每階段超時為30秒,
您可以通過定義lifecycleProcessor背景關系中命名的bean來覆寫默認的生命周期處理器實體 ,
如果只想修改超時,則定義以下內容即可:
<bean id="lifecycleProcessor" >
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="https://www.cnblogs.com/dabaieyangzhijidi/p/10000"/>
</bean>
正如前面提到的,LifecycleProcessor介面還定義了重繪和關閉背景關系的回呼方法,
后者驅動關閉程序,就好像是顯式地呼叫了stop(),但是它發生在背景關系關閉時,
另一方面,“重繪”回呼可以實作SmartLifecycle bean的另一個特性,
當背景關系被重繪時(在所有物件被實體化和初始化之后),回呼被呼叫,
這時,默認的生命周期處理器會檢查每個SmartLifecycle物件的isAutoStartup()方法回傳的布林值,
如果為真,則在該點啟動該物件,而不是等待背景關系或其自己的start()方法的顯式呼叫(與背景關系重繪不同,對于標準背景關系實作,背景關系啟動不會自動發生),
如上所述,相位值和任何“依賴”關系決定啟動順序,
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
//DefaultLifecycleProcessor 呼叫該方法autoStartupOnly始終未true
//如果bean instanceof SmartLifecycle 并且isAutoStartup()方法回傳true
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int phase = getPhase(bean);
LifecycleGroup group = phases.get(phase);
if (group == null) {
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
phases.put(phase, group);
}
group.add(beanName, bean);
}
});
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
phases.get(key).start();
}
}
}
在非Web應用程式中正常關閉Spring IoC容器
本節僅適用于非Web應用程式,
Spring的基于Web的 ApplicationContext實作已經有了相應的代碼,可以在相關Web應用程式關閉時正常關閉Spring IoC容器,
如果您在非web應用程式環境中(例如,在富客戶機桌面環境中)使用Spring的IoC容器,請向JVM注冊一個關機鉤子,
這樣做可以確保優雅地關閉,并呼叫單例bean上的相關銷毀方法,從而釋放所有資源,
您仍然必須正確配置和實作這些銷毀回呼,
要注冊一個關機鉤子,呼叫registerShutdownHook()方法,該方法在ConfigurableApplicationContext介面上宣告,如下面的例子所示:
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}
1.6.2,ApplicationContextAware和BeanNameAware
當ApplicationContext創建一個實作org.springframework.context.ApplicationContextAware的物件實體時,該實體提供對ApplicationContext的參考,下面的清單顯示了applicationcontext taware介面的定義:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,bean可以通過ApplicationContext介面或通過將參考轉換為該介面的已知子類(比如ConfigurableApplicationContext,它公開了其他功能),以編程方式操作創建它們的ApplicationContext,
一種用途是對其他bean進行編程檢索,
有時這個功能是有用的,
但是,通常應該避免使用它,因為它將代碼與Spring結合在一起,并且不遵循控制反轉風格,在這種風格中協作者作為屬性提供給bean,
ApplicationContext的其他方法提供了對檔案資源的訪問、發布應用程式事件和訪問訊息源,
這些附加特性在ApplicationContext的附加功能中進行了描述,
自動裝配是獲得對ApplicationContext的參考的另一種選擇,
傳統的constructor和byType自動裝配模式可以分別為建構式引數或setter方法引數提供型別ApplicationContext的依賴關系,
為了獲得更大的靈活性,包括自動裝配欄位和多個引數方法的能力,可以使用基于注解的自動裝配特性,如果你這樣做了,ApplicationContext就會自動生成一個欄位、建構式引數或方法引數,如果有問題的欄位、建構式或方法帶有@Autowired注解,那么這些引數期望得到ApplicationContext型別,
說了半天不如看代碼:
public class CustomAutowiringApplicationContext {
@Autowired
private ApplicationContext applicationContext;
public CustomAutowiringApplicationContext(ApplicationContext applicationContext){
System.out.println(applicationContext);
}
public void setApplicationContext(ApplicationContext applicationContext){
System.out.println(applicationContext);
}
}
//只有配置byType 或者 byName set注入才會生效
<bean id="customAutowiringApplicationContext"
autowire="byType"
/>
當ApplicationContext創建一個實作org.springframework.beans.factory.BeanNameAware的類時,BeanNameAware介面,提供當前bean的名稱的參考,下面的清單顯示了BeanNameAware介面的定義:
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
原始碼:
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
執行順序原始碼:
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
//處理aware 如上方法
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//執行初始化方法如InitializingBean、afterPropertiesSet或自定義init-method
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
BeanNameAware,BeanClassLoaderAware,BeanFactoryAware 執行是在填充普通bean屬性之后,但在初始化回呼(如InitializingBean、afterPropertiesSet或自定義init-method)之前呼叫回呼,
1.6.3,其他Aware介面
除了applicationcontext taware和BeanNameAware(前面討論過)之外,Spring還提供了廣泛的可感知回呼介面,讓bean向容器表明它們需要某種基礎設施依賴關系,作為一般規則,名稱表示依賴型別,下表總結了最重要的感知介面:
表4.Aware介面 好多沒用過,沒法做詳細解釋后續再補充,此處進行簡單羅列
| 名稱 | 注入的物件 |
|---|---|
| ApplicationContextAware | ApplicationContext |
| ApplicationEventPublisherAware | 事件發布者ApplicationContext |
| BeanClassLoaderAware | 類加載器,用于加載Bean類, |
| BeanFactoryAware | BeanFactory |
| BeanNameAware | bean的名稱, |
| BootstrapContextAware | BootstrapContext容器在其中運行的資源配接器, 通常僅在支持JCA的ApplicationContext實體中可用, |
| LoadTimeWeaverAware | 定義了在加載時處理類定義的weaver |
| MessageSourceAware | 解決訊息的已配置策略(支持引數化和國際化) |
| NotificationPublisherAware | Spring JMX通知發布者 |
| ResourceLoaderAware | 已配置用于決議訊息的策略(支持引數化和國際化) |
| ServletConfigAware | 當前ServletConfig容器在其中運行, 僅在WebApplicationContext環境下運行 |
| ServletContextAware | 當前ServletContext容器在其中運行, 僅在WebApplicationContext環境下運行 |
再次注意,使用這些介面會將您的代碼與Spring API系結在一起,并且不遵循“控制反轉”樣式,因此,我們建議將它們用于需要以編程方式訪問容器的基礎結構Bean,
1.7,Bean Definition繼承
bean定義可以包含許多配置資訊,包括建構式引數、屬性值和特定于容器的資訊,比如初始化方法、靜態工廠方法名,等等,子bean定義從父bean定義繼承配置資料,子定義可以覆寫某些值,也可以根據需要添加其他值,使用父bean和子bean定義可以節省大量輸入,實際上,這是一種模板形式,
如果您以編程方式使用ApplicationContext介面,則子bean定義由ChildBeanDefinition類表示,大多數用戶不會在這個級別上使用它們,相反,它們在類(如ClassPathXmlApplicationContext)中宣告性地配置bean定義,當您使用基于xml的配置元資料時,您可以通過使用父屬性來指示子bean定義,并將父bean指定為該屬性的值,下面的例子展示了如何做到這一點:
<bean id="inheritedTestBean" abstract="true"
>
<property name="name" value="https://www.cnblogs.com/dabaieyangzhijidi/p/parent"/>
<property name="age" value="https://www.cnblogs.com/dabaieyangzhijidi/p/1"/>
</bean>
<bean id="inheritsWithDifferentClass"
parent="inheritedTestBean" init-method="initialize">
<!-- 注意parent屬性 -->
<property name="name" value="https://www.cnblogs.com/dabaieyangzhijidi/p/override"/>
<!--age 屬性則繼承父類-->
</bean>
如果沒有指定bean 的class屬性,子bean定義使用父定義中的bean的class,但是也可以覆寫它,在后一種情況下,子bean類必須與父bean兼容(也就是說,它必須接受父bean的屬性值),
子bean定義從父bean繼承scope、建構式引數值、屬性值,并且覆寫父類方法,并提供添加新值的選項,
您指定的任何Scope、初始化方法、銷毀方法或靜態工廠方法設定都會覆寫相應的父設定,
其余的設定總是取自子定義:依賴、自動裝配模式、依賴檢查、單例和 lazy init,
前面的示例通過使用抽象屬性顯式地將父bean定義標記為抽象,如果父定義沒有指定類,則需要顯式地將父bean定義標記為抽象,如下面的示例所示:
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="https://www.cnblogs.com/dabaieyangzhijidi/p/parent"/>
<property name="age" value="https://www.cnblogs.com/dabaieyangzhijidi/p/1"/>
</bean>
<bean id="inheritsWithClass"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name" value="https://www.cnblogs.com/dabaieyangzhijidi/p/override"/>
<!--age 屬性則繼承父類-->
</bean>
父bean不能自己實體化,因為它不完整,而且它還顯式地標記為抽象,當定義是抽象的時候,它只能作為純模板bean定義使用,作為子定義的父定義,試圖單獨使用這樣一個抽象的父bean,通過參考它作為另一個bean的ref屬性,或者使用父bean ID執行顯式的getBean()呼叫,都會回傳錯誤,類似地,容器的內部預實體化esingletons()方法忽略被定義為抽象的bean定義,
默認情況下,ApplicationContext預實體化所有單例,
因此,如果你有一個(父)bean定義你只打算使用作為模板,
并且這個父bean定義指定了一個類,您必須確保設定抽象屬性為true,
否則應用程式上下文會(試圖)預實體化abstract的bean,
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/118166.html
標籤:Java
上一篇:從零搭建Spring Boot腳手架(7):Elasticsearch應該獨立服務
下一篇:java面向物件思想之繼承
