Spring injected beans null in nested class - java

I have a class with 2 static nested classes that do the same operation on 2 different generic types.
I exposed the 2 classes as beans and added #Autowired for the constructors as I usually do.
Here is the basic setup
abstract class <T> Parent implements MyInterface<T> {
private final Service service;
Parent(Service service){ this.service = service; }
#Override public final void doInterfaceThing(T thing){
T correctedT = map(thing);
service.doTheThing(correctedT);
}
protected abstract T map(T t);
#Service
public static class ImplA extends Parent<A> {
#Autowired ImplA (Service service){ super(service); }
A map(A a){ //map a }
}
#Service
public static class ImplB extends Parent<B> {
#Autowired ImplB (Service service){ super(service); }
B map(B b){ //map b }
}
}
And in another class I have
#Service
public class Doer {
private final List<MyInterface<A>> aImpls;
#Autowired public Doer(List<MyInterface<A>> aImpls){ this.aImpls = aImpls; }
public void doImportantThingWithA(A a){
aImpls.get(0).doInterfaceThing(a);
}
}
When I run the app, everything appears to be injected correctly and when I put a breakpoint in the ImplA and ImplB constructors, I have a not-null value for "service". I also have an ImplA bean in the aImpls list in Doer.
When I call doImportantThingWithA(a) however, "service" is null inside ImplA and I obviously die.
I'm not sure how this is possible because:
I see a nonnull value in my constructors for service which is a final field.
If spring is injecting ImplA and ImplB into another class, it should already have either injected a Service into ImplA or ImplB, or thrown an exception on bean initialization. I have nothing set to lazily load and all bean dependencies are required.
The reason for the nested classes is because the only thing that changes between the 2 implementations is the map() function. Trying to avoid extra classes for 1 line of varying code.
More info:
When I add a breakpoint in Parent.doInterfaceThing(), if I add a watch on "service" I get null as the value. If I add a getService() method, and then call getService() instead of referring directly to this.service, I get the correct bean for service. I don't know the implications of this but something seems weird with the proxying.

It looks like what is causing the issue is Parent.doInterfaceThing();
If I remove final from the method signature, "service" field is correctly populated and the code works as expected.
I don't understand at all why changing a method signature affects the injected value of final fields in my class... but it works now.

What I meant with my "use mappers" comment was something like this:
class MyInterfaceImpl implements MyInterface {
#Autowired
private final Service service;
#Override public final <T> void doInterfaceThing(T thing, UnaryOperator<T> mapper){
T correctedT = mapper.apply(thing);
service.doTheThing(correctedT);
}
// new interface to allow autowiring despite type erasure
public interface MapperA extends UnaryOperator<A> {
public A map(A toMap);
default A apply(A a){ map(a); }
}
#Component
static class AMapper implements MapperA {
public A map(A a) { // ... }
}
public interface MapperB extends UnaryOperator<B> {
public B map(B toMap);
default B apply(B b){ map(b); }
}
#Component
static class BMapper implements MapperB {
public B map(B a) { // ... }
}
}
This does have a few more lines than the original, but not much; however, you do have a better Separation of Concern. I do wonder how autowiring works in your code with the generics, it does look as if that might cause problems.
Your client would look like this:
#Service
public class Doer {
private final List<MapperA> aMappers;
private final MyInterface myInterface;
#Autowired public Doer(MyInterface if, List<MapperA> mappers){
this.myInterface = if;
this.aImpls = mappers; }
public void doImportantThingWithA(A a){
aMappers.stream().map(m -> m.map(a)).forEach(myInterface::doInterfaceThing);
}
}

Related

what is the best way to autowire parent class field in spring?

When I use spring framework, I find something that should be extract, for example, the service component (or member variable that is autowired).
Code show as below:
abstract class Payment {
PaymentService paymentService;
void setPaymentService(OrderPaymentService paymentService) {
this.paymentService = paymentService;
}
}
#Component
public class CancelPayment extends Payment{
private OtherService2 otherSerivce2;
#Autowired
#Override
public void setPaymentService(PaymentService paymentService) {
super.setPaymentService(paymentService);
}
#Autowired
public CancelPayment(OtherService2 s2) {
this.otherSerivce2 = s2;
}
}
#Component
public class CreatePayment extends Payment{
private OtherService1 otherSerivce1;
#Autowired
#Override
public void setPaymentService(PaymentService paymentService) {
super.setPaymentService(paymentService);
}
#Autowired
public CreatePayment (OtherService1 s1) {
this.otherSerivce1 = s1;
}
}
As you can see, I use setter injection in each child class. Is this a better practice than autowire their parent's member variable?
Here are DI guidelines by Spring team:
A general guideline, which is recommended by Spring (see the sections on Constructor-based DI or Setter-based DI) is the following:
For mandatory dependencies or when aiming for immutability, use
constructor injection
For optional or changeable dependencies, use setter injection
Avoid field injection in most cases
Now if you are sure you will use PaymentService I would suggest you to use constructor injection in your abstract class like this so object won't instantiate without dependency, also making it more immutable, clearer and thread safe:
abstract class Payment {
PaymentService paymentService;
public Payment(OrderPaymentService paymentService) {
this.paymentService = paymentService;
}
}
Then you can simply call super on your extended classes like this:
#Component
public class CreatePayment extends Payment{
private OtherService1 otherSerivce1;
#Autowired
public CreatePayment(PaymentService paymentService) {
super(paymentService);
}
}
This simply allows you to inject parent class using constructor (if paymentService is mandatory).

Using an autowired parameter in another instance variable

I have a service that implements an interface. I now want to write a mapper, that basically says, when I pass in this enum type, use that service. This is what I have
#Service
MyService implements Service {}
#Component
#RequiredArgsConstructor
MyMapper implements Mapper<Enum, Service> {
private final MyService myService;
private ImmutableMap<Enum, Service> MAPPER = ImmutableMap.<MyEnum, MyService>builder()
.put(Enum.A, myService)
.build();;
#Override
public Service map(Enum input) {
return MAPPER.get(input);
}
}
However, it seems that this doesn't work. I think I am not allowed to use an (autowired) instance variable for the instantiation of another instance variable.
To solve this I now used a singleton pattern.
#Service
MyService implements Service {}
#Component
#RequiredArgsConstructor
MyMapper implements Mapper<Enum, Service> {
private final MyService myService;
private ImmutableMap<Enum, Service> MAPPER = null;
#Override
public Service map(Enum input) {
if(MAPPER == null){
MAPPER = createMapper();
}
return MAPPER.get(input);
}
private ImmutableMap<Enum, Service> createMapper(){
return ImmutableMap.<MyEnum, MyService>builder()
.put(Enum.A, myService)
.build();;
}
}
This seems to work, but I was wondering if there were other options to solve this.
For this problem service locator is best fit.
My Enum:-
public enum MyEnum {
A,
B
}
Create service and with the name "A" and "B" (Name of your enum as string):-
#Service("A")
MyService1 implements Service {}
#Service("B")
MyService2 implements Service {}
Create MyMapper interface:-
public interface MyMapper {
Service map(MyEnum myEnum);
}
Configure ServiceLocatorFactoryBean :-
#Bean
public ServiceLocatorFactoryBean serviceLocatorFactoryBean(){
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyMapper.class);
return bean;
}
Start using:-
#Autowired
MyMapper mapper;
You are running into an edge case regarding final variables; even though it's marked final, the map's initializer statement runs before instance initializer blocks (which would otherwise be useful), which run before the constructor body that makes the assignment to the variable.
I'm not certain why you're wanting to create a map just to hold a singleton value, but you'll need to assign the map inside your constructor body. If you really want this setup for some reason, my suggestion would be to do this:
private final Map<Enum, Service> MAPPER;
public MyMapper(MyService myService) {
MAPPER = Map.of(Enum.A, myService);
}

