How to inject a session scoped bean into another session scoped bean without proxy?
#Component
#Scope("session")
class Foo {
#Inject Bar bar;
}
#Component
#Scope("session")
class Bar {
}
It reports error "No matching bean". Though a TARGET_CLASS scope-proxy could resolve this problem, but why do I need a proxy for same scoped beans?
My guess is - because at the injection point spring doesn't distinguish injected beans depending on the scope of the current bean. It needs a proxy to fetch the target bean (from the session in this case), disregarding the scope of the bean where it's injected into.
Related
If I have a Spring bean that contains another non Spring bean like so :
#Component
class SpringBean {
private MyBean mb = new MyBean();
}
I receive error : unable to wire bean MyBean
But if I declare the Bean within a method :
#Component
class SpringBean {
private void myMethod(){
MyBean mb = new MyBean();
}
}
Then the bean is initialized correctly when the bean is called.
So does Spring not allow non Spring beans to be created by Spring beans ? What is the reasoning behind this?
Only beans created by the Spring container will be managed by Spring and provide dependency injection and all the benefits (and problems) that Spring gives you.
So does Spring not allow non Spring beans to be created by Spring beans ?
Spring beans are POJOs after all, so they can be manually created like any class with a public constructor. This means, you can do:
public void foo() {
SpringBean springBeanNotManagedBySpring = new SpringBean();
//use springBeanNotManagedBySpring...
}
But, as stated at the beginning of my answer, springBeanNotManagedBySpring is not managed by Spring, so it won't have the benefits of a Spring managed bean.
Note that you're doing two different things in your example:
In the first code, you have a field in the class of type MyBean and Spring tries to inject it but looks like doesn't find a Spring bean to inject. You should post the configuration of your bean in order for us to do a better analysis on this.
In the second code, you're creating a local variable (completely different from a field in class) of type MyBean, where the instantiation is up to the programmer.
i've a question about the new JEE6 CDI specification, in particular the use of #Inject in conjunction with #Named annotations that seems to have replaced the jsf specific #ManagedBean (for register resource) and #ManagedProperty (for injection) annotations.
Assuming you have a bean class "User" annotated #ManagedBean #SessionScoped and an object of this class is injected in any bean using #ManagedProperty then when this object is created and injected it is also put in session and i could access the session (for example in a filter) and retrieve object using:
User user = (User)((HttpServletRequest) request).getSession().getAttribute("user");
So, if I try the same logic (in glassfish 3.1.2) using #Named #SessionScoped and then #Inject I can confirm that there is no such object of type User in session, but this is what i find in session:
attribute_name: org.jboss.weld.context.conversation.ConversationIdGenerator
attribute_value: org.jboss.weld.context.conversation.ConversationIdGenerator#b374765
attribute_name: org.jboss.weld.context.ConversationContext.conversations
attribute_value: {}
where i'm doing wrong?!
If you annotate a bean with #SessionScoped, you are basically binding its lifecycle to the HttpSession. This does not mean that the bean is physically injected into the session-object.
Assuming you have a bean class "User" annotated #ManagedBean
#SessionScoped and an object of this class is injected in any bean
using #ManagedProperty then when this object is created and injected
it is also put in session and i could access the session (for example
in a filter) and retrieve object using:
Very important: don't mix JSF and CDI annotations. Make all beans you want to access from within a JSF template #Named, and use CDI's #Inject and #...Scoped, that's sufficient.
To answer your question:
#SessionScoped
public class User { ... }
#RequestScoped
public class SomeController {
#Inject
User user
...
}
This will create User when the session starts and inject it into SomeController when SomeController is instantiated (with a new request, in this example).
Have a look at the Weld documentation to get a more complete start with that topic...
I want to inject the current user using #Inject #Current User across all layers (i.e. web layer, EJB layer). In order to do this, I have the following CDI Producer method:
#Named
#SessionScoped
public class UserController {
#Resource SessionContext sessionContext;
#EJB UserDao userDao;
#Produces #Current
public User getCurrentUser() {
String username = sessionContext.getCallerPrincipal().getName();
User user = userDao.findByUsername(username);
}
}
#Qualifier
#Target({TYPE, METHOD, PARAMETER, FIELD})
#Retention(RUNTIME)
public #interface Current{}
Now, I want to inject the current user into an EJB stateless session bean as follows:
#Stateless
public class SomeBackendService {
#Inject #Current
private User user;
}
My question: Is the current user object always re-injected after the session changes, because the dependencies of a stateless session bean are normally injected once at creation time and the bean may be pooled and used across different sessions?
Although I haven't tried this exact situation, in CDI beans are normally not re-injected. Instead, a proxy is injected that is aware of its context.
Via this mechanism, it's possible to inject say a session scoped bean in an application scoped bean. Every user of the application scoped bean goes to the same bean and the same proxy, but the proxy will then dynamically resolve calls on it to a different bean for each user.
So even though the scope of #Stateless is basically 'application', it would be possible that the proxy that represents User in your `SomeBackendService' still delegates to the correct session scoped version.
p.s.
If with layers you actually mean modules as in web and EJB modules that are part of an EAR, it's going to be a little bit more complicated, as CDI doesn't always works as expected between modules (especially in JBoss AS). This is partly due to the ambiguity of what an 'application' and thus the application scope is within an EAR.
Yes, to each business method called the container will be re-injected all dependencies of your SLSB. Here is text that guarantees this in EJB 3.1 specification:
"If a session bean makes use of dependency injection, the container injects these references after the bean instance is created, and before any business methods are invoked on the bean instance." - Section 4.3.2
I had this doubt too and I posted a question explaining this situation
here
By design, your stateless session bean should not have a state "User", it's stateless by all means.
If you want your EJB to have states, then use #Stateful instead.
Consider the following request-scoped CDI bean:
#RequestScoped
public class RequestScopedBean {
// ...
}
Now, I inject it in a application-scoped bean:
#ApplicationScoped
public class ApplicationScopedBean {
#Inject private RequestScopedBean requestScopedBean;
// ...
}
I ran this code and noted that the request-scoped bean instance is different between two requests but the application-scoped bean instance is the same. My doubt is: how does this work? Is the request-scoped bean instance reattributed to the application-scoped field at each request? Or the proxy of the application-scoped bean just changes between requests?
In CDI each injected object is actually a proxy. So in that case, the proxy probably holds a reference to the RequestContext and on each method invocation gets the correct bean instance.
I am new to CDI and want to use this for a JSF2 application. The class MyUser is a simple #Entity-Bean and a object is created in a #PostConstruct method in bean:
#Stateful
#Named #javax.faces.bean.SessionScoped
public class UserBean implements Serializable
{
#Named
private MyUser user;
//setter and getter
//#PostConstruct
}
Accessing the user in a JSF pages works like a charm: #{user.lastName}. But now I want to access this object from other beans, e.g. in this #ViewScopedBean:
#Named #javax.faces.bean.ViewScoped
public class TestBean implements Serializable
{
#Inject private MyUser user;
}
I want the current (logged in) MyUser user to be available in a couple of other beans, but I'm not sure how to do this. Simply #Injecting it did not work (and I'm pretty sure this would be a litte bit to simple).
13:56:22,371 ERROR [org.jboss.kernel.plugins.dependency.AbstractKernelController]
Error installing to Start: name=vfs:///Applications/Development/
jboss-6.0.0.Final/server/default/deploy/test.ear_WeldBootstrapBean state=Create:
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied
dependencies for type [MyUser] with qualifiers [#Default] at injection
point [[field] #Inject private test.controller.mbean.TestBean.user]
What is the best approach to access the user from other beans? JSF1.2 style code like UserBean bean = (UserBean)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("UserBean"); seems seems to be old fashioned!
First of all: You don't want to directly inject entities. Entities are pretty independently controlled by the ORM-framework, and have their own life cycle. Don't use them as managed beans.
According to this definition, JPA
entities are technically managed
beans. However, entities have their
own special lifecycle, state and
identity model and are usually
instantiated by JPA or using new.
Therefore we don't recommend directly
injecting an entity class. We
especially recommend against assigning
a scope other than #Dependent to an
entity class, since JPA is not able to
persist injected CDI proxies.
See here for details.
To answer your question: You cannot "outject" something like an (authenticated) user, even though this was possible in Seam 2, the whole proxy mechanism of CDI doesn't allow this anymore. What you need to do is the following:
Write a managed bean which handles the authentication and put it in the correct scope (probably session scope).
If login succeeds, use an attribute of this bean to store the authenticated user.
Use a producer method (probably with a qualifier like #LoggedIn) to make the user availabe in your application
Inject the user like this:
#Inject
#LoggedIn
private User user
That's the CDI-way ;-)
Is the #Inject ed bean a #Named bean, too?
If it is, has the MyUser bean a lesser scope than the TestBean. Remember that a #ViewScoped bean's managed properties have to be #ViewScoped, #SessionScoped or #ApplicationScoped
CDI does not specify a #ViewScoped annotation. This is a JSF2 annotation. The only possible annotations allowed are : #RequestScoped, #SessionScoped, #ApplicationScoped, #Dependent and #ConversationScoped. The first three are the only JSF scopes allowed by CDI.
If you need to support the #ViewScope annotation, you'll need write it yourself. Luckily, someone else has done this before.