今回は、Springライフサイクルとコンテナ拡張機能の使用について詳しく説明します。Springライフサイクルとコンテナ拡張機能を使用する際の注意点は何ですか?実際のケースを見てみましょう。
以下が主な紹介です:
クラスレベルのライフサイクル初期化コールバックメソッドのinit-method設定、InitializingBeanインターフェースおよびPostConstructアノテーション
コンテナレベルの拡張 BeanPostProcessor インターフェースと BeanFactoryPostProcessor インターフェース
1. クラスレベルのライフサイクルコールバック
1.1initメソッド
参照: Springbeanxsdinit メソッド
init-method は Spring設定ファイルで Bean を宣言する際の設定項目です。 init-method 構成項目の値はクラス内のパラメーターのないメソッドですが、例外がスローされる可能性があります。このメソッドは、Spring コンテナがオブジェクトをインスタンス化し、プロパティ値を設定した後に呼び出されます。 init-method が実装できる関数は、InitializingBean インターフェースおよび PostConstruct アノテーションと一致しています
Spring 構成ファイルとテスト クラスは次のとおりです:
<bean id = "initMethodBeanService" class="name.liuxi.spring.ext.InitMethodBeanService" init-method="init"> <property name="f2" value="2"/> </bean>
テストクラスは次のとおりです:
public class InitMethodBeanService { private static Integer f1; private Integer f2; static { f1 = 1; System.out.println("InitMethodBeanService static block execute..."); } public InitMethodBeanService(){ System.out.println("InitMethodBeanService construct method execute..."); } public void init(){ System.out.println("InitMethodBeanService init method execute..."); } public Integer getF2() { return f2; } public void setF2(Integer f2) { this.f2 = f2; System.out.println("InitMethodBeanService setF2 method execute..."); } }
実行結果は次のように出力されます:
InitMethodBeanService static block execute... InitMethodBeanService construct method execute... InitMethodBeanService setF2 method execute... InitMethodBeanService init method execute... test method execute...
1.2Beanインターフェースの初期化
参考:Spring公式ドキュメントBeans-factory-lifecycle-initializingbean
InitializingBean インターフェースは、afterPropertiesSet メソッドを宣言します。このメソッドは、Spring コンテナーがオブジェクトをインスタンス化し、プロパティ値を設定した後に呼び出されます。上記の init-method によって実装された関数は一貫性があるため、Spring では InitializingBean インターフェイスの使用を推奨しません。
この例は比較的単純なので、ここには記載しません
1.3PostConstruct アノテーション
翻訳: Spring 公式ドキュメント bean-postconstruct-and-predestroy-annotations
@PostConstruct アノテーションは、init-method や InitializingBean インターフェースと同じ効果を持つライフサイクル コールバック メソッドです
@PostConstruct public void postConstruct(){ System.out.println("PostConstructService postConstruct method execute..."); }
上記の 3 つのライフサイクル コールバック メソッド、init-method、InitializingBean インターフェイス、および @PostConstruct アノテーションを要約します
1. これらはすべて単一クラスのインスタンス化後の処理です
2. 実行時は、クラスがインスタンス化され、メンバー変数が注入された後に呼び出されます
3. init-method の場合、Spring 設定ファイルの beans 要素でデフォルトの初期化メソッドを設定することもできます。設定項目は、default-init-method
です。 4. 上記 3 つの方法で設定された初期化メソッドが異なる場合、実行順序は次のとおりです。 @PostConstruct アノテーション メソッド –>InitializingBean の afterPropertiesSet –>init-method メソッド。3 つの方法で設定されたメソッドが同じ場合、メソッドは一度だけ実行されます(Spring公式ドキュメントbeans-factory-lifecycle-combined-effectを参照)
5. 初期化コールバック メソッドがあり、対応する破棄コールバック メソッドもあります。 @PostConstruct アノテーション メソッド ->InitializingBean の afterPropertiesSet ->init-method メソッドは、@PreDestroy アノテーション メソッド ->DisposableBean の destroy ->destroy-method メソッドにそれぞれ対応します
2. コンテナレベルの拡張 翻訳: Spring 公式ドキュメント 3.8ContainerExtensionPoints
通常の状況では、開発者は SpringIOC コンテナを拡張するために ApplicationContext のサブクラスをカスタマイズする必要はありません。SpringIOC コンテナは、外部に公開されたインターフェイスを通じて SpringIOC コンテナを拡張できます。
2.1BeanPostProcessorインターフェース
2.1.1Bean インスタンスの初期化ポストプロセッサーとポストプロセッサーチェーン
BeanPostProcessor インターフェースは、postProcessBeforeInitialization と postProcessAfterInitialization という 2 つのコンテナレベルのコールバック メソッドを定義します。これらは、インスタンスの初期化後の論理処理に使用され、コンテナ内のすべてのインスタンスに対して処理されます。 BeanPostProcessor インターフェースを実装するクラスは、Bean インスタンス初期化ポストプロセッサーと呼ばれます。
複数のインスタンス初期化ポストプロセッサが Spring IOC コンテナに統合されている場合、これらのポストプロセッサの集合は Bean インスタンス初期化ポストプロセッサ チェーンと呼ばれます。
postProcessBeforeInitialization方法在类实例化且成员变量注入完成之后执行,初始化方法(例如InitializingBean的afterPropertiesSet方法)之前执行
postProcessAfterInitialization方法在类实例化且成员变量注入完成之后执行,初始化方法(例如InitializingBean的afterPropertiesSet方法)之后执行
总结:
1.实例初始化后处理器多用于对实例的一些代理操作。Spring中一些使用到AOP的特性也是通过后处理器的方式实现的。
2.实例初始化后处理器链是多个后处理器,就会有执行顺序的问题,可以通过实现Ordered接口,指定后处理的执行顺序,Ordered接口声明了getOrder方法,方法返回值越小,后处理的优先级越高,越早执行。
3.在通过实现BeanPostProcessor接口自定义实例初始化后处理器的时候,建议也实现Ordered接口,指定优先级。
4.这些后处理器的作用域是当前的SpringIOC容器,即后处理器被声明的SpringIOC容器。对于有层次结构的SpringIOC容器,实例初始化后处理器链不会作用于其他容器所初始化的实例上,即使两个容器在同一层次结构上。
5.实例初始化后处理器的实现类只需要和普通的被Spring管理的bean一样声明,SpringIOC容器就会自动检测到,并添加到实例初始化后处理器链中。
6.相对于自动检测,我们也可以调用ConfigurableBeanFactory的addBeanPostProcessor方法,以编程的方式将一个实例初始化后处理器添加到实例初始化后处理器链中。这在需要判定添加条件的场景下比较实用。这种编程式的方式会忽略到实现的Ordered接口所指定的顺序,而会作用于所有的被自动检测的实例初始化后处理器之前。
2.1.2bean实例初始化后处理器与AOP
BeanPostProcessor是一个特殊的接口,实现这个接口的类会被作为Spring管理的bean的实例的后处理器。因此,在Spring应用上下文启动的一个特殊阶段,会直接初始化所有实现了BeanPostProcessor接口的实例,以及该实例所引用的类也会被实例化。然后作为后处理器应用于其他普通实例。
由于AOP的自动代理是以实例化后处理器的方式实现的,所以无论是bean实例初始化后处理器链实例还是其引用的实例,都不能被自动代理。因而,不要在这些实例上进行切面织入。(对于这些实例,会产生这样的日志消息:“类foo不能被所有的实例化后处理器链处理,即不能被自动代理”)。
注意:当实例化后处理器以autowiring或@Resource的方式引用其他bean,Spring容器在以类型匹配依赖注入的时候,可能会注入非指定的bean(例如:实例化后处理器实现类以Resource方式依赖bean,若set方法和被依赖的bean的名称一致或者被依赖bean未声明名称,则依赖注入会以类型匹配的方式注入,此时可能会注入非指定的bean)。这也会导致自动代理或其他方式的实例化后处理器处理失败。
2.1.3bean实例初始化后处理器示例
public class BeanPostProcessorService implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object o, String s) throws BeansException { System.out.println("BeanPostProcessorService postProcessAfterInitialization method execute... "); return o; } @Override public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { System.out.println("BeanPostProcessorService postProcessBeforeInitialization method execute... "); return o; } }
2.2BeanFactoryPostProcessor接口
2.2.1beanfactory后处理器
通过实现BeanFactoryPostProcessor接口,可以读取容器所管理的bean的配置元数据,在bean完成实例化之前进行更改,这些bean称之为beanfactory后处理器。
BeanFactoryPostProcessors与BeanPostProcessor接口的异同点:
相同点:
都是容器级别的后处理器
都可配置多个后处理器,并通过实现Ordered接口,指定执行顺序
都是针对接口声明的容器中所管理的bean进行处理,在有层级结构的容器中,不能处理其他容器中的bean,即使两个容器是同一层次
都是只需要在容器中和普通bean一样声明,容器会自动检测到,并注册为后处理器
会忽略延迟初始化属性配置
不同点:
BeanFactoryPostProcessors接口在bean**实例化前处理bean的配置元数据,BeanPostProcessor接口在bean实例化后处理bean的实例**
BeanFactoryPostProcessors接口也能通过BeanFactory.getBean()方法获取bean的实例,这样会引起bean的实例化。由于BeanFactoryPostProcessors后处理器是在所有bean实例化之前执行,通过BeanFactory.getBean()方法会导致提前实例化bean,从而打破容器标准的生命周期,这样可能会引起一些负面的影响(例如:提前实例化的bean会忽略bean实例化后处理器的处理)。
2.2.2Spring内置及自定义beanfactory后处理器
Spring内置了一些beanfactory后处理器(例如:PropertyPlaceholderConfigurer和PropertyOverrideConfigurer)。同时也支持实现BeanFactoryPostProcessor接口,自定义beanfactory后处理器。下面说说Spring内置的两个后处理器和自定义后处理器。
PropertyPlaceholderConfigurer
Spring为了避免主要的XML定义文件的修改而引起的风险,提供了配置分离,可以将一些可能变更的变量配置到属性配置文件中,并在XML定义文件中以占位符的方式引用。这样,修改配置只需要修改属性配置文件即可。PropertyPlaceholderConfigurer用于检测占位符,并替换占位符为配置属性值。示例如下:
PropertyPlaceholderConfigurer通过jdbc.properties属性配置文件,在运行时,将dataSource这个bean中数据库相关信息的属性占位符替换成对应的配置值。
XML配置如下:
<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
PropertyPlaceholderConfigurer不仅支持属性配置文件的读取,也支持读取系统属性。通过systemPropertiesMode属性值可配置读取优先级。各种取值说明如下:
0:不读取系统属性
1:若引用的属性配置文件中未检索到对应占位符的配置,则读取系统属性。默认为1
2:先读取系统属性,再读取引用的属性配置文件。这种配置可能导致系统属性覆盖配置文件。
PropertyOverrideConfigurer
PropertyOverrideConfigurer类可以通过引用属性配置文件,直接给容器中的bean赋值。当一个bean的属性被多个PropertyOverrideConfigurer类实例赋值时,最后一个的值会覆盖前面的。
还是以上面给上面的dataSource的bean赋值为例:
PropertyOverrideConfigurer类对属性配置文件的引用使用一个新的方式,如下:
<context:property-override location="classpath:override.properties"/>
override.properties属性配置文件的属性的命名规则和上面不同(上面例子中需要保证属性名和占位符一致),命名规则是beanName.property
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb dataSource.username=sa dataSource.password=root
支持复合属性的赋值,但是要保证引用被赋值属性的对象非空
例如:foo.fred.bob.sammy=123
自定义bean factory后处理器
自定义bean factory后处理器就是实现BeanFactoryPostProcessor接口,完成对Spring容器管理的bean的配置元数据进行修改。例如:修改类属性注入的值,示例如下:
定义一个用户类UserBean
public class UserBean { private String userName; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
Spring XML配置文件配置用户类,并给用户名属性userName注入值haha
<bean class="name.liuxi.spring.ext.BeanFactoryPostProcessorService"/> <bean id="user" class="name.liuxi.spring.ext.UserBean"> <property name="userName" value="haha"/> </bean>
下面是自定义的bean factory后处理器,修改属性userName的值为heihei
public class BeanFactoryPostProcessorService implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("BeanFactoryPostProcessorService postProcessBeanFactory method execut..."); BeanDefinition bd = beanFactory.getBeanDefinition("user"); MutablePropertyValues pv = bd.getPropertyValues(); if(pv.contains("userName")) { pv.addPropertyValue("userName", "heihei"); } } }
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がSpringのライフサイクルとコンテナ拡張の使用方法の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。