Working with scoped beans when the scope is optional? - java

I have a scope which sometimes doesn't exist when I need some bean from it. That in itself isn't a problem; I could use defaults for these cases. My problem is with autowiring. The scenarios goes roughly like this:
I have a scoped bean ICurrentLocale. It's scope is User and the scope depends on whether a user is currently logged in.
Autowire bean foo contains a field #Autowired ICurrentLocale currentLocale;
Call some methods on foo.
A user logs in. Now, I have a scope User
Call some methods on foo.
My problem is that in point #5, the autowired ICurrentLocale bean is still the same despite the fact that a new bean has created in the user's scope.
Is there a good/simple/understandable way to build a spring configuration that automatically rewires beans when a new scope is entered in the same thread?
Or maybe I can ask Spring to "refresh" the proxy?
EDIT Here is my current implementation of the scope:
#Override
public Object get( String name, final ObjectFactory<?> objectFactory ) {
CurrentUserSession currentSession = userSessionFactory.getCurrentSession();
if( currentSession == null ) {
if( isLocaleProvider( name ) ) {
return createLocaleProvider();
}
throw new BeanCreationException( "Could not create bean " + name + " the bean scope " + NAME + " can be used only after the user has signed in" );
}
Object bean = currentSession.getBean(name, objectFactory);
return bean;
}
As you can see, I create a dummy bean as long as no user is logged in. If I have a session, I look into the cache in the session. If the bean doesn't exist, yet, I use the objectFactory to create a new one.

Maybe I'm misunderstanding your question.
Your ICurrentLocale bean should be something like
#Component
#Scope(value = "user", proxy-mode = "ScopedProxyMode.TARGET_CLASS")
public class CurrentLocaleImpl implements ICurrentLocale {
...
}
or the corresponding #Bean or XML configuration.
With such configuration, Spring will create a proxy for your injection target. In other words, in
#Autowired
private ICurrentLocale currentLocale;
you won't have a CurrentLocaleImpl object, you'll have a proxy object that has a reference to the underlying BeanFactory.
5.Call some methods on foo.
My problem is that in point #5, the autowired ICurrentLocale bean is
still the same despite the fact that a new bean has created in the
user's scope.
When you invoke a method on the proxy bean, the proxy will attempt to retrieve an actual CurrentLocaleImpl object from the BeanFactory using your Scope implementation and then delegate the method call to that object. Effectively, you'll be using either the proper object, the default object, or throwing an exception.

For other people with similar problems: Make sure userSessionFactory.getCurrentSession() returns what you expect.
In my case, the user session is attached to the current thread (using ThreadLocal). Now a helper framework executed some of my code in a new background thread -> ThreadLocal returned null despite me knowing that a user was logged in.

Related

How can spring using #Resource to register a bean

I am studying someone's code in my company, and found a strange code, but don't know how it work.
The code looks like this
interface A {...}
interface B {...}
class MyClass implements A, B {...}
#Bean
B myB() {new MyClass();}
#Autowired
A a;
//***************** a will inject failed(bean not found) if I delete this part
#Resource
MyClass myB;
//******************
I don't know why spring can find the "a".
I try to delete the #Resource part and it tell me bean not found, so spring use #Resource to do bean register?
I was thought only #Bean can do that(ya, I know xml can do that too), isn't it?
Interesting issue. I assume it happens because Spring uses some kind of optimization/lazy-creation during dependency injection.
This is only my assumption, but it's a quite good explanation for this issue:
When Spring find a #bean annotation, it doesn't immediately creates the bean, instead it only register it. (It's quite logical, beans could depend on each-other, so first it only tries to collect all of the registered beans, before it tries to actually create them). It also collects all the places where a bean is needed to be injected.
Then it tries to fullfill the injections in rounds. As long as it can fullfill at least one injection in a round, it continues the process.
When the beans are registered, they are registered by name and type which come from the annotation properties or from the method name and return type.
So there will be a bean registered with name myB with type B
At the first round it cannot find any beans for
#Autowired
A a;
so it skips it.
Then it tries to inject a bean into
#Resource
MyClass myB;
It can find a registered bean by name. Although the registered bean's type is not the same nor an extension of the required type, this bean was requested by name, and its actual instance type is not known yet, so - I assume - at this point Spring's injection logic trusts the programmers will, and it instantiates the bean by calling myB(), checks the actual type against the required type and because it matches, it happily injects the returned bean and updates the bean type in the registry with the actual type, which is MyClass.
There are no more injections, so it starts the next round:
Now, when it tries to inject
#Autowired
A a;
it still cannot find a bean by name, but it can find a bean where the registered type is an instance of A, so it can inject that bean too.
So, in short:
If you request a bean by name, then Spring will go and check if that bean fullfills the otherwise un-matching type requirements, but if you request a bean by type, it won't check the actual type of all of the available beans, instead raises and exception an let you refine the injection.

