Java Spring:使用构建器模式创建 bean

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3236850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:39:21  来源:igfitidea点击:

Spring: Using builder pattern to create a bean

javaspringdependency-injectioninversion-of-controlbuilder

提问by artemb

I use ektorpto connect to CouchDB.

我使用ektorp连接到 CouchDB。

The way to build an ektorp HttpClientinstance is to use builder pattern:

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

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

I am relatively new to Spring. Please advice me on how I can configure an HttpClientin my context to create it via the Builder.

我对 Spring 比较陌生。请建议我如何HttpClient在我的上下文中配置一个以通过Builder.

One way to do this is with @Configuration. Are any other options?

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

采纳答案by wax

You may try to implement FactoryBeaninterface:

您可以尝试实现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;
}}

And add to config following bean definition:

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

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

Then you can inject this bean to another beans, it will be resolved as StdHttpClientinstance.

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

回答by Script Runner

Please check Spring FactoryBean and FactoryMethod documentation.

请查看 Spring FactoryBean 和 FactoryMethod 文档。

回答by Brett Ryan

While not explicit for your case; it is possible to extend a builder if it exposes properties via standard bean pattern setmethods. i.e. if we take the org.apache.httpcomponents:httpclientHttpClientBuilderas an example we could have the following:

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

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;
    }

}

Now any method exposed by HttpClientBuilderis accessible to your factory bean. A configuration such as the following is now possible:

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

<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>

回答by Vlad Mihalcea

I once stumbled on the same issue, when I was developing FlexyPool (a connection pool monitoring and auto-sizing utility), so I wrote an articlewith both a Java-based and an xml-based example.

我曾经在开发FlexyPool(一个连接池监控和自动调整大小的实用程序)时偶然发现了同样的问题,所以我写了一篇文章,其中包含基于 Java 和基于 xml 的示例。

Basically, starting from the following 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;
    }
}

Using the Java-based configuration is straight-forward:

使用基于 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();
    }
}

But you can also use XML-based configuration as well:

但您也可以使用基于 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"/>

回答by zakmck

While FactoryBeanis cleaner there is a more quick-n-dirty method, using SpEL.

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

This is how I've just configured the Neo4j driver:

这是我刚刚配置Neo4j 驱动程序的方式

<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>

As you can see from the factory method's 3rd parameter, you can invoke a builder and its methods as a SpEL expression, with the nuance that classes have to be specified via their FQN. But that avoids you to write an entire boilerplate FactoryBean.

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