I have following CDI Bean:
#SessionScoped
public class ReportService implements Serializable {
private static final long serialVersionUID = 1L;
private MyDao myDao;
#Inject
public ReportService(MyDao myDao) {
this.myDao = myDao;
}
}
#RequestScoped
public class MyDao extends AbstractDao<Order> {
protected MyDao() {
}
#Inject
public MyDao(EntityManager em) {
super(em);
}
}
If i start my webapplication (Tomcat with Weld) the following Exception is thrown:
WELD-001435: Normal scoped bean class
com.myorg.ReportService is not proxyable because it
has no no-args constructor - Managed Bean [class
com.myorg.ReportService] with qualifiers [#Any
#Default].
How is it possible to use constructor injection in a SessionScoped Bean?
Is it safe just to add a package-visible no-args constructor?
I already searched a lot, but i did not find any information about passivating a CDI Bean whitch uses Constructor Injection.
The error you are getting is based on CDI specification requirements, namely the need to have no-args constructor. When instantiating the object, CDI will of course prioritize a constructor annotated with #Inject, so don't worry about that.
The real reason why you need no-args one is for proxies. Weld/CDI will try to create one or more proxies of your object, which are basically an enriched delegates. In order to instantiate them, you want to invoke no-arg constructor - think of it as Java limiation, you shouldn't be instantiating objects without calling constructors. Therefore the spec mandates the need for no-arg constructor. As a matter of fact, Weld itself allows you to bypass this need in certain cases, but I strongly suggest against it.
Is it safe just to add a package-visible no-args constructor?
Yes, go ahead and do that.
Related
In a "monolitic" Jakarta-EE 8 application we want to use JSF in combination with CDI. The following graph gives an example on how the view and classes depend on each other:
JSF-View -> ViewController -> BeanA --> BeanA1
\-> BeanA2
In this case the ViewController is #Named + #ViewScoped and all other beans (BeanA, BeanA1, BeanA2) are #SessionScoped.
We want to use constructor injection as a best practice. Based on this our classes looks like this:
#Named
#ViewScoped
public class ViewController implements Serializable {
private final BeanA bean;
#Inject
public ViewController(final BeanA bean) {
this.bean = bean;
}
}
#SessionScoped
public class BeanA implements Serializable {
private final BeanA1 bean1;
private final BeanA2 bean2;
#Inject
public BeanA(final BeanA1 bean1, final BeanA2 bean2) {
this.bean1 = bean1;
this.bean2 = bean2;
}
}
When deploying this as a WAR to Wildfly 20 we end with the following error / exception:
"BeanA is not proxyable because it has no no-args constructor".
Since we do not plan to run the server in a cluster I do not get why we need a non-args constructor (no serialization needed at all).
Adding the META-INF/org.jboss.weld.enableUnsafeProxies file solves the problem and we can deploy and run the application without any error.
I ask myself if this is a good practice or if we miss anything?
First, the quickest possible answer: any bean that is in a normal scope must have a non-private, zero-argument constructor. In addition, such classes must not be final and must not have non-private, virtual final methods. #SessionScoped is a normal scope.
If you follow the link, you'll see that the reason for this in the CDI specification is not (perhaps primarily) because of serialization but because of proxying.
The property you refer to is a Weld-specific feature. If you know you are going to continue using Weld for your CDI implementation then you may of course continue to use this property, but strictly speaking your CDI application is now non-portable. This may or may not matter to you.
The most pragmatic, real-world solution to this problem that I've found is to define a package-private zero argument constructor that is #Deprecated that sets fields to null.
Spring's Annotation Type Required is marked as deprecated
Deprecated.
as of 5.1, in favor of using constructor injection for required settings (or a custom InitializingBean implementation)
Same for relevant RequiredAnnotationBeanPostProcessor
But it's not clear what's the replacement, it seems that it should be unavailable.
Is this change prevent us marking method as required unless it's part of constructor method ? to prevent unexpected exceptions on class creation ?
There are three ways to inject a bean via annotation:
Field injection
#Autowired
private FooService fooService;
Setter injection
private FooService fooService;
#Autowired
public void setFooService(FooService fooService) {
this.fooService = fooService
}
Constructor injection (this is the mentioned replacement)
private final FooService fooService;
#Autowired
public MyComponent(FooService fooService) {
this.fooService = fooService;
}
As you can see, the only way to declare your Service final is by using the constructor injection, which replaces the #Required annotation because it forces the user of your class to instantiate it with the required services. The user does not have to be Spring, it could be a simple unit test as well.
You should use constructor injection for mandatory dependencies and setter injections for optional dependencies instead of field injection.
Some reasons why:
It makes it clear to everybody which dependencies are required
It makes testing easier
You can make your objects immutable
Further reading:
http://olivergierke.de/2013/11/why-field-injection-is-evil/
https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/
https://spring.io/blog/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required/
Update: non-annotated constructor injection
As one commentator was wondering about a final field annotated with #Autowired while the constructor was not annotated:
If a class only declares a single constructor to begin with, it will always be used, even if not annotated.
https://docs.spring.io/spring-framework/docs/5.2.5.RELEASE/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html ("Autowired Constructors")
But even though it is not necessary to annotate the constructor in this case, I would still do it: it documents the code and if anyone will ever add another (non-annotated) constructor, the code will still work.
Yes , it is deprecated but still you can use it by mentioning below in xml file.
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />
When injecting java.util.Random into a Bean, deployment fails:
CDI deployment failure:WELD-001408: Unsatisfied dependencies for type Random with qualifiers #Default at injection point [BackedAnnotatedField] #Inject myPackage.MyBean.random
Question: Why can't an instance of the java.util.Random class be injected ?
I created a class A with similar properties (like having a final method with default visibility) that injects without problems. Here's the code:
#Named
#SessionScoped
public class MyBean implements Serializable {
#Inject private java.util.Random random; // (R)
#Inject private A a;
...
}
public class A implements Serializable {
int n;
public A() { System.out.println("A"); }
public A(int n) { this.n = n; }
final int nextInt(int bound) { return bound -n; }
}
If line (R) is commented out, everything deploys and runs fine.
You cannot inject java.util.Random as a bean because your application does not recognize any bean or producer of that given type.
You have beans.xml with discovery all - meaning CDI will go over classes within your application and turn them into beans if possible (if they meet the requirements set by CDI specification). java.util.Random is not withing your application and hence CDI knows no way to instantiate such bean for you and throws an exception. On the other hand your A bean is within your application and since you have discovery to all and it meets the requirements, CDI will consider it a bean (of #Dependent scope with #Any and #Default qualifiers).
To be able to inject java.util.Random, you need to tell CDI how to do that. You can achieve that fairly easily with a producer method. Note that producer method must be declared inside CDI bean in order for CDI to find it.
#Produces
#Dependent //choose desired scope, or none and it will be dependeny
// you can also add qualifiers, optional
public Random produceRandom() {
return new Random();
}
With the above producer, CDI will not detect it and be able to create such object when you need to #Inject it.
For a class to be discovered as a bean, it has to be deployed in a bean archive, see http://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_archive
As far as I understand, this also includes something like target/classes, but not the contents of rt.jar.
As I think that your question is only about the general mechanics and not about "how-to-inject-Random", I'll leave it at that. See the other answers on how to make Random injectable by a producer method.
Your class has to be discovered by CDI as a bean. For that you have to do something like this:
Put a bean defining annotation on it. As #Model is a stereotype it's why it does the trick. A qualifier like #Named is not a bean defining annotation, probably the reason why it doesn't work.
One of the new features in Spring 4.3 is implicit constructor injection in which we don't have to specify #Autowired anymore on top of the constructor.
My question is, how does the implicit constructor injection behave on lazy bean?
#Component
#Lazy
public class SomeClass {}
#Component
public class ClientClass {
// does SomeClass still lazily initialized???
public ClientClass(SomeClass someClass) { ... }
}
You've applied the annotation to the bean, which means that Spring will create a lazy proxy and inject it where SomeClass is required. The fact that the bean requirement comes from an implicit (singleton) constructor doesn't matter.
I'm a newbie to EJB. I want to know is can inject EJB in method of other bean session, if not why? as code below:
#Local
interface car {
public void drive();
}
#Stateless
public class Toyota implements Car {
#Override
public void drive() {
#EJB
Color color;
...
}
}
The code example is welcome.
No, you cant inject into method. You may only use #EJB at class level, field or setter like this:
#Stateless
#EJB(name="myBeanRef", beanInterface=MyBean.class) // this creates only reference - you will need to initialize it for example via initialConetxt.lookup()
public class EJBTests{
#EJB (name=”ejb/bean1”) // this injects bean named ejb/bean1
MyBean1 bean1;
MyBean2 bean2;
....
#EJB (name="ejb/bean2") // this injects bean using setter method
public void setEcho(MyBean2 bean2) {
this.bean2 = bean2;
}
}
For more details check 7.1 #EJB – injecting an EJB from the EJB 3.1 specification.
You can't. #EJB #Target is defined like this:
#Target({TYPE, METHOD, FIELD})
and #Inject #Target is defined like this:
#Target({ METHOD, CONSTRUCTOR, FIELD })
That means that annotation can only be used with listed element types. From Javadoc of #Target annotation:
Indicates the kinds of program element to which an annotation type is
applicable.
You should have LOCAL_VARIABLE as an ElementType in order to be able to inject it as a local variable of a method.
If you read more about EJBs you will actually find out, that there is a reason for that, as it would be impossible for a container to manage local variables.