Proxied prototype bean is created every time a method is invoked from it

I am having an issue accessing a Request scoped bean from a websockets callback thread (different thread than the one managing the original HTTP request - hence the issue). You can read more about it here:
Accessing a request scoped bean in a different thread (that handles websocket traffic)
I have since, managed to get around the problem (even though I am not 100% happy with the solution) but I am seeing a behaviour I do not understand.
My current solution is as follows:
I have changed the Bean from request scope to prototype scope:
#Bean
#Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public DbClientI getDbClient() throws StrykeDomainException {
DbClientI dbClient = requestContextHolder.getAttribute("dbClientReq", DbClientI.class);
if (dbClient != null) {
logger.info("Retrieved DbClient proxy instance: {}", dbClient.hashCode());
}
return dbClient;
}
I am creating and destroying the instance behind the Bean in a dedicated Interceptor (HandlerInterceptorAdapter) and storing it in the RequestContextHolder, so that it can be retrieved by my Bean configuration (above).
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
boolean shouldProceedInterceptorChain = true;
if (authenticatedUserInfo.isUserAuthenticated()) {
try {
DbClientI dbClient = dbCliFactory.createDbClientForCurrentRequest();
requestContextHolder.setAttribute("dbClientReq", dbClient, true);
dbClient.connect();
} catch (Exception e) {
shouldProceedInterceptorChain = false;
}
}
return shouldProceedInterceptorChain;
}
#Override
public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) {
//Note: we must use this method to disconnect instead of "postHandle", because postHandle will not run in case of an exception
if (authenticatedUserInfo.isUserAuthenticated()) {
DbClientI dbClient = appContext.getBean(DbClientI.class);
if (dbClient != null && dbClient.isConnected()) {
dbClient.disconnect();
dbClient = null;
}
}
}
The solution works, but the method getDbClient() is called every time the dbClient Bean is accessed in the code! Calling a method off the dbClient bean from within another bean causes the getDbClient() method to be called.
My understanding is that the getDbClient() method should only get called each time it is injected into another bean, for example when injecting it in the constructor of another bean. This is what the Spring docs say:
The non-singleton, prototype scope of bean deployment results in the
creation of a new bean instance every time a request for that specific
bean is made (that is, it is injected into another bean or it is
requested via a programmatic getBean() method call on the container)
https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html#beans-factory-scopes-prototype
Functionality-wise this is fine because underneath there is always the same instance of DbClient (the one created by the interceptor), but the fact that the method getDbClient() is called every time the bean is used is surely impacting performance.
How can I change my code so that getDbClient() is only called when the bean is supplied to another bean and not every time it is used?
Thanks.
Using the insight from the comments and doing a bit more testing on my end I realised that the key of my misunderstanding lied on the usage of the proxy.
Indeed, as the Spring docs say, when using a Prototype scope a new instance will only get created every time the bean is injected or ApplicationContext.getBean() is called. A new instance will NOT get created just by simply accessing the bean, for example by calling a method on it.
However, if the same bean is also decorated with the proxy property, what gets created on injection is a proxy and not an actual instance of the class. This results in Spring calling the "configuration method" to retrieve the actual underlying instance (in my case the getDbClient method) every time the bean is accessed (eg: calling a method on it).
Note that the above is true for "proxyed" prototype beans. For "proxyed" request scoped beans, a single call to get the actual instance will be performed at the start of the request. Subsequent use of the bean will not trigger a call to retrieve a new instance of the bean.

Reinitialize spring #Autowire bean

