【问题标题】:How to create a factory-method with arguments?如何创建带有参数的工厂方法?
【发布时间】:2018-09-02 11:12:02
【问题描述】:

你能帮我摆脱ApplicationContext吗?

我有一个工厂,所以所有书籍实例都是 spring-beans。 我认为把所有的豆子都做成春豆子是一个不错的决定。

@Component
public class BookFactoryImpl implements BookFactory {

    private final ApplicationContext applicationContext;

    @Autowired
    public BookFactoryImpl(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Book createBook(String name) {
        return applicationContext.getBean(Book.class, name);
    }

}

这是一个带有@Bean方法的配置类,用于实例化Book类的新实例:

@Configuration
@ComponentScan({"factory"})
public class AppConfig {

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Lazy
    public Book book(String name) {
        return Book.builder().name(name).build();
    }

}

这是我的Book实体类:

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
@Builder
public class Book {

    @Id
    @GeneratedValue
    private int id;

    @Basic(fetch = FetchType.LAZY)
    private String name;

}

我还有一个想法 - 用 @Configuration 注释 BookFactoryImpl 并在其中移动 @Bean 方法,但在这种情况下,我的工厂将变成具有损坏生命周期的 @Configuration 类。

您认为,实现工厂的最佳方式是什么,以及如何减少像ApplicationContext 这样的外部依赖?

或者用@Bean方法将所有工厂都设为@Configuration类,你觉得怎么样?

【问题讨论】:

  • Book 实例如何使用?什么叫BookFactory.createBook()
  • 假设 psvm() 调用它。没关系,只是客户端代码
  • 创建新 Book 实例的目的是什么?我猜你想把它们保存在数据库中?它们是否被存储库使用?
  • 是的,例如,我可以持久化它们——它们是实体

标签: java spring spring-annotations factory-method spring-ioc


【解决方案1】:

不,让应用程序中的每个类都由 Spring 管理并不是一个好主意。

JPA 实体通常应该由 Spring 托管 bean 中的代码实例化。

【讨论】:

  • 你能详细解释一下吗?你的意思是在客户端代码中调用 Book 类的 builder 吗?如果我想要 init-method 怎么办?
  • 在您提供的 Builder 的 sn-p 中也没有任何意义。如果您的实体具有 1 或 2 个属性,那么构建器就是过度工程。所以就做new Book().setName(name);。另请阅读有关拥有不可变模型的优势。
  • 我希望尽可能拥有不可变对象。如果我有可变数据,我可以简单地使用 @Lookup 方法,然后使用 setter
【解决方案2】:

我通常使用以下方法:

定义将包含对工厂的依赖的单例bean:

    public class MyService {
     private final Provider<Book> bookFactory;

     public MyService(Provider<Book> bookFactory) {
         this.bookFactory = bookFactory;
     } 
     public void doSomething() {
        Book book = bookFactory.get();
        book.setNumberOfReaders(numOfReaders); // this is a drawback, book is mutable, if we want to set runtime params (like numberOfReaders)
         ....
      }
    }

现在为 book bean 定义一个原型:

@Configuration 
public class MyConfiguration {

   @Bean
   @Scope("prototype")
   public Book book(...) {
      return new Book(...);
   }

   @Bean // scope singleton by default
   public MyService myService(Provider<Book> bookFactory) {
       return new MyService(bookFactory);
   }
}

注意,Provider的类型是“javax.inject.Provider”,为了使用id,import(例如在maven中):

<dependency>
   <groupId>javax.inject</groupId>
   <artifactId>javax.inject</artifactId>
   <version>1</version>
</dependency>

Spring 可以从 4.x(我猜是 4.1)开始处理这个问题,无需任何额外配置

当然,这种方法消除了向工厂注入应用程序上下文以及维护工厂的需要

一个缺点是它不允许使用参数构建对象,这些参数必须在运行时指定。

还有另一种方法,运行时生成子类与@Lookup 注释相结合,其描述为Here,但IMO Provider 方法更好。

【讨论】:

  • 创建一个 Lookup 方法并调用它并使用 setter 似乎更容易
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-14
  • 1970-01-01
  • 1970-01-01
  • 2018-01-11
  • 2010-10-17
  • 1970-01-01
相关资源
最近更新 更多