How can I inject for generic inherited types?

I spent a plenty of time for finding any answers, but I think I have to ask.
I'm using Weld-SE for testing my entities.
I prepared entity randomizer for testing.
abstract class BaseEntityRandomizer<T extends BaseEntity>
implements Randomizer<T> {
#Override public T getRandomValue() {
...
}
}
class MySomeOtherEntityRandomizer
extends BaseEntityRandomizer<MySomeOther> {
#Override public MySomeOther getRandomValue() {
...
}
}
Now, with my test class, I want to inject those randomizers which each matches generic parameters
#ExtendWith(WeldJunit5Extension.class)
#AddPackages({BaseEntityRandomizer.class})
abstract class BaseEntityTest<T extends BaseEntity> {
#Test void doSome() {
}
#Inject
private BaseEntityRandomizer<T> entityRandomizer;
}
class MySomeOtherTest extends BaseEntityTest<MySomeOther> {
...
// I expect an instance of MySomeOtherRandomizer in injected
// into the entityRandomizer field.
}
Subclasses of randomizers and tests are prepared.
But I failed to make it work.
How can I make it work?
I tried with following factory class
class BaseEntityRandomizerFactory {
#Produces
public BaseEntityRandomizer<MySome> produceMySomeRandomizer() {
return new MySomeRandomizer();
}
}
I got
org.jboss.weld.exceptions.IllegalArgumentException:
WELD-001408: Unsatisfied dependencies for type BaseEntityRandomizer<T extends BaseEntity> with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject protected transient ....BaseEntityTest.entityRandomizer
at ....BaseEntityTest.entityRandomizer(BaseEntityTest.java:0)
One way to achieve this is to use CDI Programmatic lookup. In your case, I'd start with #Inject Instance<Object> and then you can use subsequent calls to select() and get() methods to pick up whichever bean you desire. Usage looks something like this (assumes existence of beans with types Foo, Bar and List<String>):
#Inject
private Instance<Object> instance;
#Test void doSome() {
// selecting and obtaining instances of beans
Foo foo = entityRandomizer.select(Foo.class).get();
Bar bar = entityRandomizer.select(Bar.class).get();
// in case you need to select a parameterized type from instance, use TypeLiteral
List<String> listBean = entityRandomized..select( new TypeLiteral<List<String>>(){}).get()
}

