Accessing a session-scoped bean inside a controller - spring

I'm experimenting with session-scoped beans in Spring 3. I have the following bean definition:
<bean id="userInfo" class="net.sandbox.sessionbeans.UserInfo" scope="session" />
Here is net.sandbox.controllers.RegistrationController, a controller class that needs access to this bean. I've taken out the imports for brevity's sake.
#Controller
#RequestMapping("/register")
public class RegistrationController {
private UserInfo userInfo; // This should reference the session-scoped bean
#RequestMapping(method = RequestMethod.GET)
public String showRegForm(Model model) {
RegistrationForm regForm = new RegistrationForm();
model.addAttribute("regform", regForm);
return "regform";
}
#RequestMapping(method = RequestMethod.POST)
public String validateForm(#Valid RegistrationForm regForm, BindingResult result, Model model) {
if (result.hasErrors()) {
return "regform";
}
userInfo.setUserName(regForm.getFirstName());
model.addAttribute("regform", regForm);
return "regsuccess";
}
}
Is there a way to automatically tie the session-scoped bean I defined to the member variable private UserInfo userInfo in RegistrationController?

Yes - see section 3.4.5.4 of the Spring manual, "Scoped beans as dependencies".
Briefly, you can ask Spring to wrap your session-scoped bean in a singleton proxy, which looks up the correct session when you invoke a method on the scoped bean. This is called a "scoped proxy", and uses the <aop:scoped-proxy> config macro. You can then inject the reference as you would any other (e.g. <property>, or #Autowired). See the above link for details.

By default, Spring creates a proxy by implementing an interface at run-time. So, the only methods available on the proxy are those defined in any interfaces UserInfo implements (if any). You may have to create a suitable interface that includes the setUserName() method.
Alternatively, you will need to force a CGI based proxy (the proxy is a sub-class of your class created at run-time so it doesn't need an interface). Specify:
<bean id="userInfo" class="net.sandbox.sessionbeans.UserInfo" scope="session" >
<aop:scoped-proxy proxy-target-class="true"/>
</bean>

About this comment:
I tried applying this technique. I put
inside the bean
definition and I #Autowired'd private
UserInfo userInfo. It seems to work,
but for some reason the bean's setter
function isn't executed properly...
i.imgur.com/zkxVA.png – Pieter 1 hour
ago
If you use interface-based proxies, the setter method is not available on the Proxy unless the interface has the setter method.

If you don't like XML, you can also use ObjectFactory<T> like this :
#RestController
public class MyController {
private final ObjectFactory<MySessionScopedComponent> OFSession;
#Autowired
public MyController(ObjectFactory<MySessionScopedComponent> OFSession) {
this.OFSession = OFSession;
}
#RequestMapping(path = "/path", method = RequestMethod.GET)
public String myMethod () {
MySessionScopedComponent sessionBean = OFSession.getObject();
// Do some stuff
return bean.myValue();
}
}
Note: Tested with Spring Boot 1.5.6 (Spring 4.3)

Related

Spring Instantiate a component dynamically inside a method

I'm trying to instantiate a component inside a method, to be more precise inside a rest method. I need a new instance of the component every time the rest method is invoked
#Validated
#RestController
#RequestMapping("test")
public class Test {
#GetMapping
public String test() {
// Spring equivalent of
// TestComponent component = new TestComponent();
return component.uuid();
}
}
My component is defined like this
#Scope
#Component
#Transactional
public class TestComponent {
private EntityManager entityManager;
private UUID randomUUID;
#Autowired
public TestComponent(EntityManager entityManager) {
this.entityManager = entityManager;
this.randomUUID = UUID.randomUUID();
}
public String uuid() {
// entityManager transactional stuff
return randomUUID.toString();
}
}
I tried to use a factory method but the instance was not transactional, I tried to use ApplicationContext.getBean but the instance was a singleton. How can I instantiate my component dynamically whenever I need it?
I'm using Spring 3.0.0-RC1
You can use different scopes to define the life cycle and visibility of that bean in the context. The default scope is Singleton, which means only one instance is created.
In your particular case, I think that you should choose between:
Prototype - creates a new instance every time a request for that specific bean is made. It can be defined with #Scope("prototype") annotation.
Request - each HTTP request has its own instance, only valid in the context of a web-aware Spring ApplicationContext. It can be defined with #RequestScope annotation.
To find out more details about all supported scopes, check the documentation.

Spring #Cachable method within the same class (self-invocation, proxy issue) - What is the best way to solve it?

I'm trying to call a #Cacheable method from within the same class.
And it didn't work. Because of:
In proxy mode (the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the target object that calls another method of the target object) does not lead to actual caching at runtime even if the invoked method is marked with #Cacheable. Consider using the aspectj mode in this case. Also, the proxy must be fully initialized to provide the expected behavior, so you should not rely on this feature in your initialization code (that is, #PostConstruct).
It means, #Cachable(also #Transactional) works by proxy classes which is Spring AOP in. a internal call in the same class make call by 'this' instead of proxy classes.
To solve the problem, I should call a method by proxy or using AspectJ(another AOP).
So, I found 4 solutions.
What is your choice? and why others are not recommended?
Please, share your opinion!
using AspectJ (another AOP)
get the Bean from ApplicationContext and use it
#Service
public class UserService implements Service {
#Autowired
private ApplicationContext applicationContext;
private Service self;
#PostConstruct
private void init() {
self = applicationContext.getBean(UserService.class);
}
}
self-autowiring using #Resource //since Spring 4.3
#Component
#CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {
/**
* 1. Self-autowired reference to proxified bean of this class.
*/
#Resource
private SphereClientFactory self;
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull TenantConfig tenantConfig) {
// 2. call cached method using self-bean
return self.createSphereClient(tenantConfig.getSphereClientConfig());
}
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull SphereClientConfig clientConfig) {
return CtpClientConfigurationUtils.createSphereClient(clientConfig);
}
}
make the Bean scope of the class as 'prototype' instead of 'singleton'
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {
private final AService _aService;
#Autowired
public AService(AService aService) {
_aService = aService;
}
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = _aService.getEmployeeData(date);
...
}
}
I'm a newbie in spring :)
Actually, I choose the 4th solution, but I felt it isn't a good way. because I just need to call the caching method by proxy, and it make several beans to achieve it.
After reading articles, I think AspectJ is the best choice. It looks cool, Spring recommends it, and many people also recommend too.
But I don't understand how to AspectJ works (I will study) and I also don't know why others is not recommended.
references
Spring Cache #Cacheable - not working while calling from another method of the same bean
Spring cache #Cacheable method ignored when called from within the same class
https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring
https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache

