I'm relatively new to Spring, and I'm trying to understand an issue I'm experiencing in which a dependency is not being autowired into a bean that is the parent of another bean that is being constructed.
The parent class is specified as follows. Note that I've abbreviated some of the logic for clarity. The Log class is implemented as an entity.
#Component
#Scope("prototype")
public class AppTransaction {
private String componentName;
#Autowired
private LogRepository logRepository;
public AppTransaction(String componentName) {
this.componentName = componentName;
}
public void writeLog(String logText) {
Log log = new Log(this.componentName, logText);
this.logRepository.save(log);
}
}
The child class is implemented as follows.
#Component
#Scope("prototype")
public class EmployeeJobTransaction extends AppTransaction {
#Autowired
private EmployeeRepository employeeRepository;
private String componentName;
private String jobName;
public EmployeeJobTransaction(String componentName, String jobName) {
super(componentName);
this.jobName = jobName;
}
public List<String> doJob() {
List<String> statuses = this.employeeRepository.getEmployeeJobStatuses();
writeLog("Job complete.");
}
}
In my service class, I'm using the application context to get an instance of the EmployeeJobTransaction bean. I'm doing this because the input parameters to the EmployeeJobTransaction bean are based on user input.
#Service
public class JobServiceImpl {
#Autowired
private ApplicationContext applicationContext;
public void initJob() {
EmployeeJobTransaction employeeJobTransaction = this.applicationContext.getBean(EmployeeJobTransaction.class, "EmployeeComponent", "CheckEmployeeJobStatus");
jobTransaction.doJob();
}
}
When I get the EmployeeJobTransaction bean, the EmployeeRepository bean is autowired as expected. However, the AppTransaction instance created does not appear to be a Spring-managed bean because the LogRepository object that has been autowired is null. I'm assuming this is because the call to super() in the EmployeeJobTransaction class does not create an instance of AppTransaction as a Spring-managed bean.
What are my options? Must I autowire the LogRepository bean into the EmployeeJobTransaction class and then pass it to the writeLog method as a parameter? I'm not understanding why the parent class is not created as a Spring bean when the child class is created as such.
For those that may come across this question in the future, I understand now that I was interpreting what Spring was doing incorrectly.
I turned on debugging and had set a breakpoint in the AppTransaction constructor.
public AppTransaction(String componentName) {
this.componentName = componentName; //Breakpoint here
}
When I executed my logic, I was confused because the autowired LogRepository bean was null.
According to the Spring documentation:
Fields are injected right after construction of a bean, before any config methods are invoked.
Therefore, it makes sense that the LogRepository bean is null in the constructor. When the writeLog method is called, the LogRepository bean is not null because it was successfully autowired AFTER the construction of the AppTransaction bean.
Thanks to those who responded.
TLDR: Autowired fields are null inside the constructor of a bean. The beans are not autowired until the construction of the bean in which they are referenced has completed.
I think you are a littel confused. I will try to explain me:
Your code shows 3 Spring beans:
AppTransaction
With an autowired dependency of the logRepository
EmployeeJobTransaction
With an autowired dependency of the employeeRepository
With an autowired dependency of the logRepository (inhereted of the parent class AppTransaction)
JobServiceImpl
When you call to he super method on EmployeeJobTransaction you are not calling to AppTransaction bean. You are calling to the parent class constructor method.
Is unnecessary to have a Spring bean of the AppTransaction class (remove the Component and Scope annotations of this class)
When you call to getBean method, Spring create a new bean each time. I think this is unnecessary. You could declare EmployeeJobTransaction bean as singleton (remove de Scope annotation) and to add the componentName and jobName attributes as parameters of doJob method.
I think woult be the best way.
Related
the below bean of Class I want to inject in other non-managed bean, but it is not working as expected
#Component
#Setter
#Getter
public class AbstractLayoutProperties {
#Value("${spring.application.name}")
private String appName;
#Autowired
S3Service s3Service;
#Autowired
S3Client s3Client;
}
Below is the class which is not managed by the spring, but I am using #Configurable
#Configurable(preConstruction = true, autowire = Autowire.BY_NAME)
#EnableSpringConfigured
public class OverlayServiceImpl
implements GenericVehicleOverlayService<T, R> {
public findOnlyActive(){
appName = layoutProperties.getAppName(); // throwint NullPointerException beacuse the object not injected properly
}
#Autowired
AbstractLayoutProperties layoutProperties;
}
One more point, findOnlyActive method I am not calling directly, I am calling this from another service, lets say
#Service
public class OtherService{
public void findActive(){
OverlayServiceImpl impl=new OverlayServiceImpl();
impl.findOnlyActive();
}
#Autowired
OtherRepository otherRepo;
}
Problem statrement:
when impl.findOnlyActive(); is executed, it should inject all the required beans inside OverlayServiceImpl. In the same class I have two beans which are autowired, it seems none of them injected, this is the reaosn that every time I am encountering Nullpointer exception. so my question is how do I make it work, what are the steps and action I need to take so that spring will inject the dependencies in non managed object i,e OverlayServiceImpl.
I've got a bean with constructor parameters which I want to autowire into another bean using annotations. If I define the bean in the main config and pass in the constructor parameters there then it works fine. However, I have no main config but use #Component along with #ComponentScan to register the beans. I've tried using #Value property to define the parameters but then I get the exception No default constructor found;
#Component
public class Bean {
private String a;
private String b;
public Bean(#Value("a") String a, #Value("b") String b)
{
this.a = a;
this.b = b;
}
public void print()
{
System.out.println("printing");
}
}
#Component
public class SecondBean {
private Bean bean;
#Autowired
public SecondBean(Bean bean)
{
this.bean = bean;
}
public void callPrint()
{
bean.print();
}
}
The constructor for Bean needs to be annotated with #Autowired or #Inject, otherwise Spring will try to construct it using the default constructor and you don't have one of those.
The documentation for #Autowired says that it is used to mark a constructor, field, setter method or config method as to be autowired by Spring's dependency injection facilities. In this case you need to tell Spring that the appropriate constructor to use for autowiring the dependency is not the default constructor. In this case you're asking Spring to create SecondBean instance, and to do that it needs to create a Bean instance. In the absence of an annotated constructor, Spring will attempt to use a default constructor.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html
I was able to test that autowiring a prototype bean, within a singleton bean results in only a single prototype bean being created.
As a solution to that, I read that I could define AOP scoped proxy for the prototype bean or use Spring's lookup method injection.
Here is what I have tried -
PrototypeBean.java
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.INTERFACES)
public class PrototypeBean implements Prototype {
private String welcomeMessage;
public String getWelcomeMessage() {
return welcomeMessage;
}
public void setWelcomeMessage(final String welcomeMessage) {
this.welcomeMessage = welcomeMessage;
}
}
SingletonBean.java
#Component
public class SingletonBean implements Singleton{
#Autowired private Prototype prototype;
public Prototype getPrototype() {
return prototype;
}
public void greet() {
System.out.println(prototype.getWelcomeMessage());
}
}
Test Class
public class AutowiredDependenciesDemo {
#Autowired private Singleton autowiredSingleton;
#Autowired ConfigurableApplicationContext context;
#Test
public void testPrototypeBeanWithAopScopedProxy(){
Assert.assertNotNull(autowiredSingleton);
Prototype prototypeBean = (Prototype) ((SingletonBean) autowiredSingleton).getPrototype();
prototypeBean.setWelcomeMessage("hello world");
autowiredSingleton.greet();
Singleton contextSingleton = (Singleton) context.getBean("singletonBean");
Assert.assertSame(autowiredSingleton, contextSingleton);
Prototype anotherPrototypeBean = (Prototype) ((SingletonBean)contextSingleton).getPrototype();
anotherPrototypeBean.setWelcomeMessage("hello india");
contextSingleton.greet();
autowiredSingleton.greet();
// i expected both the prototype instances to be different. in the debugger, it does show two different 'proxied' instances. however the test fails.
Assert.assertNotSame(prototypeBean, anotherPrototypeBean);
}
Am I missing something here ? Also, the calls to greet() method return null.
There are things you mix in your thinking about Proxies and prototype beans.
When Spring Framework injects a Prototype Scoped bean into a Singleton Scoped bean then it creates a Proxy object (which implements all required interfaces) and injects it instead of instance of the Prototype bean. Then, whenever method is called on this Prototype Proxy, Spring creates a new instance and the method is called on this new instance.
In your case - in test - you only compare injected Proxies and they are the same because there exists only 1 proxy for the Prototype Bean and this Proxy is responsible for creation of new instance of the Prototype Bean whenever needed.
Here is my example:
I have an interface Prototype and its implementation PrototypeImpl. In my test I obtain bean of type Prototype directly from ApplicationContext and I also inject it using #Autowired. Then in debugger I see this:
Notice that there is only one and the same Proxy (look at its address) but calling 'toString()' on this proxy shows two different addresses of the PrototypeImpl objects. Which shows exactly what I have written above.
EDIT: Info about un-proxying
To extend the comment by M. Deinum you can extract the underlying object from the Proxy in the following way:
Prototype extracted = null;
if(AopUtils.isAopProxy(a) && a instanceof Advised) {
Object target = ((Advised)a).getTargetSource().getTarget();
extracted = (Prototype) target;
}
This is my current scenario:
#WebListener
public class WebListenerService implements HttpSessionListener{
.... implement methods
#Produces
#Dependent
public SessionDependentService sessionDependentService(){
}
}
#SessionScoped
#Named
public class AccountController implements Serializable{
//Injected properly and works as expected
#Inject
private SessionDependnetService sessionDependentService;
#Inject
#OnLogin
private Event<Account> accountEvent;
public void onLogin(){
accountEvent.fire(authenticatedAccount);
}
}
#SessionScoped
public class AccountObserver implements Serializable{
//This does not work. It is always null.
#Inject
private SessionDependnetService sessionDependentService;
public void onLoginEvent(#Observes #OnLogin final Account account) {
//When this methods is invoked
//the sessiondependentservice is always null here.
}
}
In the AccountController, the SessionDependentService is correctly injected and is not null, while in the AccountObserver, it is always null.
EDIT:
Event using the parameter injection still results to a null value.
public void onLoginEvent(#Observes #OnLogin final Account account, final SessionDependnetService sessionDependentService) {
//When this methods is invoked
//the sessiondependentservice is always null here.
}
Netbeans correctly highlights this as an injection point.
Why is this the case?
I am using wildfly 8 server.
I changed the producer bean from SessionScoped to Stateless bean:
#Stateless
public class WebListenerSessionService {
//Use Instance since http session are dynamic.
#Inject
private Instance<HttpSession> httpSession;
#Produces
#Dependent
public SessionDependentService sessionDependentService(){
//use session to lookup existing service or produce a new one.
}
}
Even though this works fine, there is no where in the CDI spec which says Producer method must be session beans.
to quote:
A producer method must be a default-access, public, protected or private, non-abstract method
of a managed bean class or session bean class. A producer method may be either static or non-
static. If the bean is a session bean, the producer method must be either a business method of
the EJB or a static method of the bean class.
And since #SessionScoped is a managed bean class, why would it an injection into an observer bean not work.
I'm having trouble getting #Autowired to work in a class annotated by #Service, the autowired variable always is null. Let me explain:
#Service
public class Searcher extends Thread implements ISearcher {
#Autowired
protected ISessionProvider sessionProvider; <-- always null
...
public Searcher() {
sessionProvider.doSomeStuff();
}
sessionProvider here is always null.
The strange thing is that the same autowire in a #Controller does work:
#Controller
#RequestMapping("/search")
#Secured({ "ROLE_USER" })
public class SearchController extends BaseController {
#Autowired
protected ISessionProvider sessionProvider; <-- does work
#Autowired
protected ISearcher searcher;
The last line throws exception because the constructor of Searcher (implementing ISearcher) tries to access sessionProvider, which is null.
I am not sure what i might be doing wrong, it looks like spring doesn't autowire the ISessionProvider in Searcher.
It might be that spring first autowires the Searcher in SearchController, but it should first autowire SessionProvider in Searcher and next autowire Searcher in SearchController. Cause searcher cannot be autowired without a valid SessionProvider. Puzzles my brain ;-)
Can somebody offer a helping brain?
[edit]
component-scan includes my services, controllers and everything, just checked.
the I before interfaces is indeed not very nice (old habits)
not sure if this is a "duplicate" question, mainly because i'm not doing anything with "new", i let do spring do all the hard work, but i'll take a better peek.
Spring will first create the bean instance, then inject the beans. You're trying to access to the injected bean when the current bean is created, thus the bean will be null. That's default behavior.
If you want/need to execute any logic after creating the bean, use #PostConstruct decorated method, which is invoked after the bean has been created and all the dependencies injected. Here's a sample:
#Service
public class Searcher extends Thread implements ISearcher {
#Autowired
protected ISessionProvider sessionProvider;
public Searcher() {
//nothing should be here...
}
#PostConstruct
public void init() {
sessionProvider.doSomeStuff();
}
}
Spring can only do dependeny injection after the bean has been constructed. You are calling the method in the constructor and at that point the ISessionProvider hasn't been injected yet and hence it is null which in turn leads to a nice NullPointerException.
You have 2 solutions
Move the code from the constructor to a method annotated with #PostConstruct
Change the default no-arg constructor to take an argument and use that to do dependeny injection instead of the #Autowired field.
Solution 1: move that code to a method annotated with #PostConstruct.
#Service
public class Searcher extends Thread implements ISearcher {
#Autowired
protected ISessionProvider sessionProvider;
...
public Searcher() {}
#PostConstruct
public void init() {
sessionProvider.doSomeStuff();
}
Solution 2: Use constructor based dependency injection.
#Service
public class Searcher extends Thread implements ISearcher {
protected final ISessionProvider sessionProvider;
#Autowired
public Searcher(ISessionProvider sessionProvider) {
this.sessionProvider=sessionProvider;
sessionProvider.doSomeStuff();
}
}
I didn't do test, but I think the problem is, in class Searcher, you created a no-argument constructor, and there you used the "autowired" bean. I guess there you would get NPE. Since spring would instantiate your Searcher using default constructor (by reflection) if you didn't specify one, that is, it will use the no-argument constructor you created, but at this moment, the "autowired" bean was not yet injected.
If you want to do something immediately after the bean was instantiated, you can wrap the logic code in a method, and annotate it with #PostConstruct.
I am also getting the same issue but i cannot use #PostConstruct because i am not using doinit. I have a simple method which is getting the language from lang service.
My lang service is null here but in another controller it is not null, it is getting autowired.
#Service
#Configurable(preConstruction = true, autowire = Autowire.BY_NAME)
public class FormSelectLanguageHandler extends SimpleTagSupport {
#Autowired
private LangService langService;
public List<Lang> getLanguages(){
Sessions session = (Sessions) getJspContext().findAttribute("SHLSESSION");
List<Lang> languages=new ArrayList<Lang>();
//List<Lang> allLanguages = langService.findAllLangs();
List<Lang> all =new ArrayList<Lang>();
if (isRenderLangLabel()) {
all = langService.findByDisplayOrderAsc();
} else {
all = langService.findByNameOrderByNameAsc();
}
Long companyId = (null == session || null == session.getCompany())? null:session.getCompany().getId();
}
}