使用静态实例化方法在构建器类中自动连接依赖项

时间:2017-07-19 10:48:49

标签: java spring autowired builder

我正在考虑这个应该根据字段值计算哈希的构建器类。也许这本身对初学者来说是错误的,但目前在我看来它属于那里因为我正在努力争取一个不可变的open -e Podfile

我想自动注入/注入Article但是当我在场上放置ArticleMD5HashCalculator时,IntelliJ抱怨:不推荐使用场注入。构造函数注入是不可能的,因为它是一个构建器模式类,这意味着它有一个没有参数的私有构造函数和一个实例化的静态方法,在hashCalculator中传递它是不方便的。

将助洗剂注入刮刀中。刮刀将为许多物品重复使用相同的构建器。当Spring使用原型范围创建构建器时,构建器将在下一篇文章没有覆盖旧值时使用旧值。

新的hashCalculator结果是一个硬依赖,使得注入模拟是不切实际的。处理这种情况的最佳方法是什么?

以下是现在的代码:

@Autowired

3 个答案:

答案 0 :(得分:1)

假设:

  1. ArticleBuilder和ArticleMD5HashCalculator之间存在一对一的关系。这意味着您不打算在项目的不同位置将不同的hashCalculator实例注入到ArticleBuilder中(基本上有多个ArticleBuilder实例)
  2. 您可以按如下方式更改ArticleBuilder impl

    public class ArticleBuilder {
    
        private ArticleMD5HashCalculator hashCalculator;
    
        public ArticleBuilder(ArticleMD5HashCalculator hashCalculator) { 
            this.hashCalculator = hashCalculator;
        }
    }
    

    您可以创建类型为ArticleMD5HashCalculator的spring bean,并以下列方式将其注入ArticleBuilder类型的spring bean。

    @Configuration
    public class ArticleConfig {
    
        @Bean
        public ArticleMD5HashCalculator articleMD5HashCalculator() {
            return new ArticleMD5HashCalculator(new MD5HashCalculator());
        }
    
        @Bean
        public ArticleBuilder() {
            return new ArticleBuilder(articleMD5HashCalculator());
        }
    }
    

    您可以在项目的其他位置自动装配ArticleBuilder并将其用作构建器。

    我不确定你为什么要创建一个私有构造函数和一个静态方法来调用它。我认为这是因为你想要一个单独的ArticleBuilder。这可以通过上述方法实现。如果我错了,请纠正我。

    更新1:

    根据您在评论中提供的信息,您要在ArticleBuilder对象中注入Scraper,并希望每次都能获得ArticleBuilder的新实例。您可以使用spring @Lookup注释。

    Scraper类的存根实现。

    public class Scraper {
    
    
        //assuming this is the method where you want to use ArticleBuilder
        public void scrape() {
            getArticleBuilder();
        }
    
        //You can even pass constructor arguments to this method. 
        //They will be used to match a constructor on the target bean and that gets invoked
        @Lookup
        public ArticleBuilder getArticleBuilder() {
            //Spring creates a runtime implementation of this method.
            return null;
        }
    }
    

    您可以随时调用getArticleBuilder来获取bean的新实例。如果它被声明为原型,您将始终获得该bean的新实例。

    但唯一需要注意的是Lookup注释不适用于使用@Bean注释创建的bean。您的备用配置可能如下所示。

    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class ArticleBuilder {
    
        @Autowired
        private ArticleMD5HashCalculator hashCalculator;
    
        public ArticleBuilder(ArticleMD5HashCalculator hashCalculator) { 
            this.hashCalculator = hashCalculator;
        }
    }
    
    @Component
    public class ArticleMD5HashCalculator {
    
        public ArticleMD5HashCalculator(MD5HashCalculator hashCalculator) {
            this.hashCalculator = hashCalculator;
        }
    }
    

    beans.xml中:

    <beans>
        <bean class="MD5HashCalculator" /> 
        <!-- Fully qualified class name is needed -->
    </beans>
    

答案 1 :(得分:0)

同样由于Spring文档中使用的约定,请尽可能使用基于构造函数的注入。

  

Spring团队通常提倡构造函数注入,因为它使应用程序组件能够实现为不可变对象,并确保所需的依赖项不为空。

完整信息(滚动一点):Spring DOCS

答案 2 :(得分:0)

我酝酿了这种替代方法来取出new并打开一个注射模拟的窗口。解决方案意味着构建器必须由工厂实例化并重新创建。

工厂:

import org.observer.media.hash.ArticleHashCalculator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Component
public class ArticleBuilderFactory {

    private ArticleHashCalculator articleHashCalculator;

    @Autowired
    public ArticleBuilderFactory(ArticleHashCalculator articleHashCalculator) {
        this.articleHashCalculator = articleHashCalculator;
    }

    public ArticleBuilder create() {
        return new ArticleBuilder(articleHashCalculator);
    }

    public class ArticleBuilder {

        private ArticleHashCalculator articleHashCalculator;

        private String headline;
        private String subheading;
        //...

        private ArticleBuilder(ArticleHashCalculator articleHashCalculator) {
            this.articleHashCalculator = articleHashCalculator;
        }

        public ArticleBuilderFactory.ArticleBuilder withIndex(int index) {
            this.index = index;
            return this;
        }

        public ArticleBuilderFactory.ArticleBuilder withHeadline(String headline) {
            this.headline = headline;
            return this;
        }
        //...

        public Article build() {
            return new Article(headline, subheading, lead, body, images, quotations, subArticles, url, calculateHash(), author, sources, category, subjects, index, medium, company, datePublished, dateFetched);
        }

        private String calculateHash() {
            return articleHashCalculator.hash(headline, subheading, lead, body, quotations, datePublished, dateFetched);
        }
    }
}

工厂用法:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.observer.media.hash.ArticleMD5HashCalculator;
import org.observer.media.hash.MD5HashCalculator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Java6Assertions.assertThat;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
        ArticleBuilderFactory.class,
        MD5HashCalculator.class,
        ArticleMD5HashCalculator.class
})
public class ArticleBuilderFactoryTest {

    private static final String HEADLINE = "headline";
    private static final String LEAD = "lead";
    private static final String BODY = "body";

    @Autowired
    private ArticleBuilderFactory articleBuilderFactory;
    @Autowired
    private ArticleMD5HashCalculator hashCalculator;

    @Test
    public void build() {
        ArticleBuilderFactory.ArticleBuilder articleBuilder = articleBuilderFactory.create();
        Article article = articleBuilder
                .withHeadline(HEADLINE)
                .withLead(LEAD)
                .withBody(BODY)
                .build();

        assertThat(article.getHeadline()).isEqualTo(HEADLINE);
        assertThat(article.getLead()).isEqualTo(LEAD);
        assertThat(article.getBody()).isEqualTo(BODY);
        assertThat(article.getHash()).isEqualTo(hashCalculator.hash(HEADLINE, null, LEAD, BODY, null, null, null));
    }
}

ArticleMD5HashCalculator有@Component:

@Component
public class ArticleMD5HashCalculator {
}