Why beans (with request scope) are not initialized in every request in controllers?

My ActionResponse code is :
#Component
#Scope(value = "request",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ActionResponse{
public int a;
//body
}
My Controller:
#Controller
#RequestMapping(value="/ajax/discussion")
public class DiscussionController extends AbstractController {
#Autowired
private ActionResponse actionResponse;
public void setActionResponse(ActionResponse actionResponse) {
this.actionResponse = actionResponse;
}
#RequestMapping("/test")
public #ResponseBody String test(){
String response=this.actionResponse.a+"";
if(this.actionResponse.a==0)
this.actionResponse.a=10;
return response;
}
}
I start the project and then the first time I request /ajax/discussion/test it shows 0
but after that for other requests it shows 10
It has to show 0 in every request because of request scope for ActionResponse
The question is:
Why the bean(ActionResponse) is created once not in every request?!!!
CGLIB works on class level.
CGLIB proxy is still a singleton, so it inherits the fields from the base class. When you change its public properties you change the values of the singleton.
You should encapsulate your data changes in public getters and setters.
Was a little late - Just adding on to Boris Treukhov's answer(have +1'd it):
The reason is that since you have annotated ActionResponse with #Scope(proxyMode=..) Spring ends up creating a CGLIB subclass of this ActionResponse which internally handles the scope appropriately.
Now when you inject ActionResponse into the DiscussionController it is the CGLIB proxy that gets injected, and since you are setting the fields directly with going through the setter, it just modifies the fields of the proxy and not the underlying scoped proxied object. The fix is simply to make state changes via the getters and setters not through fields.

AspectJ autoproxy issues with Spring Controllers and Webflow Actions

