Spring中使用SpEL动态选择加载Bean

一、背景

我的组需要开发一个公共基础Spring配置,来完成多个工程所需的共性配置与功能。但带来的问题则是Spring配置扩展问题,公共的Spring配置固然好,但如果需要扩展某个Bean就有问题了。列举一个典型的例子,如公共模块中LocalSessionFactoryBean已经定义了annotatedClasses列表。但引用公共模块配置的工程希望在里面加入自己的附加annotatedClasses列表。

二、分析

由于Spring的Xml配置启动时就因为自动按依赖关系加载好需要的依赖Bean。如果想干扰Bean初始化的过程是比较麻烦的,有可能需要编写一些特殊的BeanFactory类来完成。在查阅资料时发现Spring 3中的SpEL功能比较强大,也许可以不用做Java开发通过纯Xml配置来完成我想要的配置。

三、SpEL动态配置样例

1、common.xml(公共模块配置)

    <bean id="annotatedClassesList" class="java.util.ArrayList" >
        <constructor-arg index="0" value="#{ getBeanFactory().containsBean('otherAnnotatedClassesList') ? @otherAnnotatedClassesList : annotatedClassesEmptyList}" />
    </bean>

    <bean id="annotatedClassesEmptyList" class="java.util.Collections" factory-method="emptyList"/>

    <bean id="annotatedClassesListAddDefault" factory-bean="annotatedClassesList" factory-method="addAll">
        <constructor-arg index="0">
            <list>
                <value>org.noahx.XXXX1</value>
                <value>org.noahx.XXXX2</value>
                <value>org.noahx.XXXX3</value>
            </list>
        </constructor-arg>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" depends-on="annotatedClassesListAddDefault">
        <property name="dataSource" ref="dataSource"/>
        <property name="annotatedClasses" ref="annotatedClassesList"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

让我们通过逆序追踪的方法来看Bean加载过程。

1、sessionFactory中加入了depends-on属性,所以在加载sessionFactory前必先加载annotatedClassesListAddDefault
2、annotatedClassesListAddDefault负责调用annotatedClassesList的addAll方法加入默认持久类列表,所以之前必先加载annotatedClassesList
3、annotatedClassesList初始化时,构造函数的参数中会使用SpEL来做一个判断
4、SpEL中使用getBeanFactory().containsBean()方式,可以检查整个Spring环境中是否存在otherAnnotatedClassesList(关键)

5、存在则将otherAnnotatedClassesList注入构造函数参数,不存在则将annotatedClassesEmptyList注入构造函数参数
6、这样SpEL就可以决定annotatedClassesList初始化时的结果

2、project.xml(项目配置)

<import resource="classpath:org/noahx/xxx/common.xml"></import> <!--引用公共Jar中的Xml配置-->

 <bean id="otherAnnotatedClassesList" class="java.util.ArrayList" >
        <constructor-arg index="0">
            <list>
                <value>org.noahx.XXXX4</value>
                <value>org.noahx.XXXX5</value>
            </list>
        </constructor-arg>
    </bean>

如果项目需要加入自己的classes就可以定义一个otherAnnotatedClassesList。公共类加载时,也会将XXXX4与XXXX5一同加载。如果项目不需要修改持久类,则完全可以不定义otherAnnotatedClassesList。

这样,otherAnnotatedClassesList就变为一个配置扩展点,不需要项目做什么高级的处理。有扩展点加载执行,没有不加载执行。

四、总结

SpEL还是比较强大的,除了我采用getBeanFactory().containsBean()这样非常规方式来判断Bean是否存外,SpEL还有非常多的特性。它的使用将使我们的Spring配置具有更强的灵活性。

Spring 4的说明(SpEL):http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/expressions.html

注:Spring 3后就可以使用SpEL,只是高版本支持的功能更多。

你可能感兴趣的:(spring,配置,动态加载,SPEL)