Spring autowried in Abstract class construtor - java

I have a scenario where autowired is null when called in the constructor of an abstract class like this :
public abstract class AbstractClass{
#Autowired
#Qualifier("proId")
protected Prop prop;
public AbstractClass(){
prop.getName();
The above throws NullException on deployment.
But the following works when the autowired property called after instantiating
public abstract class AbstractClass{
#Autowired
#Qualifier("proId")
protected Prop prop;
public void Init(){
prop.getName();
}
}
public class DefaultClass extends AbstractClass(){
...
#autowired
DefaultClass dc ;
...
dc.Init();
How can I get the first case to work ?

You can't. Injection can only happen after an object has been created (or during construction with constructor injection). In other words, when prop.getName() is called inside the abstract class constructor, the field is still null since Spring hasn't processed it.
Consider refactoring your code so that your abstract class has a constructor that accepts a Prop argument and use constructor injection
public AbstractClass(Prop prop) {
this.prop = prop;
prop.getName();
}
...
#Autowired
public DefaultClass(Prop prop) {
super(prop);
}

Do you know object creation life-cycle in Java?
Spring doesn't do any magic about that.
There are multiple phases when using Spring for creating beans
an object is instantiated using the constructor
dependencies are injected into bean (obviously except for constructor dependencies which are passed through the object in first phase)
objects #PostConstruct (or InitializingBean) methods are called.
Remember that before constructor there is no instance of your bean, so Spring can not wire anything in it!

Related

How to inject bean to configuration overriden method

I'm using spring and want to implement some java-configuration in my application.
Here is spring's abstract config, for example
#Configuration
class AbstactConfiguration {
protected void configureSettings(Builder builder) {}
}
And my implementation
I want to setup some injected collection in configureSettings method, so I've done field injection to configuration class, since I can't add this field as argument to override method
class ConfigurationImpl extends AbstactConfiguration {
#Autowired(required = false)
Collection<Something> collection;
#Override
protected void configureSettings(Builder builder) {
builder.addCollection(collection)
}
}
Am I doing it right? Because I got warning: Field injection is not recommended
You should create a constructor of ConfigurationImpl and it must has "Collection<>" as an input. So you can inject bean from another class.

How to override/replace #MockBean field declared in the parent class with #Autowired real bean

To avoid the Spring context reloading again and again, I have moved #MockBean annotated injection to a parent class, something like this.
#SpringBootTest
.......
public abstract BaseTest {
#MockBean
protected OneService oneService;
This serves the test classes which need a mock for OneService. However, for the test that I would like also extended from BaseTest, but inject the real OneService via #Autowired will not work, as it will inherit the mock injection from the BaseTest.
public AnotherTest extends BaseTest {
#Autowired
protected OneService oneService;
Even I used #Autowired annotation, the oneService field will be the mock instance inherited from BaseTest.
Is there a way I can force inject to use autowired?
In Java there is technically no way to override a field. When you declare a field with the same name in a subclass, that field shadows the field with the same name in the superclass. So that is one hurdle.
The second hurdle stems from the fact that there is no way in Spring Boot's testing support to turn off mocking of a bean in a subclass if the superclass configures the mocking via #MockBean.
Thus, the solution is to invert what you are trying to do by having the real bean injected via #Autowired in the superclass and then turn on mocking for the specific bean type in the subclass.
Declare the #Autowired field in the superclass but do not redeclare the field in the subclass.
Declare #MockBean at the class level in the subclass, specifying which classes (i.e., bean types) to be mocked.
That last step results in the inherited field being injected with a mock in the subclass.
The following two classes demonstrate this technique in practice.
#SpringBootTest(classes = BaseTests.Config.class)
class BaseTests {
#Autowired
protected Service service;
#Test
void service() {
assertThat(service.getMessage()).isEqualTo("real");
}
#Configuration
static class Config {
#Bean
Service service() {
return new Service();
}
}
static class Service {
String getMessage() {
return "real";
}
}
}
#MockBean(classes = BaseTests.Service.class)
class MockBeanTests extends BaseTests {
#BeforeEach
void setUpMock() {
when(service.getMessage()).thenReturn("mock");
}
#Test
#Override
void service() {
assertThat(service.getMessage()).isEqualTo("mock");
}
}

Java Spring: getting the generic type

I have a parameterized class that implements the FactoryBean interface:
public class DogFactory<T extends Dog> implements FactoryBean<T> {
// ...
}
What I want is to use this factory for spawning objects of different classes (all of these classes extend Dog). So I imagined that I could do something like the following:
public class ShepherdService {
private DogFactory<Shepherd> shepherdFactory;
public ShepherdService(
#Autowired
DogFactory<Shepherd> shepherdFactory
) {
this.shepherdFactory = shepherdFactory;
}
// ...
}
Alas, I get the following error: Couldn't autowire. No beans of DogService<Shepherd> type found. :-(
How to inject it and use as DogFactory<Shepherd> or DogFactory<Corgi> (not just DogFactory)?
And another question. I also need to pass the Shepherd.class (or Corgi.class) to this bean, so it could "know" at run-time, objects of what exactly class should it produce. Is there a way to do that?
Or should I forget about FactoryBean and instantiate the factory as a regular class? Of course, I could do it this way:
DogFactory<Shepherd> shepherdFactory = new DogFactory<Shepherd>(Shepherd.class);
It would work perfectly, but I'd like to use FactoryBean as I use it for other factories in this project, so I would like to stick to FactoryBean.
Thanks in advance!
Update 1
Maybe I should clarify it more precise. What I need is a factory that could produce objects of different classes. All these classes should be extensions of the certain class (e.g., Shepherd and Corgi - all these classes extend Dog). And as the final result I actually need something like that:
public class DogService<T extend Dog> {
private DogFactory<T> dogFactory;
#Autowired
public DogService(DogFactory<T> dogFactory) {
this.dogFactory = dogFactory;
}
public T spawnDog(Color color) {
T dog = DogFactory.getObject(color);
return dog;
}
}
But it seems that I can't make a "universal" factory with the FactoryBean interface, so I can't do something like that:
public class DogFactory<T extends Dog> implements FactoryBean<T> {
// ...
}
Instead of that I have to do something like the following:
public class ShepherdFactory implements FactoryBean<Shepherd> {
// ...
}
public class CorgiFactory implements FactoryBean<Corgi> {
// ...
}
Is that true?
Thanks!
What I want is to instantiate this factory in another class
If you mean "having the factory injected in the class instead of the bean it creates", you could inject the factory by prefixing its name with an ampersand:
public class ShepherdService {
private DogFactory<Shepherd> shepherdFactory;
#Qualifier("&dogFactory")
public ShepherdService(DogFactory<Shepherd> shepherdService) {
this.shepherdService = shepherdService;
}
}
From the Spring documentation (section 3.8.3):
When you need to ask a container for an actual FactoryBean instance
itself, not the bean it produces, you preface the bean id with the
ampersand symbol & (without quotes) when calling the getBean method of
the ApplicationContext. So for a given FactoryBean with an id of
myBean, invoking getBean("myBean") on the container returns the
product of the FactoryBean, and invoking getBean("&myBean") returns
the FactoryBean instance itself.
Furthermore, my guess is that the FactoryBean is not picked up as a Spring bean as your snippets did not contain an XML config section, a Java config section, or a #Component annotation. I would explicitly declare the factory bean in a #Configuration annotated class, or annotate the factory class with #Component.
[edit]
But it seems that I can't make a "universal" factory with the
FactoryBean interface, so I can't do something like that:
You could still have a single Configuration class in which you declare all the factory beans.
#Configuration
public class DogFactoryConfig {
#Bean
public DogFactory<Shepherd> shepherdFactory() {
return new DogFactory<Shepherd>();
}
#Bean
public DogFactory<Corgi> corgiFactory() {
return new DogFactory<Corgi>();
}
}
And remove the #Component annotation from your DogFactory if it is present.

CDI bean is not injected but can be looked up

So, I wrote an extension which registers bean I am trying to create. The bean gets scanned by CDI and I can get it using:
MyInterface myInterface = CDI.current().select(MyInterface.class).get();
And I can then access myInterface.myMethod();
However, when I try to inject my bean using:
#Inject
#MyBean
MyInterface myInterface;
it is not injected and is null.
What I want to achieve is that I specify interface, which defines some methods,then my code generates instance of this interface and returns proxy of interface type:
// defined interface
#RegisterAsMyBean
interface MyInterface {
void myMethod();
}
// usage in code:
#Inject
#MyBean
MyInterface myInterface;
I declared my bean like this:
public class MyExtension implements Extension {
public void register(#Observes #WithAnnotations(RegisterAsMyBean.class) ProcessAnnotatedType<?> aType) {
Class<?> typeDef = aType.getAnnotatedType().getJavaClass();
if(typeDef.isInterface()) {
proxyTypes.add(typeDef);
aType.veto();
}
}
}
public class BeanCreator implements Bean<Object> {
#Override
public Object create(CreationalContext<Object> creationalContext) {
// my instance building logic
}
// ... other overriden methods
}
Also in META-INF/services/javax.enterprise.inject.spi.Extension I put reference to MyExtension
The problem was in bean creation lifecycle - i was trying to create bean in processAnnotatedType cycle, while I should have done that during afterBeanDiscovery cycle.
After moving my bean creation logic to appropriate cycle the bean is created and properly injected.

Spring: Choosing constructor while Autowiring a Component

I've a Component as follows:
#Component
class A(){
private s;
public A(){}
public A(String s){this.s=s;}
}
Here is the other class Where I'm auto wiring the above class:
#Component
class B(){
#Autowire
private A a;
}
In the above autowiring, I need to use the parameterized constructor. How can I pass the constructor args?
You can't, at least not via #Autowired in B but there are other ways to do it:
Wire the parameter into A's constructor:
One constructor is annotated with #Autowired because:
As of Spring Framework 4.3, the #Autowired constructor is no longer
necessary if the target bean only defines one constructor. If several
constructors are available, at least one must be annotated to teach
the container which one it has to use.
#Component
class A(){
private s;
public A(){}
#Autowired
public A(#Value("${myval}") String s){this.s=s;}
}
Expose A as a #Bean
Straight from the docs:
#Configuration
public class AppConfig {
#Bean
public A a(#Value("${myval}") String s) {
return new A(s);
}
}
Construct A in B using an initialization callback
Docs
#Component
class B(){
private A a;
#Value("${myval}")
private String myval;
#PostConstruct
private void init()
{
a = new A(myval);
}
}
There is a concept of prototype bean which I think you require in your case. #Component will create a singleton bean and changing it in one place will change in all parent classes where this was injected.
You need to understand how to inject a prototype bean in singleton bean.
Follow this example
https://stackoverflow.com/a/25165971/949912
Just use setters instead of constructor.
If you want to create object by yourself with new keyword then this object will not be managed by container.

Categories