I have a scenario where I need to initialize a bean based on application configuration during startup. Later, due to dynamic configuration fetched based on an event, I have to update the bean.
This bean can't be updated but can only be replaced with a new instance.
Does using the new operator initialize only the local instance or will it change the bean?
#Component
public class TestComp {
#Autowired
private BeanA beanA;
public void updateBean() {
beanA = new BeanA("new value");
}
}
I referred the bean in another class and checked after I initialized it with new. It reflected the new object. But, I need a confirmation from experts if it does.
I have a scenario where I need to initialize a bean based on application configuration during startup.
It's fine. The singleton scope is a good choice here.
Later, due to dynamic configuration fetched based on an event, I have to update the bean.
It's a problem. Updating a bean in the context is a complex process: you need to remove the existing bean definition, add a new one, and update all the beans that are somehow related to the bean (reinitialise these components, refresh the context). Technically, it's possible and it has been simplified by Spring Cloud's #RefreshScope.
Does using new operator initialize only the local instance or will it change the bean?
It affects only the field in this class. No one is aware of the change. ApplicationContext#getBean still will return the old object, and all the components will be (or have already been) initialised with the old instance.
I referred the bean in another class and checked after I initialized it with new. It reflected the new object.
It can't be true. Probably, it refers to the TestComp#beanA field, not to its own BeanA field.
The solution I am suggesting is to define a custom bean scope based on the events you are receiving. It will keep the bean and the context updated.
It sounds like you want a factory instead. Below is a rough idea of what that might look like; your needs may vary.
#Component
public class BeanFactory {
private volatile BeanA beanAInstance;
public BeanA createBeanA(String value) {
if (null == beanAInstance) {
synchronized (this) {
if (null == beanAInstance) {
beanAInstance = new BeanA(value);
}
}
}
return beanAInstance;
}
public void refreshBeanA(String newValue) {
synchronized (this) {
beanAInstance = new BeanA(newValue);
}
}
}
You then wire this in, and based on configuration, you can then refresh and use the new value. Bear in mind that this would change the value you get from this bean.

Is it possible to undeclare or define a null bean in Spring?

While using Spring, I've encountered a scenario in which some logic is called if a particular bean is not null. I do not want this logic to be called; therefore, I need this object to be null. The bean in question has a default non-null value created by autoconfiguration.
My question is this: is there a way to "undeclare" a bean so that it's null?
This won't work:
#Bean
public UserDetailsService userDetailsService() {
return null;
}
It yields:
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userDetailsService' is expected to be of type 'org.springframework.security.core.userdetails.UserDetailsService' but was actually of type 'org.springframework.beans.factory.support.NullBean'
Is there an Spring configuration way to do this or do I have to dive in and call constructors and setters to set this object to null?
It sounds like you do not want UserDetailsService to be treated as a bean, therefore you shouldn't create it as one. Remove annotations setting it up as a bean (e.g #Service, #Bean)

understanding of spring bean scopes in a web application

As per springsource documentation a singleton scoped bean is instantiated only once per
container. For example I have a singleton scoped UserDetails bean which contains information
about a user.
In my main() method:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"Spring-Customer.xml"});
UserDetails ud1 = (UserDetails)context.getBean("userDetails");
custA.setAddress("Address set by ud1");
System.out.println("Address : " + ud1.getAddress());
UserDetails ud2 = (UserDetails)context.getBean("userDetails");
System.out.println("Address : " + ud2.getAddress());
The output will be
Address set by ud1
Address set by ud1
Because of userDetails is a singleton bean, the second retrieval by ud2 will give the same result as that of ud1.
NOW here is my problem:
For my web application I have the following UserDetails bean in my dispatcher-servlet.xml.
<bean id="userDetails" class="com.mukund.DTO.UserDetails" />
first question: is singleton scope is the default for a web application too ?
IF YES:
This bean is autowired into AccountService and CustomerService classes.
If a client say clientA has set the first name of the user to "UserA" in CustomerService class and after some time it retrieves the first name from AccountService class,
second question: does it get the same instance of UserDetails with "UserA" as the first name ?
third question: In the mean time if another client say clientB tries to get the first name in AccountService class will it get "UserA" ?
fourth question: will the same UserDetails instance be shared by clientA, clientB and others ? If yes: what scope to choose prototype, request or session.
I hope you understand my point. Please explain me spring bean scopes with regards to a web application.
THANKS
Yes singleton is the default scope for web applications. So you get the same instance of UserDetails in all your services (and for all your users).
What scope is the right one for you depends on what you exactly want. Do you really want to inject a data transfer object into services? How long should the object exist?
Prototype scope: Each service gets its own UserDetails object
Request scope: You get same instance for the time of the request
Session scope: You get the same instance as long as you are in the same session.
By Default the scope of spring beans is singleton that means one instance per container.
But that doesn't mean that the same instance is used by all requests.
It works like this.
Client A requests for bean A, the container will look for the instance of that bean A, if the instance is not available it will create an instance and then will give it to client A.
But if bean A is being used by another Client B then Client A has to wait till Client B releases the bean A.

Categories

Resources