【问题标题】:Spring: Using builder pattern to create a beanSpring:使用构建器模式创建 bean
【发布时间】:2011-03-15 06:59:03
【问题描述】:

我使用ektorp 连接到 CouchDB。

构建 ektorp HttpClient 实例的方法是使用构建器模式:

HttpClient httpClient = new StdHttpClient.Builder()
                                .host("mychouchdbhost")
                                .port(4455)
                                .build();

我对 Spring 比较陌生。请告诉我如何在我的上下文中配置HttpClient 以通过Builder 创建它。

一种方法是使用@Configuration。还有其他选择吗?

【问题讨论】:

    标签: java spring dependency-injection inversion-of-control builder


    【解决方案1】:

    请查看 Spring FactoryBean 和 FactoryMethod 文档。

    【讨论】:

      【解决方案2】:

      你可以尝试实现FactoryBean接口:

      public class HttpFactoryBean implements FactoryBean<HttpClient>{
      
      private String host;
      private int port;
      
      
      public HttpClient getObject() throws Exception {
          return new StdHttpClient.Builder()
                                  .host(host)
                                  .port(port)
                                  .build();
      }
      
      public Class<? extends HttpClient> getObjectType() {
          return StdHttpClient.class;
      }
      
      public boolean isSingleton() {
          return true;
      }
      
      public void setHost(String host) {
          this.host = host;
      }
      
      public void setPort(int port) {
          this.port = port;
      }}
      

      并添加到配置以下 bean 定义:

      <beans ..."> 
         <bean name="myHttpClient" class="HttpFactoryBean">
             <property name="port" value="8080"/>
             <property name="host" value="localhost"/>
         </bean>
      </beans>
      

      然后你可以把这个bean注入另一个bean,它会被解析为StdHttpClient实例。

      【讨论】:

        【解决方案3】:

        虽然没有明确说明您的情况;如果构建器通过标准 bean 模式 set 方法公开属性,则可以扩展构建器。即,如果我们以org.apache.httpcomponents:httpclient HttpClientBuilder 为例,我们可以有以下内容:

        public class HttpClientFactoryBean
                extends HttpClientBuilder
                implements InitializingBean,
                           FactoryBean<HttpClient> {
        
            private HttpClient value;
        
            @Override
            public void afterPropertiesSet() throws Exception {
                this.value = build();
            }
        
            @Override
            public HttpClient getObject() throws Exception {
                return value;
            }
        
            @Override
            public Class<?> getObjectType() {
                return HttpClient.class;
            }
        
            @Override
            public boolean isSingleton() {
                return true;
            }
        
        }
        

        现在您的工厂 bean 可以访问 HttpClientBuilder 公开的任何方法。现在可以进行如下配置:

        <beans id="httpClient" class="com.drunkendev.factory.HttpClientFactoryBean">
          <beans name="defaultCredentialsProvider" ref="credentialsProvider"/>
          <beans name="targetAuthenticationStrategy">
            <util:constant static-field="org.apache.http.impl.client.TargetAuthenticationStrategy.INSTANCE"/>
          </beans>
        </beans>
        

        【讨论】:

          【解决方案4】:

          我曾经在开发FlexyPool 时偶然发现了同样的问题,所以我就是这样做的。

          基本上,从以下 Builder 开始:

          public final class Configuration<T extends DataSource> extends ConfigurationProperties<T, Metrics, PoolAdapter<T>> {
           
              public static final long DEFAULT_METRIC_LOG_REPORTER_PERIOD = 5;
           
              public static class Builder<T extends DataSource> {
                  private final String uniqueName;
                  private final T targetDataSource;
                  private final PoolAdapterBuilder<T> poolAdapterBuilder;
                  private final MetricsBuilder metricsBuilder;
                  private boolean jmxEnabled = true;
                  private long metricLogReporterPeriod = DEFAULT_METRIC_LOG_REPORTER_PERIOD;
           
                  public Builder(String uniqueName, T targetDataSource, MetricsBuilder metricsBuilder, PoolAdapterBuilder<T> poolAdapterBuilder) {
                      this.uniqueName = uniqueName;
                      this.targetDataSource = targetDataSource;
                      this.metricsBuilder = metricsBuilder;
                      this.poolAdapterBuilder = poolAdapterBuilder;
                  }
           
                  public Builder setJmxEnabled(boolean enableJmx) {
                      this.jmxEnabled = enableJmx;
                      return this;
                  }
           
                  public Builder setMetricLogReporterPeriod(long metricLogReporterPeriod) {
                      this.metricLogReporterPeriod = metricLogReporterPeriod;
                      return this;
                  }
           
                  public Configuration<T> build() {
                      Configuration<T> configuration = new Configuration<T>(uniqueName, targetDataSource);
                      configuration.setJmxEnabled(jmxEnabled);
                      configuration.setMetricLogReporterPeriod(metricLogReporterPeriod);
                      configuration.metrics = metricsBuilder.build(configuration);
                      configuration.poolAdapter = poolAdapterBuilder.build(configuration);
                      return configuration;
                  }
              }
           
              private final T targetDataSource;
              private Metrics metrics;
              private PoolAdapter poolAdapter;
           
              private Configuration(String uniqueName, T targetDataSource) {
                  super(uniqueName);
                  this.targetDataSource = targetDataSource;
              }
           
              public T getTargetDataSource() {
                  return targetDataSource;
              }
           
              public Metrics getMetrics() {
                  return metrics;
              }
           
              public PoolAdapter<T> getPoolAdapter() {
                  return poolAdapter;
              }
          }
          

          使用基于 Java 的配置很简单:

          @org.springframework.context.annotation.Configuration
          public class FlexyDataSourceConfiguration {
           
              @Bean
              public Configuration configuration() {
                  return new Configuration.Builder(
                          UUID.randomUUID().toString(),
                          poolingDataSource,
                          CodahaleMetrics.BUILDER,
                          BitronixPoolAdapter.BUILDER
                  ).build();
              }
          }
          

          但您也可以使用基于 XML 的配置:

          <bean id="configurationBuilder" class="com.vladmihalcea.flexypool.config.Configuration$Builder">
              <constructor-arg value="uniqueId"/>
              <constructor-arg ref="poolingDataSource"/>
              <constructor-arg value="#{ T(com.vladmihalcea.flexypool.metric.codahale.CodahaleMetrics).BUILDER }"/>
              <constructor-arg value="#{ T(com.vladmihalcea.flexypool.adaptor.BitronixPoolAdapter).BUILDER }"/>
          </bean>
           
          <bean id="configuration" factory-bean="configurationBuilder" factory-method="build"/>
          

          【讨论】:

          • 通读您的文章。我喜欢这种方式,发现它很独特。你能帮我理解最后两个 constructor-arg 的构造吗?我找不到任何关于那里使用的 BUILDER 构造的帮助。
          • 这是一个 Builder 带着其他 Builder 来解决一些依赖关系。
          • 哦...所以“BUILDER”是“CodahaleMetrics”和“BitronixPoolAdapter”的属性,如果没有记错的话。
          • 是的。 BUILDER 是一个默认的单例构建器,您可以将其用作依赖项。
          • 你能扩展你的例子吗?它没有显示如何在配置上调用setJmxEnabledsetMetricLogReporterPeriod,并且只使用构建器构造函数
          【解决方案5】:

          虽然FactoryBean 更干净,但还有一种更快捷的方法,使用SpEL

          这就是我刚刚配置Neo4j driver的方式:

          <bean id = "neoDriver" class = "org.neo4j.driver.v1.GraphDatabase" 
                  factory-method="driver">
              <constructor-arg value = "bolt://127.0.0.1:7687" />
              <constructor-arg>
                  <bean class = "org.neo4j.driver.v1.AuthTokens" factory-method = "basic">
                      <constructor-arg value = "neo4j" />
                      <constructor-arg value = "***" />
                  </bean>
              </constructor-arg>
              <constructor-arg type="org.neo4j.driver.v1.Config" 
                  value = "#{T(org.neo4j.driver.v1.Config).build ()
                      .withConnectionAcquisitionTimeout ( 10, T(java.util.concurrent.TimeUnit).SECONDS )
                      .withConnectionTimeout ( 10, T(java.util.concurrent.TimeUnit).SECONDS )
                      .toConfig ()
                  }"
              />
          </bean>
          

          从工厂方法的第三个参数可以看出,您可以将构建器及其方法调用为 SpEL 表达式,其中的细微差别是必须通过其 FQN 指定类。但这避免了您编写完整的样板 FactoryBean。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-04-24
            • 2015-09-07
            • 2016-10-20
            • 1970-01-01
            • 2021-08-11
            相关资源
            最近更新 更多