Spring:使用构建器模式创建 bean
Posted
技术标签:
【中文标题】Spring:使用构建器模式创建 bean【英文标题】:Spring: Using builder pattern to create a 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
。还有其他选择吗?
【问题讨论】:
【参考方案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 是一个默认的单例构建器,您可以将其用作依赖项。 你能扩展你的例子吗?它没有显示如何在配置上调用setJmxEnabled
或setMetricLogReporterPeriod
,并且只使用构建器构造函数【参考方案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。
【讨论】:
以上是关于Spring:使用构建器模式创建 bean的主要内容,如果未能解决你的问题,请参考以下文章