Reputation: 1932
Could you please help me to get rid of ApplicationContext
?
I have a factory so that all book instances are spring-beans. I think it's a good decision to make all beans 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);
}
}
Here is a configuration class with a @Bean
method that is used to instantiate a new instance of Book
class:
@Configuration
@ComponentScan({"factory"})
public class AppConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public Book book(String name) {
return Book.builder().name(name).build();
}
}
Here is my Book
entity class:
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
@Builder
public class Book {
@Id
@GeneratedValue
private int id;
@Basic(fetch = FetchType.LAZY)
private String name;
}
I have more one idea - to annotate BookFactoryImpl
with @Configuration
and move @Bean
method in it but in this case, my factory will be turned into @Configuration
class with the broken lifecycle.
What do you think, what is the best way to implement a factory and how to reduce external dependencies like ApplicationContext
?
Or maybe it's nice to make all factories as @Configuration
classes with @Bean
methods, how do you think?
Upvotes: 1
Views: 373
Reputation: 42551
I usually use the following approach:
Define the singleton bean that will contain a dependency on factory:
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)
....
}
}
Now Define a prototype for 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);
}
}
Notice, Provider is of type "javax.inject.Provider", in order to use id, import (for example in maven):
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
Spring can handle this since 4.x ( I guess 4.1) without any additional configuration
Of course, this approach eliminates that need to inject application context to the factory and in general to maintain a factory
One drawback is that it doesn't allow building the object with arguments, these arguments have to be specified in runtime.
There is also another approach, a runtime generation of sub-class in conjunction with @Lookup
annotation, its described Here but IMO Provider approach is better.
Upvotes: 0
Reputation: 16039
No, it is not a good idea to make every single class in your application managed by Spring.
JPA entities usually should be instantiated by your code inside Spring managed beans.
Upvotes: 3