Can #PostConstruct be used as a substitute of #Bean/#Produces? - java

I was reading this post when I thought about the possibility of substituting any #Bean (Spring DI) or #Produces (CDI) with a simple #PostConstructor as in the following CDI example:
Replace:
public class MyClassFactory {
#Produces
#RequestScoped
public MyClass createMyClass() {
MyClass myClass = new MyClass();
myClass.setA(1);
return myClass;
}
}
public class MyClass {
private int a;
private void setA(int a) {
this.a = a;
}
}
With:
public class MyClass {
#PostConstruct
public void init() {
this.setA(1);
}
private int a;
private void setA(int a) {
this.a = a;
}
}
Is that correct? What are the differences between those options?

No, #PostConstruct defines an interceptor for the initialization of a bean. It cannot be used to define the instantiation of a bean.

I dont see this as an incorrect question.
#PostConstruct method is invoked after the default constructor invocation and dependency injection (if any) are finished. You can initialize instance variables of a bean using the within a #PostConstruct annotated method. In this case, the difference b/w using #PostConstruct and CDI is that, the instance returned by CDI is a contextual one and since it is #RequestScoped, the lifetime of the 'produced' bean is confined to one HTTP request. A new instance of the bean would be produced once the existing HTTP request is complete. In case of #PostConstruct, its an #Dependent scoped bean (by default) and its life cycle would 'depend' upon the bean which injects it (using CDI #Inject)

Related

#Component with request scope attributes

I have a class in my SpringBoot project with #Component. By default, the Scope of this is singleton and it's OK.
But now I need an object, with request scope, that will be used in many methods of this Component class. The only way to do this is passing this object as parameter in all methods? Or can I, for example, declare a #RequestScope attribute in a singleton, or something like that?
----EDIT
An example:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
#Autowired
private MyBC myBC;
private MyClass myObject;
public method1(MyClass param) {
myObject = param;
method2();
}
public method2() {
System.out.println(myObject);
}
}
My problem is: in this code, myObject is a singleton. Depending on concurrency, I will have problems with different requests, one will affect the other in method2(). I need myObject to be Request Scoped.
Doesn't seem clean to me:
Firstly, let's call your #Component class as a Component.
Do you want to use another singleton object ( call it OtherS) inside your Component's methods?
Use Lombok's RequiredArgsConstructor with private static final OtherS otherSingleton, then you can use it inside your Component, in any methods.
OR
Do you want to use Component's object in other methods of other class?
You can inverse previous block and make same thing with Component (as with OtherS earlier). As you declare it as a Bean inside another Bean using #RequiredArgsConstructor it will not be null and will be used as a Singleton class attribute (so you can access it with this-kw).
You can define MyClass as a #RequestScope component as follows:
#Component
#RequestScope
public class MyClass {
private Object data;
public MyClass(HttpServletRequest request) {
data = //extract data from the request
}
public Object getData() {
return data;
}
}
Then you inject it like any other bean and don't have to pass the MyClass parameter to each method. The component is instantiated by each request only if you access it, as described here.
Be aware that the request scope is not available outside a thread attached to a request. If you access it from a non-request-bound thread the following exception will be thrown:
java.lang.IllegalStateException: No thread-bound request found
You can use #Lazy annotation.
#Lazy with #Bean (or #Lazy with #Component): don't load
eagerly during application start up, until it's used in the
application
#Lazy with #Autowired : don't load during outer class initialization,
until it's first used by the application.
When started context, you musnt to load MyReqBean.
#RequiredArgsConstructor
#Configuration
#ComponentScan(basePackages = "com.xyz")
public class MyConfig {
private final MyBC myBC;
#Bean
#Lazy
#RequestScope //define req bean.
public MyReqBean myReqBean() {
return new MyReqBean(myBC);
}
}
#RequiredArgsConstructor
public class MyReqBean {
private final MyBC myBC; //singleton bean
public method2() {
System.out.println(myBC.getX());
}
}
MyReqBean is created on each request and will be destroyed when the request ends.

