I need to create multiple instances of a spring bean (let's call it MainPrototypeBean), which I can do with the prototype scope. It depends on some other beans, and I want to create new instances of them each time the main bean is created. However, there is a shared dependency between some of the beans, let's call it SharedPrototypeBean. How do I inject the same instance of SharedPrototypeBean in each of the dependent beans, while also creating a new instance for each MainPrototypeBean?
I'm looking into implementing a custom scope, but I'm hoping to find a cleaner way. Making any of the beans singletons is not an option, as they need to be isolated between different instances of MainPrototypeBean.
Here's an example of what I'm trying to do:
#SpringBootApplication
public class DIDemo {
public static void main(String[]args){
ConfigurableApplicationContext context = SpringApplication.run(DIDemo.class, args);
context.getBean(MainPrototypeBean.class);
}
#Component #Scope("prototype") static class SharedPrototypeBean {}
#Component #Scope("prototype") static class FirstPrototypeBean {
#Autowired SharedPrototypeBean shared;
#PostConstruct public void init() {
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
#Component #Scope("prototype") static class SecondPrototypeBean {
#Autowired SharedPrototypeBean shared;
#PostConstruct public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
#Component #Scope("prototype") static class MainPrototypeBean {
#Autowired FirstPrototypeBean first;
#Autowired SecondPrototypeBean second;
}
}
And the output of executing it is:
FirstPrototypeBean.init() with shared DIDemo$SharedPrototypeBean#1b84f475
SecondPrototypeBean.init() with shared DIDemo$SharedPrototypeBean#539d019
You can use the FactoryBean for complex construction logic. Implement its abstract subclass AbstractFactoryBean for creating a MainPrototypeBean, and inject all three dependent beans into it. You can then wire them together in the createInstance method.
The FactoryBean implementation:
public class MainFactoryBean extends AbstractFactoryBean<MainPrototypeBean> implements FactoryBean<MainPrototypeBean> {
private FirstPrototypeBean firstPrototype;
private SecondPrototypeBean secondPrototpye;
private SharedPrototypeBean sharedPrototype;
public MainFactoryBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
this.firstPrototype = firstPrototype;
this.secondPrototpye = secondPrototype;
this.sharedPrototype = sharedPrototype;
}
#Override
protected MainPrototypeBean createInstance() throws Exception {
MainPrototypeBean mainPrototype = new MainPrototypeBean();
firstPrototype.setSharedPrototypeBean(sharedPrototype);
secondPrototpye.setSharedPrototypeBean(sharedPrototype);
mainPrototype.first = firstPrototype;
mainPrototype.second = secondPrototpye;
//call post construct methods on first and second prototype beans manually
firstPrototype.init();
secondPrototpye.init();
return mainPrototype;
}
#Override
public Class<?> getObjectType() {
return MainPrototypeBean.class;
}
}
Note: sharedPrototype is injected after the post-construct phase in the lifecycle of the first and second prototype. So, if you have post-construction logic in these beans that require the sharedPrototype, you need to manually call the init-method when creating the MainPrototypeBean.
Your annotation - configuration changes as as a consequence. The sharedPrototype attributes are no longer autowired (they are set inside FactoryBean), and MainPrototypeBean is not annotated anymore. Instead you need to create the MainFactoryBean.
#Configuration
public class JavaConfig {
//method name is the name refers to MainPrototypeBean, not to the factory
#Bean
#Scope("prototype")
public MainFactoryBean mainPrototypeBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
return new MainFactoryBean(firstPrototype, secondPrototype, sharedPrototype);
}
//Annotations are not needed anymore
static class MainPrototypeBean {
FirstPrototypeBean first;
SecondPrototypeBean second;
}
#Component
#Scope("prototype")
static class SharedPrototypeBean {
}
#Component
#Scope("prototype")
static class FirstPrototypeBean {
private SharedPrototypeBean shared;
//no autowiring required
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
#PostConstruct
public void init() {//reference to shared will be null in post construction phase
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
#Component
#Scope("prototype")
static class SecondPrototypeBean {
private SharedPrototypeBean shared;
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
#PostConstruct
public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
}
After reading the comments and the other answer, I realized that the design is indeed too complex. I made SharedPrototypeBean, FirstPrototypeBean and SecondPrototypeBean regular POJOs, not managed by Spring. I then create all of the objects in a #Bean annotated method.
#Bean
public MainPrototypeBean mainPrototypeBean() {
Shared shared = new Shared();
First first = new First(shared);
Second second = new Second(shared);
return new MainPrototypeBean(first, second);
}
Related
I'm implementing a websocket service where incoming messages are passed to controllers and the controllers can then broadcast response messages to another websocket session(s).
When they broadcast the message back, there is either 1 of 2 issues. Either MySocketHandler is a different instance than the one that handled afterConnectionEstablished (using Autowired annotation on MySocketHandler in MessageRouter seems to create a new instance) or I get NoUniqueBeanDefinitionException (if I use ApplicationContext to specifically get the bean by class type).
An instance of my application should only have 1 MySocketHandler, so I annotated MySocketHandler with #Scope(ConfigurableBeanFactory.SCOPE_SINGLETON).
I suspect this has something to do with asynchronous event publishing and listening. I've refactored this code a few times to try to implement this the "Spring" way but there's some fundamental error each time.
I want to know how I can enforce the Spring container to create and reuse only 1 instance of MySocketHandler.
Here is my a minimalized version of MySocketHandler.java to exemplify the problem:
#Component
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class MySocketHandler extends BinaryWebSocketHandler {
#Autowired private ApplicationContext applicationContext
#Autowired private MessageRouter messageRouter;
private final HashMap<String, WebSocketSession> sessions = new HashMap<>();
#EventListener
public void onOutgoingBinaryMessageEvent(OutgoingBinaryMessageEvent event) {
// ERROR: NoUniqueBeanDefinitionException
applicationContext.getBean(MySocketHandler.class).broadcast(event.getBytes(), event.getConnectionIds());
}
#Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.put(session.getId(), session);
}
#Override
public void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
eventPublisher.publishEvent(new IncomingBinaryMessageEvent(
this,
message.getPayload().array(),
session.getId()));
}
private void broadcast(byte[] bytes, Set<String> playerIds) {
BinaryMessage binaryMessage = new BinaryMessage(bytes);
// this.sessions is null because its a different instance of MySocketHandler than the one that actually managing the connections
for (WebSocketSession session : sessions.values()) {
try {
webSocketSession.sendMessage(binaryMessage);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
And an example of the MessageRouter.java:
#Component
public class MessageRouter {
#Autowired private ApplicationEventPublisher eventPublisher;
public void send(Message message) {
eventPublisher.publishEvent(message);
}
#EventListener
private void routeMessageToController(SomeMessageEvent any, String connectionId) {
.....
// Parse message and route it to a controller class.
.....
}
}
}
Application entry point:
public class MyApplication implements WebSocketConfigurer {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(getSocketHandler(), "/").setAllowedOriginPatterns("*");
}
#Bean
public MySocketHandler getSocketHandler() {
return new MySocketHandler();
}
}
An instance of my application should only have 1 MySocketHandler, so I
annotated MySocketHandler with
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON).
First of all , singleton is applied in the bean level but not the type level. It can't ensure your application will only has the single bean of a particular type. You can still define multiple singleton bean for the same type.
In most general cases , a bean can be defined by the following ways:
Annotating the class with #Component (or its specialisation version such as #Repository , #Service , #Controller , #Configuration etc.)
Using #Bean method in the #Configuration class
Now you are doing :
#Component
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class MySocketHandler extends BinaryWebSocketHandler
}
#SpringBootApplication
public class MyApplication implements WebSocketConfigurer {
#Bean
public MySocketHandler getSocketHandler() {
return new MySocketHandler();
}
}
Note: #SpringBootApplication is a composed annotation which contain #Configuration
which means you are now defining two MySocketHandler beans . One with the name mySocketHandler (defined via #Component) and the other has the name getSocketHandler (defined via #Bean)
So to ensure there is only one MySocketHandler bean , either remove #Component from MySocketHandler or remove this #Bean method.
public class SomeClass {
private static final int num = 432;
#Bean
public int getNum(){
return num;
}
}
or would the method signature need to actually have the static keyword ?
I am not entirely sure about what you mean about a static Bean, Beans are instances in runtime.
If you mean Singleton, meaning that the bean would be created on application start and destroyed in application end.
Then by default, every Bean is #Bean(scope=DefaultScopes.SINGLETON), if you want a bean to be created every new usage of it, you can define it as #Bean(scope=DefaultScopes.PROTOTYPE)
Take a look at the doc: https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
My understanding of a static bean would be different. As an example, take a look at the EventPublisherHolder class from Eclipse Hawkbit:
public final class EventPublisherHolder {
private static final EventPublisherHolder SINGLETON = new EventPublisherHolder();
#Autowired
private ApplicationEventPublisher eventPublisher;
public static EventPublisherHolder getInstance() {
return SINGLETON;
}
public ApplicationEventPublisher getEventPublisher() {
return eventPublisher;
}
...
}
The way the ApplicationEventPublisher is injected into the EventPublisherHolder is through Spring magic
#Bean
EventPublisherHolder eventBusHolder() {
return EventPublisherHolder.getInstance();
}
The EventPublisherHolder class makes it easier to get the ApplicationEventPublisher statically.
As an example, take a look at the way this class is intended to be used:
From JpaAction class:
#Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new ActionCreatedEvent(...));
}
In this sense, you can consider the EventPublisherHolder bean to be a static bean.
I'm using Kinesis Client Library (KCL) and Spring boot. To use KCL, I have to implement a class (I named it RecordProcessor) for interface IRecordProcessor. And KCL will call this class and process records from kinesis. But when I tried to use dependency injection, I found it was not succeeded.
Here's the snippet for RecordProcessor:
#Component
public class RecordProcessor implements IRecordProcessor {
#Autowired
private SingleRecordProcessor singleRecordProcessor;
#Override
public void initialize(String shardId) {
...
}
#Override
public void processRecords(List<Record> records, IRecordProcessorCheckpointer checkpointer) {
...
}
}
I use Class SingleRecordProcessor to process single each record from kinesis. And this is my SingleRecordProcessor class snippet:
#Component
public class SingleRecordProcessor {
private Parser parser;
private Map<String, Table> tables;
public SingleRecordProcessor() {
}
#Autowired
private void setParser(Parser parser) {
this.parser = parser;
}
#Autowired
private void setTables(Map<String, Table> tables) {
this.tables = tables;
}
public void process(String record) {
...
}
}
I want to let spring framework automatically inject the SingleRecordProcessor instance into the class and use it. But I found that the field singleRecordProcessor is null.
Any idea why the dependency injection is failed? Or is it impossible to inject dependencies into a class which is called by other framework (in this case it's KCL)? Any suggestions will be appreciated! Really need some help please!!
[UPDATE]:
Sorry for not expressing the error clearly. The error was NullPointerException. I tried to inject singleRecordProcessor and call method process() on it. I think the injection was not successful so the instance singleRecordProcessor is null and there comes the NullPointerException.
More information is as follows:
I have a major class called Application
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./app.pid"));
ConfigurableApplicationContext ctx = application.run(args);
}
}
And I have the MainProcessor class which will call KCL.
#Service
public final class MainProcessor {
#EventListener(ApplicationReadyEvent.class)
public static void startConsumer() throws Exception {
init();
IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
Worker worker = new Worker(recordProcessorFactory, kinesisClientLibConfiguration);
...
worker.run(); // this line will call KCL library and eventually call ProcessorRecord class.
}
}
[UPDATE2]
RecordProcessorFactory only has one method like this
#Component
public class RecordProcessorFactory implements IRecordProcessorFactory {
#Autowired
RecordProcessor recordProcessor;
#Override
public IRecordProcessor createProcessor() {
return recordProcessor;
}
}
It creates a new RecordProcessor instance for KCL to use it.
You should autowire an instance of this into your MainProcessor:
#Component
public class RecordProcessorFactory {
#Lookup IRecordProcessor createProcessor() { return null; }
}
Spring will instantiate a RecordProcessorFactory for you, and replace the implementation of createProcessor() in it with one that will return a new IRecordProcessor each time it's called. Both the factory and the processors will be Spring beans - which is what you want.
Is possible to specify that all setter should be autowired with one annotation?
This is my class:
#Component
public class MyClass {
private static Bean1 bean1;
//...
private static BeanN beanN;
public static Bean1 getBean1() {
return bean1;
}
#Autowired
public void setBean1(Bean1 bean1) {
MyClass.bean1 = bean1;
}
//...
public static BeanN getBeanN() {
return beanN;
}
#Autowired
public void setBeanN(BeanN beanN) {
MyClass.beanN = beanN;
}
}
No. There is no such built-in annotation. Also, Spring doesn't care that your method is to be interpreted as a bean mutator (a setter). Any method can be annotated with #Autowired and Spring will try to invoke it with the appropriate arguments.
Since the whole point of Spring is dependency injection, there's no reason for you to have static fields. Just inject the bean where you need it.
I am trying to add a simple String to my Spring Application Context, and then autowire this to a different existing bean (A) within the application context. I know this is not the usual way to go, yet I need to add many beans programmatically, which would otherwise make my xml configuration huge.
public class MyPostProcessor implements BeanFactoryPostProcessor, Ordered {
#Override
public int getOrder() {
return 0;
}
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerSingleton("myString", "this is the String");
A a = beanFactory.getBean(A.class);
beanFactory.autowireBean(a);
}
}
public class A {
#Autowired
public transient String message;
}
When running this, the property message of the instance of A is null. What am I missing?
EDIT: this is my application context:
#Configuration
class TestConfig {
#Bean
public A a() {
return new A();
}
#Bean
public MyPostProcessor postProcessor() {
return new MyPostProcessor();
}
}
And this is my test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
public class MyTest {
#Autowired
private transient A a;
#Test
public void test() throws Exception {
System.err.println("Running");
System.err.println("This is the autowired String: " + a.message);
Thread.sleep(1000);
}
}
Thanks
You should not instantiate beans from BeanFactoryPostprocessors.
From BeanFactoryPostProcessor JavaDoc:
A BeanFactoryPostProcessor may interact with and modify bean
definitions, but never bean instances. Doing so may cause premature
bean instantiation, violating the container and causing unintended
side-effects.
In your case, the A bean is instantiated before BeanPostProcessors and therefore not autowired.
Remove the lines:
A a = beanFactory.getBean(A.class);
beanFactory.autowireBean(a);
And will work.
Try using the #Qualifier to specific which bean you want to Auto wire.
public class A {
#Autowired
#Qualifier("myString")
public transient String message;
}