I have two related issues regarding spring/AspectJ AOP. I have a typical logger aspect which logs exceptions thrown from any class in my application including services, daos, controllers and webflow actions...
#Aspect
public class AspectLogger {
#AfterThrowing(pointcut = "execution(* com.myapp..*.*(..))", throwing = "t")
public void logGustavoException(JoinPoint joinPoint, Throwable t) {
Log logger = LogFactory.getLog(joinPoint.getTarget().getClass());
logger.error(t.getMessage(), t);
}
}
In my application context I have an equally typical configuration...
<context:annotation-config />
<!-- AOP logging config -->
<aop:aspectj-autoproxy>
<aop:include name="aspectLogger" />
</aop:aspectj-autoproxy>
<bean id="aspectLogger" class="com.myapp.AspectLogger" />
This works fine for the most part, the issue I have is with the webflow actions and controllers which implement an interface.
1 - Controllers which implement an Interface...
One of our controllers implements an interface which defines one method, as well as defining several public methods which are used as #RequestMapping handlers...
#Controller
public class AmazingController implements OutstandingInterface {
// implements the method from OutstandingInterface
#Override
public Object doSomethingOutstanding(){
...
}
#RequestMapping(value="/amazingUrl.htm", method = RequestMethod.GET)
public String doSomethingAmazing(HttpSession session, ModelMap model) {
return "anAmazingViewName";
}
...
}
The issue here is that due to the fact that the controller implements an interface that doesn't define all its public methods (i.e. controller request mapping methods), a proxy is created for the controller which only proxies the 'doSomethingOutstanding' method from OutstandingInterface. As such, when a request comes in to /amazingUrl.htm, Spring does not route it to the appropriate request handler - it's as though the request mapping doesn't exist. I have solved this by defining an interface for the controller which extends OutstandingInterface and also defines the request handler methods required by the controller, but it seems odd/wrong to me to have to define an interface for a controller just so that the AspectJ stuff doesn't 'hide' the request handler...
#Controller
public interface IAmazingController extends OutstandingInterface{
#RequestMapping(value="/amazingUrl.htm", method = RequestMethod.GET)
public String doSomethingAmazing(HttpSession session, ModelMap model);
}
...
public class AmazingController implements IAmazingController {
#Override
public Object doSomethingOutstanding(){
...
}
#Override
#RequestMapping(value="/amazingUrl.htm", method = RequestMethod.GET)
public String doSomethingAmazing(HttpSession session, ModelMap model) {
return "anAmazingViewName";
}
...
}
2 - Webflow Actions
The second issue is very similar. After introducing the AspectJ configuration, none of my webflow Action classes were being autowired correctly - I kept getting 'cannot find bean of type FantasticAction' sort of errors. Again, I introduced interfaces for all of the Action classes and this solved the problem as it was the proxy that was being injected at runtime, not the actual action implementation class.
So finally... the question in both instances is - is there a way of getting around these AspectJ issues without having to define interfaces for every class I want to advise?
You should add CGLIB dependendy in your class path so you will not need to create interfaces for working with AOP
Take a look to the doc.

Autowired not working as expected

I'm using Spring Roo and want to access a bean within of Controller class which has following configuration in applicationContext.xml:
<bean class="com.reservation.jobs.Configuration" id="jobsConfiguration" autowire="byType">
<property name="skipWeeks" value="4" />
</bean>
The configuration class itself is:
package com.reservation.jobs;
public class Configuration {
private int skipWeeks;
public void setSkipWeeks(int value) {
System.out.println("SkipWeeks set auf: " + value);
this.skipWeeks = value;
}
public int getSkipWeeks() {
return this.skipWeeks;
}
}
In my Controller I thought that a simple Autowired annotation should do the job
public class SomeController extends Controller {
#Autowired
private com.reservation.jobs.Configuration config;
}
During startup Spring prints the message within the setSkipWeeks method. Unfortunately whenever I call config.getSkipWeeks() within the controller it returns 0.
Have I to use the getBean method of the ApplicationContext instance or is there some better way?
autowire="byType" is redundant. It indicates that fields of the Configuration class should be autowired, and you have just one primitive. So remove that attribute.
Apart from that, config.getSkipWeeks() must return 4 unless:
you are using a different instance (made by you with new)
you have called the setter somewhere with a value of 0

Resources