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.
Related
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.
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.
I have a Wicket panel in which I want to inject bean using #SpringBean
public class SomePanel extends Panel {
#SpringBean
private BlogSummaryMailGenerator blogSummaryMailGenerator;
}
But this BlogSummaryMailGenerator has injection via constructor defined like this:
#Component
public class BlogSummaryMailGenerator {
private BlogRepository blogRepository;
private BlogPostRepository blogPostRepository;
#Autowired
public BlogSummaryMailGenerator(BlogRepository blogRepository,
BlogPostRepository blogPostRepository) {
this.blogRepository = blogRepository;
this.blogPostRepository = blogPostRepository;
}
}
And when SomePanel is instantiated I am getting an exception
Caused by: java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:721) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:499) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) ~[cglib-3.1.jar:na]
at org.apache.wicket.proxy.LazyInitProxyFactory.createProxy(LazyInitProxyFactory.java:191) ~[wicket-ioc-7.2.0.jar:7.2.0]
Adding empty no-args constructor to the BlogSummaryMailGenerator solves this issue but adding such code only to make injection work is wrong and I would like to avoid it.
Any suggestions how to make #SpringBean work with beans using injection via constructor?
The real problem is in CGLIB. It requires a default constructor to be able to create the proxy instance. The real Spring bean is created separately by Spring and has no such restrictions. The default constructor needed by CGLIB could be even private as far as I remember.
Update: Since Wicket 9.5.0 Wicket could also use ByteBuddy instead of CGLib.
Another solution is to use an interface for this bean. Then Wicket will use JDK Proxy instead of CGLIB and in this case there is no need of default constructor in the implementation.
Solution
To be able to take advantage of constructor injection for #SpringBean in Wicket components you just have to add Objenesis to your compile time dependencies.
Explanation
Objenesis is a little and less known byte code manipulation library which (in opposite to CGLIB provided together with Wicket) is able to create a proxy object for a class which has no default (no args) constructor. If you add it as a compile dependency to your project then Wicket will switch it's internal lazily initializable proxy creation logic to take advantage of Objenesis (instead CGLIB which requires no args constructor) while instantiating a proxy. Unfortunately this feature is not documented but I can confirm it works in my case.
The error message is pretty clear. Wicked trying to create instance of proxy class for BlogSummaryMailGenerator with CGLIB library. Since you didn't (or you can't) provide arguments to constructor, it looking for contstructor with no arguments. But it can't, and you get error.
Just replace constructor injection with property injection, and create no argument constructor:
#Component
public class BlogSummaryMailGenerator {
#Autowired
private BlogRepository blogRepository;
#Autowired
private BlogPostRepository blogPostRepository;
public BlogSummaryMailGenerator() {}
}
Actually, you do not need to declare an empty constructor. I did it just for clarity. Note, that BlogRepository and BlogPostRepository should be declared as beans (marked with #Component annotation, or created as #Bean in Spring configuration).
UPDATE:
When you add SpringComponentInjector in your WebApplication.init(), you can specify false for third paramter, which means 'wrapInProxies'. Wicket will never wrap Spring beans in porxy, and you can use #Autowired for constructors.
#Override
public void init()
{
super.init();
AnnotationConfigApplicationContext springContext =
new AnnotationConfigApplicationContext();
springContext.register(SpringConfig.class);
springContext.refresh();
getComponentInstantiationListeners().add(new SpringComponentInjector(this,
springContext, false));
}
The correct way to solve this is not to add Objenesis to your project, but to inject interfaces instead of concrete implementations, as #martin-g already explained (of course, we do not always have the privilege to be able to do the right thing, but when we do, we should do it).
I have a project that started to give the exact same error and stack after a library update I still don't exactly understand (complete Maven dependency hell, I inherited it, go easy on me). The reason was that I was creating a Spring request-scoped bean from a concrete subclass of ListModel<MyClass> and Wicket was hell bent on wrapping that class into a lazy loaded proxy, which it couldn't do because there was no zero-args-constructor.
I fixed it by changing the configuration class to create a named instance of IModel<List<MyClass>> and by defining the injected dependency using the name.
In the configuration class:
#Bean(name = "namedModel")
#RequestScope
public IModel<List<MyClass>> myObjectList() {
return new MyClass(parameters);
}
In the component:
#Inject
#Named("namedModel")
private IModel<List<MyClass>> myModel;
I've seen the #Autowired annotation placed just before the constructor of a POJO used as controller.
#Controller
public class LoginController{
private UsuarioService usuarioService;
#Autowired
public void LoginController(UsuarioService usuarioService){
this.usuarioService = usuarioService;
}
// More code
}
This constructor takes as argument the reference to the object we want Spring to inject.However if I place this annotation just before the property declaration the application works just the same.
#Controller
public class LoginController{
#Autowired
private UsuarioService usuarioService;
// More code
}
My question is what is the difference between this two approaches in terms of pros and cons.
My advice to you would be to never use #Autowired on fields (except in Spring #Configuration classes).
The reason is very simple: TESTING!!!!
When you use #Autowired on fields of a class, then that class becomes harder to unit test because you cannot easily use your own (possible mocked) dependencies for the class under test.
When you use constructor injection then is becomes immediately evident what the dependencies of the class are, and creating that class becomes straight forward (simple constructor call).
Some points that need to made:
1) Some might argue that even when #Autowired is used the class can still be unit tested with the use of Mockito's #InjectMocks, or Spring's ReflectionTestUtils.setField, but my opinion is that the creation of a unit under test should be as dead simple as possible.
2) Another point that could be mentioned is that there might be many arguments in the constructor making the manual invocation of the constructor (either in the test or elsewhere) difficult. This however is not a problem regarding the creation of the class, but a problem in the design. When a class has to many dependencies, in most cases it is trying to do too much and needs to broken into smaller classes with fewer dependencies.
#Autowired annotation on setter methods is to get rid of the element in XML configuration file. When Spring finds an #Autowired annotation used with setter methods, it tries to perform byType autowiring on the method
#Autowired annotation on properties is to get rid of the setter methods. When you will pass values of autowired properties using Spring will automatically assign those properties with the passed values or references.
Here is an example of both the usage:
http://www.tutorialspoint.com/spring/spring_autowired_annotation.htm
This Springsource blog post mentions that constructor injection makes it easy to validate required dependencies, if used in combination with contructors assertions that are good practice anyway and would also work if the class is instantiated outside Spring with the new operator:
#Controller
public class LoginController{
private UsuarioService usuarioService;
#Autowired
public void LoginController(UsuarioService usuarioService){
if (usuarioService == null) {
throw new IllegalArgumentException("usuarioService cannot be null.");
}
this.usuarioService = usuarioService;
}
}
This type of assertions are general best practice that is advised to do independently of the class being a Spring bean or not.
For setter or property injection there is also the #Required annotation for validating missing dependencies or #Autowired(required = true). According to the blog post, constructor injection provides this advantage, but for historical reasons setter injection is more frequently used in Spring.