• Spring中Bean多种实现切换方案


    一个公共工程中的Spring配置文件,可能会被多个工程引用。因为每个工程可能只需要公共工程中的一部分Bean,所以这些工程的Spring容器启动时,需要区分开哪些Bean要创建出来。另一种场景是:想通过Properties文件中的配置开关,就将Spring配置文件中Bean的实现切换成另一套。

    方法一:Qulifier区分Bean

    1.1应用实例

    以Apache开源框架Jetspeed中的一段配置为例:page-manager.xml

    ===============================================================================

      <bean name="xmlPageManager"class="org.apache.jetspeed.page.psml.CastorXmlPageManager"init-method="init" destroy-method="destroy">
        <meta key="j2:cat" value="xmlPageManager orpageSerializer" />
        <constructor-arg index="0">
          <ref bean="IdGenerator"/>
        </constructor-arg>
        <constructor-arg index="1">
          <refbean="xmlDocumentHandlerFactory" />
        </constructor-arg>
        ……
      </bean>
     
      <bean id="dbPageManager"class="org.apache.jetspeed.page.impl.DatabasePageManager"init-method="init" destroy-method="destroy">
        <meta key="j2:cat" value="dbPageManager orpageSerializer" />
        <!-- OJB configuration file resourcepath -->
        <constructor-arg index="0">
         <value>JETSPEED-INF/ojb/page-manager-repository.xml</value>
        </constructor-arg>
        <!-- fragment id generator -->
        <constructor-arg index="1">
          <ref bean="IdGenerator"/>
        </constructor-arg>
        ……
      </bean>
     

    1.2 Bean过滤器

    JetspeedBeanDefinitionFilter在Spring容器解析每个Bean定义时,会取出上面Bean配置中j2:cat对应的值,例如dbPageManageror pageSerializer。然后将这部分作为正则表达式与当前的Key(从配置文件中读出)进行匹配。只有匹配上的Bean,才会被Spring容器创建出来。

    JetspeedBeanDefinitionFilter

    ===============================================================================

        public boolean match(BeanDefinition bd)
        {
            String beanCategoriesExpression = (String)bd.getAttribute(CATEGORY_META_KEY);
            boolean matched = true;
            if (beanCategoriesExpression != null)
            {
                matched = ((matcher != null)&& matcher.match(beanCategoriesExpression));
            }
            return matched;
    }
     
        public void registerDynamicAlias(BeanDefinitionRegistry registry, String beanName,BeanDefinition bd)
        {
            String aliases =(String)bd.getAttribute(ALIAS_META_KEY);
            if (aliases != null)
            {
                StringTokenizer st = newStringTokenizer(aliases, " ,");
                while (st.hasMoreTokens())
                {
                    String alias = st.nextToken();
                    if (!alias.equals(beanName))
                    {
                        registry.registerAlias(beanName, alias);
                    }
                }
            }
        }

    ===============================================================================

    match()方法中的CATEGORY_META_KEY的值就是j2:cat,matcher类中保存的就是当前的Key,并负责将当前Key与每个Bean的进行正则表达式匹配。

    registerDynamicAlias的作用是:在Bean匹配成功后,定制的Spring容器会调用此方法为Bean注册别名。详见下面1.3中的源码。


    1.3定制Spring容器

    定制一个Spring容器,重写registerBeanDefinition()方法,在Spring注册Bean时进行拦截。

    ===============================================================================

    public class FilteringXmlWebApplicationContextextends XmlWebApplicationContext
    {
        private JetspeedBeanDefinitionFilterfilter;
       
        publicFilteringXmlWebApplicationContext(JetspeedBeanDefinitionFilter filter, String[]configLocations, Properties initProperties, ServletContext servletContext)
        {
            this(filter, configLocations,initProperties, servletContext, null);
        }
       
        publicFilteringXmlWebApplicationContext(JetspeedBeanDefinitionFilter filter, String[]configLocations, Properties initProperties, ServletContext servletContext,ApplicationContext parent)
        {
            super();
            if (parent != null)
            {
                this.setParent(parent);
            }
            if (initProperties != null)
            {
                PropertyPlaceholderConfigurer ppc =new PropertyPlaceholderConfigurer();
               ppc.setIgnoreUnresolvablePlaceholders(true);
               ppc.setSystemPropertiesMode(PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_FALLBACK);
                ppc.setProperties(initProperties);
                addBeanFactoryPostProcessor(ppc);
            }
            setConfigLocations(configLocations);
            setServletContext(servletContext);
            this.filter = filter;
        }
       
        protected DefaultListableBeanFactorycreateBeanFactory()
        {
            return new FilteringListableBeanFactory(filter,getInternalParentBeanFactory());
        }
    }
     
    public classFilteringListableBeanFactory extends DefaultListableBeanFactory
    {
        private JetspeedBeanDefinitionFilterfilter;
       
        public FilteringListableBeanFactory(JetspeedBeanDefinitionFilterfilter, BeanFactory parentBeanFactory)
        {
            super(parentBeanFactory);
            this.filter = filter;
            if (this.filter == null)
            {
                this.filter = newJetspeedBeanDefinitionFilter();
            }
            this.filter.init();
        }
     
        /**
         * Override of the registerBeanDefinitionmethod to optionally filter out a BeanDefinition and
         * if requested dynamically register anbean alias
         */
        public void registerBeanDefinition(StringbeanName, BeanDefinition bd)
                throws BeanDefinitionStoreException
        {
            if (filter.match(bd))
            {
               super.registerBeanDefinition(beanName, bd);
                if (filter != null)
                {
                    filter.registerDynamicAlias(this, beanName, bd);
                }
            }
        }
    }


    1.4为Bean起别名

    使用BeanReferenceFactoryBean工厂Bean,将上面配置的两个Bean(xmlPageManager和dbPageManager)包装起来。将Key配成各自的,实现通过配置当前Key来切换两种实现。别名都配成一个,这样引用他们的Bean就直接引用这个别名就行了。例如下面的PageLayoutComponent。

    page-manager.xml

    ===============================================================================

    <bean class="org.springframework.beans.factory.config.BeanReferenceFactoryBean">
        <meta key="j2:cat"value="xmlPageManager" />
        <meta key="j2:alias"value="org.apache.jetspeed.page.PageManager" />
        <propertyname="targetBeanName" value="xmlPageManager" />
      </bean>
     
      <bean class="org.springframework.beans.factory.config.BeanReferenceFactoryBean">
        <meta key="j2:cat"value="dbPageManager" />
        <meta key="j2:alias"value="org.apache.jetspeed.page.PageManager" />
        <propertyname="targetBeanName" value="dbPageManager" />
      </bean>
     
      <bean id="org.apache.jetspeed.layout.PageLayoutComponent"
        class="org.apache.jetspeed.layout.impl.PageLayoutComponentImpl">
        <meta key="j2:cat"value="default" />
        <constructor-arg index="0">
          <refbean="org.apache.jetspeed.page.PageManager" />
        </constructor-arg>
        <constructor-arg index="1">
          <value>jetspeed-layouts::VelocityOneColumn</value>
        </constructor-arg>
      </bean> 
     

    方法二:使用注解区分Bean

    (未完 待续)

  • 相关阅读:
    Sublime Text3 包管理器、插件安装
    Sublime text3 安装
    VS中的波浪线
    VS的启动方式
    VS常用快捷键
    C#基础性问题
    nginx前端项目发布
    vue父子组件实现数据双向绑定
    常用在线echarts图表
    使用echarts地图踩坑记
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157724.html
Copyright © 2020-2023  润新知