Consider the following classes:
#Component
#Scope(scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
class PrototypeBean
{
public void method1() { ... };
public void method2() { ... };
}
#Component
public class SingletonBean
{
#Autowired
private PrototypeBean prototypeBean;
public void SingletonBean doSomething()
{
prototypeBean.method1();
prototypeBean.method2();
}
}
Now, I would expect that everytime doSomething() method is called, a new PrototypeBean instance is created and injected to SingletonBean.
But what really happens is a new PrototypeBean instance is created and injected into SingletonBean when I call method1() and method2().
I don't really get why, this should've worked as far is I'm concerned.
Please correct my if I'm wrong.
Yes, That is expected !!
Because you declared the bean's scope attribute as prototype #Scope(scopeName = "prototype")that force the Spring IoC container creates a new bean instance of the object every time a request for that specific bean is made.
Note:As a rule, use the prototype scope for all state-full beans and the singleton scope for stateless beans.
Yes that is expected since beans are only initialized on demand, for prototype scope the bean will be initialized each time it is needed.
Related
I have a websocket scoped bean that do something everytime that a new session is created. The problem is that this bean use some observable handlers, it's not called directly, so the bean is never instantiated.
How do I instantiate a websocket scoped bean without making a direct acces to it?
My solution:
Create an interface to use on the components that you want to be initialized:
public interface WebSocketSessionPrematureInitialization {
default void initialize() {}
}
Create a component to initialize all the components that implements the interface:
#Component
#Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class WebSocketBeanInitializer {
private final List<? extends WebSocketSessionPrematureInitialization> wsInit;
#Autowired
public WebSocketBeanInitializer(List<? extends WebSocketSessionPrematureInitialization> wsInit) {
this.wsInit = wsInit;
}
public void initialize() {
for (WebSocketSessionPrematureInitialization bean : wsInit)
bean.initialize();
}
}
Now you just need to call the initialize() method of the WebSocketBeanInitializer always that a new session is created.
Let's say I have a factory CarFactory that returns car object
class CarFactory {
#Autowired
ApplicationContext context;
public Car get(String type) {
if(type.equals("Merc")
return context.getBean(Merc.class);
if(type.equals("Mclaren")
return context.getBean(Mclaren.class);
}
}
Is there any way I can get rid of that context.getBean ? Somebody suggested be to inject Merc and Mclaren in the Factory and return them. But this would mean the same object is returned always. I need to create new Car objects everytime they are requested from the factory
Configure in your SpringApplication (or what ever your config class is named) the following bean:
#Bean
#Scope("prototype")
public McLaren mcLarenProtyoe() {
return new McLaren();
}
Also for Merc.
After that you can inject via #Autowired the bean. And because of the #Scope("prototype") Spring creates every time a new bean if it gets requested.
I wish to perform a couple of setup methods within my Spring Context.
I currently have the following code but it doesn't work as I am saying they are beans and have no return type.
#Configuration
#Component
public class MyServerContext {
...
// Works
#Bean
public UserData userData() {
UserData userData = new AWSUserDataFetcher(urlUtil()).fetchUserData();
return userData;
}
// Doesn't work
#Bean
public void setupKeyTrustStores() {
// Setup TrustStore & KeyStore
System.setProperty(SYS_TRUST_STORE, userData().get(TRUST_STORE_PATH));
System.setProperty(SYS_TRUST_STORE_PASSWORD, userData().get(TRUST_STORE_PASSWORD));
System.setProperty(SYS_KEY_STORE, userData().get(KEY_STORE_PATH));
System.setProperty(SYS_KEY_STORE_PASSWORD, userData().get(KEY_STORE_PASSWORD));
// Prevents handshake alert: unrecognized_name
System.setProperty(ENABLE_SNI_EXTENSION, "false");
}
...
}
How can I run this method automatically by the #Configuration context without the #Bean annotation?
You can use the #PostConstruct annotation instead of #Bean:
#Configuration
#Component
public class MyServerContext {
#Autowired
private UserData userData; // autowire the result of userData() bean method
#Bean
public UserData userData() {
UserData userData = new AWSUserDataFetcher(urlUtil()).fetchUserData();
return userData;
}
#PostConstruct
public void setupKeyTrustStores() {
// Setup TrustStore & KeyStore
System.setProperty(SYS_TRUST_STORE, userData.get(TRUST_STORE_PATH));
System.setProperty(SYS_TRUST_STORE_PASSWORD, userData.get(TRUST_STORE_PASSWORD));
System.setProperty(SYS_KEY_STORE, userData.get(KEY_STORE_PATH));
System.setProperty(SYS_KEY_STORE_PASSWORD, userData.get(KEY_STORE_PASSWORD));
// Prevents handshake alert: unrecognized_name
System.setProperty(ENABLE_SNI_EXTENSION, "false");
}
...
}
Use #PostConstruct instead of #bean
#PostConstruct
Due to the Weld Reference injection and initialization happens in this order;
First, the container calls the bean constructor (the default
constructor or the one annotated #Inject), to obtain an instance of
the bean.
Next, the container initializes the values of all injected fields of
the bean.
Next, the container calls all initializer methods of bean (the call
order is not portable, don’t rely on it).
Finally, the #PostConstruct method, if any, is called.
So, the purpose of using #PostConstruct is clear; it gives you a chance to initialize injected beans, resources etc.
public class Person {
// you may have injected beans, resources etc.
public Person() {
System.out.println("Constructor is called...");
}
#PostConstruct
public void init() {
System.out.println("#PostConstruct is called...");
} }
So, the output by injecting a Person bean will be;
Constructor is called...
#PostConstruct is called...
One important point about #PostConstruct is, it won't be invoked if you try to inject and initialize bean through producer methods. Because using a producer method means that you are programmatically creating, initializing, and injecting your bean with new keyword.
Resource Link:
CDI Dependency Injection #PostConstruct and #PreDestroy Example
Why use #PostConstruct?
I was able to test that autowiring a prototype bean, within a singleton bean results in only a single prototype bean being created.
As a solution to that, I read that I could define AOP scoped proxy for the prototype bean or use Spring's lookup method injection.
Here is what I have tried -
PrototypeBean.java
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.INTERFACES)
public class PrototypeBean implements Prototype {
private String welcomeMessage;
public String getWelcomeMessage() {
return welcomeMessage;
}
public void setWelcomeMessage(final String welcomeMessage) {
this.welcomeMessage = welcomeMessage;
}
}
SingletonBean.java
#Component
public class SingletonBean implements Singleton{
#Autowired private Prototype prototype;
public Prototype getPrototype() {
return prototype;
}
public void greet() {
System.out.println(prototype.getWelcomeMessage());
}
}
Test Class
public class AutowiredDependenciesDemo {
#Autowired private Singleton autowiredSingleton;
#Autowired ConfigurableApplicationContext context;
#Test
public void testPrototypeBeanWithAopScopedProxy(){
Assert.assertNotNull(autowiredSingleton);
Prototype prototypeBean = (Prototype) ((SingletonBean) autowiredSingleton).getPrototype();
prototypeBean.setWelcomeMessage("hello world");
autowiredSingleton.greet();
Singleton contextSingleton = (Singleton) context.getBean("singletonBean");
Assert.assertSame(autowiredSingleton, contextSingleton);
Prototype anotherPrototypeBean = (Prototype) ((SingletonBean)contextSingleton).getPrototype();
anotherPrototypeBean.setWelcomeMessage("hello india");
contextSingleton.greet();
autowiredSingleton.greet();
// i expected both the prototype instances to be different. in the debugger, it does show two different 'proxied' instances. however the test fails.
Assert.assertNotSame(prototypeBean, anotherPrototypeBean);
}
Am I missing something here ? Also, the calls to greet() method return null.
There are things you mix in your thinking about Proxies and prototype beans.
When Spring Framework injects a Prototype Scoped bean into a Singleton Scoped bean then it creates a Proxy object (which implements all required interfaces) and injects it instead of instance of the Prototype bean. Then, whenever method is called on this Prototype Proxy, Spring creates a new instance and the method is called on this new instance.
In your case - in test - you only compare injected Proxies and they are the same because there exists only 1 proxy for the Prototype Bean and this Proxy is responsible for creation of new instance of the Prototype Bean whenever needed.
Here is my example:
I have an interface Prototype and its implementation PrototypeImpl. In my test I obtain bean of type Prototype directly from ApplicationContext and I also inject it using #Autowired. Then in debugger I see this:
Notice that there is only one and the same Proxy (look at its address) but calling 'toString()' on this proxy shows two different addresses of the PrototypeImpl objects. Which shows exactly what I have written above.
EDIT: Info about un-proxying
To extend the comment by M. Deinum you can extract the underlying object from the Proxy in the following way:
Prototype extracted = null;
if(AopUtils.isAopProxy(a) && a instanceof Advised) {
Object target = ((Advised)a).getTargetSource().getTarget();
extracted = (Prototype) target;
}
This is my current scenario:
#WebListener
public class WebListenerService implements HttpSessionListener{
.... implement methods
#Produces
#Dependent
public SessionDependentService sessionDependentService(){
}
}
#SessionScoped
#Named
public class AccountController implements Serializable{
//Injected properly and works as expected
#Inject
private SessionDependnetService sessionDependentService;
#Inject
#OnLogin
private Event<Account> accountEvent;
public void onLogin(){
accountEvent.fire(authenticatedAccount);
}
}
#SessionScoped
public class AccountObserver implements Serializable{
//This does not work. It is always null.
#Inject
private SessionDependnetService sessionDependentService;
public void onLoginEvent(#Observes #OnLogin final Account account) {
//When this methods is invoked
//the sessiondependentservice is always null here.
}
}
In the AccountController, the SessionDependentService is correctly injected and is not null, while in the AccountObserver, it is always null.
EDIT:
Event using the parameter injection still results to a null value.
public void onLoginEvent(#Observes #OnLogin final Account account, final SessionDependnetService sessionDependentService) {
//When this methods is invoked
//the sessiondependentservice is always null here.
}
Netbeans correctly highlights this as an injection point.
Why is this the case?
I am using wildfly 8 server.
I changed the producer bean from SessionScoped to Stateless bean:
#Stateless
public class WebListenerSessionService {
//Use Instance since http session are dynamic.
#Inject
private Instance<HttpSession> httpSession;
#Produces
#Dependent
public SessionDependentService sessionDependentService(){
//use session to lookup existing service or produce a new one.
}
}
Even though this works fine, there is no where in the CDI spec which says Producer method must be session beans.
to quote:
A producer method must be a default-access, public, protected or private, non-abstract method
of a managed bean class or session bean class. A producer method may be either static or non-
static. If the bean is a session bean, the producer method must be either a business method of
the EJB or a static method of the bean class.
And since #SessionScoped is a managed bean class, why would it an injection into an observer bean not work.