Как создать фабричный метод с аргументами? - PullRequest
0 голосов
/ 02 сентября 2018

Не могли бы вы помочь мне избавиться от ApplicationContext?

У меня есть фабрика, так что все экземпляры книг - это бобы. Я думаю, что это хорошее решение, чтобы сделать все бобы весенними бобами.

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

}

У меня есть еще одна идея - аннотировать BookFactoryImpl с помощью @Configuration и перемещать в него метод @Bean, но в этом случае моя фабрика превратится в класс @Configuration с нарушенным жизненным циклом.

Как вы думаете, как лучше реализовать фабрику и как уменьшить внешние зависимости, такие как ApplicationContext?

Или, может быть, неплохо сделать все фабрики классами @Configuration с методами @Bean, как вы думаете?

Ответы [ 2 ]

0 голосов
/ 02 сентября 2018

Я обычно использую следующий подход:

Определите синглтон-компонент, который будет содержать зависимость от фабрики:

    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)
         ....
      }
    }

Теперь определите прототип для боба книги:

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

Обратите внимание, что провайдер имеет тип "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, описанная здесь Здесь , но подход IMO Provider лучше.

0 голосов
/ 02 сентября 2018

Нет, не стоит создавать все классы в вашем приложении под управлением Spring.

Объекты JPA обычно должны создаваться вашим кодом внутри управляемых bean-компонентов Spring.

...