Error injecting a class with generic type #Inject Guice

I am trying to inject a class of Generic type (say ClassA) in another class (say ClassB) using Guice with #Inject annotation. The code of the class that is being injected is shown below:
public interface InterfaceA<T> {
}
public class ClassA<T> implements InterfaceA<T> {
private final Class<T> data;
private Dependency1 dependency1;
#Inject
public ClassA(Class<T> data, Dependency1 dependency1) {
this.data = data;
this.dependency1 = dependency1;
}
}
Code of ClassB is as follows:
public class ClassB {
private InterfaceA<Entity1> interfaceA;
#Inject
public ClassB(InterfaceA<Entity1> interfaceA) {
this.interfaceA = interfaceA;
}
}
The module class is as follows:
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(new TypeLiteral<InterfaceA<Entity1>>(){}).to(new TypeLiteral<InterfaceA<Entity1>>(){});
}
}
However, when the application starts, it is giving the following error:
ERROR [2017-01-14 19:54:00,646] com.hubspot.dropwizard.guice.GuiceBundle: Exception occurred when creating Guice Injector - exiting
! com.google.inject.CreationException: Unable to create injector, see the following errors:
!
! 1) Could not find a suitable constructor in java.lang.Class. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
! at java.lang.Class.class(Class.java:119)
Any inputs on how to solve this would be really helpful. Thanks in advance.
You can't do this in a declarative way. You have to use provider methods:
public class MyModule extends AbstractModule {
#Override protected void configure() {}
#Provides InterfaceA<Entity1> provideInterfaceAEntity1(Dependency1 dep) {
return new ClassA<Entity1>(Entity1.class, dep);
}
}
This is the only way because you can't automagically inject Class<T> in ClassA.
You need such a method in your Module for each Entity you want to couple with InterfaceA.
I finally found out the solution. Here, instead of injecting Class<T> data in ClassA's constructor, I am injecting TypeLiteral<T> literal and reading the class type from TypeLiteral using it's getRawType() method.
Code for ClassA is as follows:
public class ClassA<T> implements InterfaceA<T> {
private final Class<? super T> data;
private Dependency1 dependency1;
#Inject
public ClassA(TypeLiteral<T> literal, Dependency1 dependency1) {
this.data = literal.getRawType();
this.dependency1 = dependency1;
}
}
The rest of the code for other classes remains same as before.

What is the difference between putting #Autowired to a variable and a method?

Class A {
private B instanceB;
#Autowired
public setInstanceB(B instanceB) {
this.instanceB = instanceB;
}
}
Above one versus this one.
Class A {
#Autowired
private B instanceB;
public setInstanceB(B instanceB) {
this.instanceB = instanceB;
}
}
Will the behavior differ based on the access modifier ?
The difference is the setter will be called if that's where you put it, which is useful if it does other useful stuff, validation, etc. Usually you're comparing:
public class A {
private B instanceB;
#Autowired
public setInstanceB(B instanceB) {
this.instanceB = instanceB;
}
}
vs
public class A {
#Autowired
private B instanceB;
}
(ie there is no setter).
The first is preferable in this situation because lack of a setter makes mocking/unit testing more difficult. Even if you have a setter but autowire the data member you can create a problem if the setter does something different. This would invalidate your unit testing.

Categories