Injecting a CDI interceptor into a beans does not work

I have created a CDI (WELD) interceptor that works and intercept what it is supposed to intercept.
#MyInterceptorBinding
#Interceptor
#Dependent
public class MyInterceptor implements Serializable {
private int myIntegerField;
#AroundInvoke
public Object interceptMethod(InvocationContext ctx) throws Exception {
// Do some operations and side effects on myIntegerField;
try {
Object result = ctx.proceed();
return result;
} catch (Exception e) {
throw e;
}
}
public List<Class<?>> getMyIntegerField() {
return myIntegerField;
}
}
Where MyInterceptorBinding is an interceptor binding:
#Inherited
#InterceptorBinding
#Target({TYPE, METHOD, PARAMETER, FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyInterceptorBinding {}
I would like to inject my interceptor into a bean class like this:
#ApplicationScoped
public class MyBean implements Serializable {
#Inject
private MyInterceptor interceptor;
public void aMethod(){
int var = interceptor.getMyIntegerField();
// use var in some way...
}
}
but this injection brings to an error:
Unsatisfied dependencies for type MyInterceptor with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject private trials.MyBean.interceptor
How can i overcome this issue? Is a problem related to the fact that is an interceptor?
Should i use the CDI portable extension facility? And, if so, how?
You can't inject interceptors because they aren't beans. What you probably can do is create a helper bean and inject that in both the interceptor and the application scoped bean. That helper bean can then contain the value, the interceptor can set it and the other bean can read it.
A warning about scopes: your MyBean is application scoped, which means that there will be only once instance. If the interceptor is used in request scoped beans, what should the value of the interceptor value be? For this to work, the helper bean should most likely also be application scoped, and you should take care of thread safety.

Spring DI having two constructors at the same time

