Spring: Using builder pattern to create a bean

后端 未结 5 593
[愿得一人]
[愿得一人] 2020-12-08 00:54

I use ektorp to connect to CouchDB.

The way to build an ektorp HttpClient instance is to use builder pattern:

HttpClient httpClient = n         


        
相关标签:
5条回答
  • 2020-12-08 01:04

    You may try to implement FactoryBean interface:

    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:

    <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 StdHttpClient instance.

    0 讨论(0)
  • 2020-12-08 01:06

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

    This is how I've just configured the 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>
    

    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.

    0 讨论(0)
  • 2020-12-08 01:12

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

    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 HttpClientBuilder is accessible to your factory bean. A configuration such as the following is now possible:

    <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>
    
    0 讨论(0)
  • 2020-12-08 01:19

    Please check Spring FactoryBean and FactoryMethod documentation.

    0 讨论(0)
  • 2020-12-08 01:23

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

    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:

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

    <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"/>
    
    0 讨论(0)
提交回复
热议问题