Spring 不但可以通过 <ref> 引用另一个 Bean,建立起 Bean 和 Bean 之间的依赖关系,<bean> 元素标签之间也可以建立类似的关系,完成一些特殊的功能。
1.继承
OOP思想告诉我们,如果多个类拥有相同的方法和属性,则可以引入一个父类,在父类中定义这些类共同的方法和属性,以消除重复的代码。同样,如果多个 <bean> 存在相同的配置信息,则 Spring 允许定义一个父<bean>,子<bean>将自动继承父<bean>的配置信息。
下面通过一个实例,对使用和未使用父子<bean>的配置进行比较,从中看出父子<bean>给配置带来的便利性,如下所示。
未使用父子<bean>的配置
<bean id="car1" class="com.smart.tagdepend.Car" p:brand="红旗CA72" p:price="2000.00" p:color="黑色" /> <bean id="car2" class="com.smart.tagdepend.Car" p:brand="红旗CA72" p:price="2000.00" p:color="红色" />
上面代码配置了两个 car Bean,我们发现这两个 Bean 的配置存在大量的重复信息。事实上,二者除了 color 属性配置值不一样外,其他配置信息都相同。通过父子<bean>的继承关系就可以很好地消除这种重复的配置信息如下所示。
使用父子<bean>的配置
<!-- ①定义为抽象<bean> --> <bean id="abstractCar" class="com.smart.tagdepend.Car" p:brand="红旗CA72" p:price="2000.00" p:color="黑色" abstract="true"/> <!-- ②继承于abstractCar --> <bean id="car3" parent="abstractCar"> <property name="color" value="红色"/> </bean> <!-- ③继承于abstractCar --> <bean id="car4" parent="abstractCar" > <property name="color" value="白色"/> </bean>
car3 和 car4 这两个 <bean> 都继承于 abstractCar 的 <bean>,Spring 会将父<bean>的配置信息传递给子<bean>。如果子<bean>提供了父<bean>已有的配置信息,那么子<bean>的配置信息将覆盖父<bean>的配置信息。
父<bean>的主要功能是简化子<bean>的配置,所以一般声明为 abstract="true",表示这个<bean>不实例化为一个对应的Bean。如果用户没有指定 abstract="true",则 Spring IOC容器会实例化一个名为 abstractCar 的 Bean。
2.依赖
一般情况下,可以使用 <ref> 元素标签建立对其他 Bean 的依赖关系,Spring 负责管理这些 Bean 的关系。当实例化一个 Bean 时,Spring 保证该 Bean 所依赖的其他 Bean 已经初始化。
但在某些情况下,这种 Bean 之间的依赖关系并不那么明显。下面举一个例子。“小春论坛”拥有很多系统参数(如会话过期时间、缓存更新时间等),这些系统参数用于控制系统的运行逻辑。我们用一个 SystemSettings 类表示这些系统参数。
public class SystemSettings { public static int SESSION_TIMEOUT = 30; public static int REFRESH_CYCLE = 60; }
在 SystemSettings 类中为每个系统参数提供了默认值,但一个灵活的论坛必须提供一个管理后台,在管理后台中可以调整这些系统参数并保存到后台数据库中,在系统启动时,初始化程序从数据库后台加载这些系统参数的配置值以覆盖默认值。
public class SysInit { public SysInit(){ System.out.println("SysInit"); //模拟从数据库中加载系统设置信息 SystemSettings.REFRESH_CYCLE = 100; SystemSettings.SESSION_TIMEOUT = 10; } }
假设论坛有一个缓存刷新管理器,它需要根据系统参数 SystemSettings.REFRESH CYCLE创建缓存刷新定时任务。
public class CacheManager { public CacheManager(){ Timer timer = new Timer(); TimerTask cacheTask = new CacheTask(); timer.schedule(cacheTask,0,SystemSettings.REFRESH_CYCLE*1000); } }
在以上实例中,CacheManager 依赖于 SystemSettings,而 SystemSettings 的值由 Syslnit 负责初始化。虽然 CacheManager 不直接依赖于 Syslnit,但从逻辑上看,CacheManager 希望在 Syslnit 加载并完成系统参数设置后再启动,以避免调用不到真实的系统参数值。如果这3个 Bean 都在 Spring 配置文件中定义,那么如何保证 Syslnit 在 CacheManager 之前进行初始化呢?
Spring 允许用户通过 depends-on 属性显式指定 Bean 前置依赖的 Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好。
<!-- <bean>的依赖 --> <bean id="cacheManager" class="com.smart.tagdepend.CacheManager" depends-on="sysInit" /> <bean id="sysInit" class="com.smart.tagdepend.SysInit" />
通过 depends-on 属性将 syslnit 指定为 manager 前置依赖的 Bean,这样就可以保证 managerBean 在实例化并运行时所引用的系统参数是最新的设置值,而非 SystemSettings 类中的默认值。如果前置依赖于多个 Bean,则可以通过逗号、空格或分号的方式创建 Bean 的名称。
3.引用
假设一个<bean>要引用另一个<bean>的id属性值,则可以直接使用以下配置方式:
<!-- <bean>引用 --> <bean id="car" class="com.smart.tagdepend.Car"/> ① <bean id="boss" class="com.smart.tagdepend.Boss" > ② <property name="carId" > <value>car</value> </property> </bean>
假设希望将 boss Bean 的 carld 设置为①处 <bean> 的 id 值,虽然可以通过②处的方式以字面值的形式进行设置,但二者之间并没有建立引用关系。一般情况下,在一个 Bean 中引用另一个 Bean 的 id 是希望在运行期通过 getBean(beanName) 方法获取对应的 Bean。由于 Spring 并不会在容器启动时对属性配置值进行特殊检查,因此,即使编写错误,也需要等到具体调用时才会发现。
Spring 为此提供了一个 <idref> 元素标签,可以通过 <idref> 引用另一个 <bean> 的名字。在容器启动时,Spring 负责检查引用关系的正确性,这样就可以提前发现错误。因此,下面的配置是推荐的优化方案:
<bean id="car" class="com.smart.tagdepend.Car"/> ① <bean id="boss" class="com.smart.tagdepend.Boss" > ② <idref bean="car"/> </property> </bean>
假设②处由于配置错误,误将 <idref bean="car"/> 写为 <idref bean="cat"/>,那么 Spring 容器在启动时,将会抛出 BeanDefinitionStoreException,提示容器中没有名为 cat 的 Bean
如果引用者和被引用者的 <bean> 位于同一个XML配置文件中,则可以使用 <idref local="car"> 的配置方式,这时IDE的XML分析器就可以在开发期发现引用错误了。
整合多个配置文件
对于一个大型应用来说,可能存在多个 XML 配置文件,在启动 Spring 容器时,可以通过一个 String 数组指定这些配置文件。Spring 还允许通过 <import> 将多个配置文件引入到一个文件中,进行配置文件的集成。这样,在启动 Spring 容器时,仅需指定这个合并好的配置文件即可。
<import resource="classpath:com/smart/impt/beans1.xml"/> ① <bean id="boss1" class="com.smart.fb.Boss" p:name="John" p:car-ref="car1"/> <bean id="boss2" class="com.smart.fb.Boss" p:name="John" p:car-ref="car2"/>
假设已经在 beans1.xml 中配置了 car1 和 car2 的 Bean,在①处通过 <import> 的 resource 属性引入 beans1.xml,beans2.xml 就拥有了完整的配置信息,Spring 容器仅需通过 beans2.xml 就可以加载所有的配置信息。
需要指出的是,如果一个配置文件 a.xml 定义的 <bean> 引用了另一个配置文件 b.xml 定义的 <bean>,那么并不一定需要通过 <import> 引入 b.xml,只需在启动 Spring 容器时,a.xml 和 b.xml 都在配置文件列表中即可。区别在于,如果 a.xml 采用 import 引入了 b.xml,相当于 a.xml 一个文件就包含了 a.xml 和 b.xml 两个文件的内容,因此 Spring 容器启动时仅需加载 a.xml 即可:否则就需要在启动 Spring 容器时,同时加载 a.xml 和 b.xml 配置文件,以便在内存中对 a.xml 和 b.xml 进行合并。
一个 XML 配置文件可以通过 <import> 组合多个外部的配置文件,resource 属性支持 Spring 标准的资源路径。
原文地址:https://www.cnblogs.com/jwen1994/p/11062209.html