This is an anti pattern, but I am curious what will actually happen.
If you explicitly define a no-args constructor and a constructor with an autowired parameter, how exactly will spring framework initialize it?
#Service
class Clazz {
private MyBean myBean;
public Clazz(){}
#Autowired
public Clazz(MyBean myBean){
this.myBean = myBean;
}
}
On top of above answers, if there is single constructor declared without #autowire, spring uses same constructor for injection.
If there multiple constructors, then Spring uses constructor which is #autowired.
Mentioned in Spring Doc https://docs.spring.io/spring/docs/4.3.x/spring-framework-reference/htmlsingle/#beans-autowired-annotation
As of Spring Framework 4.3, an #Autowired annotation on such a
constructor is no longer necessary if the target bean only defines one
constructor to begin with. However, if several constructors are
available, at least one must be annotated to teach the container which
one to use
The constructor marked by #Autowired will be used by spring. You can validate this by running the following code.
public class Main {
#Component
static class MyBean {}
#Service
static class Clazz {
private MyBean myBean;
public Clazz(){
System.out.println("empty");
}
#Autowired
public Clazz(MyBean myBean){
this.myBean = myBean;
System.out.println("non-empty");
}
}
#Component
#ComponentScan("my.package")
private static class Configuration {
}
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext();
ctx.register(Configuration.class);
ctx.refresh();
ctx.getBean(Clazz.class);
}
}
The code prints non-empty.
Spring choose constructor first by the largest number of parameters
sortConstructors consider preferring public constructors and ones with a maximum number of arguments.
Meaning Clazz(MyBean myBean)
This is the Comparator used:
(e1, e2) -> {
int result = Boolean.compare(Modifier.isPublic(e2.getModifiers()), Modifier.isPublic(e1.getModifiers()));
return result != 0 ? result : Integer.compare(e2.getParameterCount(), e1.getParameterCount());

Why is #Required ignored by Spring?

I've read that #Required uses to make sure the property has been set.
But when I try to use it along with Spring Annotation Configuration it doesn't work.
Below you can familiarize with my code base.
#Configuration
#ComponentScan
public class AppConfig {
#Bean(initMethod = "initMethod")
public SimpleClass simpleClass(){
return new SimpleClass();
}
}
public class SimpleClass implements InitializingBean {
private int n;
public SimpleClass() {
System.out.println("constructor");
}
public int getN() {
return n;
}
#Required
public void setN(int n) {
System.out.println("setter");
this.n = n;
}
void initMethod(){
}
#Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet()");
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
SimpleClass simpleClass = context.getBean(SimpleClass.class);
}
}
Why does Spring application context create the SimpleClass Bean and don't complain about the absence of injection via setter?
UPD:
When I try to do the same using XML configuration and add then I receive a "Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Property 'n' is required for bean 'simple'"
#Required documentation states (emphasis is mine) :
Marks a method (typically a JavaBean setter method) as being
'required': that is, the setter method must be configured to be
dependency-injected with a value.
With Spring in order to configure a method as dependency injected you have to specify it (#Autowired is the standard way).
But specifying both #Autowired and #Required on a method seems clumsy today :
#Autowired
#Required
public void setN(int n) {
System.out.println("setter");
this.n = n;
}
Instead, to configure the setter to be both dependency-injected and required I advise to use only #Autowired that by default is required as you can notice :
public #interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {#code true}.
*/
boolean required() default true;
}
So it is enough :
#Autowired
public void setN(int n) {
System.out.println("setter");
this.n = n;
}
As a side note, the setter injection will probably fail as the int n will probably not be resolved as a dependency. The #Value annotation on the parameter could probably help you.
You need to use it along with #Autowired (or #Value if you inject simple values) annotation.
Referring to this article:
Without a RequiredAnnotationBeanPostProcessor bean, Spring will not complain.
So ... add another bean of type RequiredAnnotationBeanPostProcessor (using #Autowired) to make it complain.
Also, #Required annotation is to be used for Bean setter. You are trying to do #Required check on a primitive type. As far as I understand, all Spring Beans (or J2EE beans) are to be objects.
For primitive types, you can use a #Value injection.

Autowire annotation driven bean into bean specified in Java config with constructor injection

Let's say I have a service bean that's initialised by component-scanning:
#Service
public class MyService { ... }
I have a bean A configured in my Java application config, which depends on another bean B also defined in the Java config, which in turn depends on Service using constructor injection. I can use autowiring in the bean factory for A and pass it on to B:
#Bean
#Autowired
public A a(MyService service) {
return new A(b(service));
}
#Bean
public B b(MyService service) {
return new B(service);
}
However, this doesn't feel very clean to me. a() shouldn't be concerned with MyService. But if I'm to call new B(service) I need to get a service into b() somehow, and if that's a method parameter, a() needs to put something in there.
I have tried:
#Autowired private MyService service;
#Autowired
public A a() {
return new A(b());
}
#Bean
public B b() {
return new B(service);
}
But this injects null into new B(...) - evidently Spring populates the field after calling b().
I've also tried:
public B b() {
return new B(context.getBean(MyService.class));
}
... this results in a BeanInstantiationException related to a circular reference.
How can I get the MyService bean into b()'s new B(...) directly?
Update: the above is a simplification of what I actually want to do.
The real scenario is that I have a bunch of Action beans which use shared DataService bean to access data. Then I have a CompositeAction class which contains references to several Action objects and invokes the appropriate one depending on runtime criteria.
I want to configure two beans of type CompositeAction, each with its own hand-configured set of delegate Action beans. But I would like the Action beans to be autowired with the DataService, by constructor injection.
CompositeAction ----> Action ---\
| \
\-----> Action ---\ \
----> DataService
CompositeAction ----> Action ---/ /
| /
\------> Action ---/
Why not?
#Bean
public A a(B b) {
return new A(b);
}
#Bean
public B b(MyService service) {
return new B(service);
}

Categories

Resources