9

I have a logger class that makes use of a service. Each time a new logger is created, I expect to have access to the singleton scoped logging service.

I autowire the logging service into the logger however, a null pointer exception is returned. I have tried a few solutions:

  1. manually defining the bean in the application context,
  2. Trying to get the logger to be spring managed but that just resulted in more issues.

I am trying to get this to work in my junit tests, and I do specify the context file to make use of a different application context. However even if kept identical it does not resolve the issue.

Please find code below:

The following is an excerpt from the application context.

<context:component-scan base-package="com.platform"/>
<bean id="asyncLoggingService" class="com.platform.services.AsyncLoggingServiceImplementation" scope="prototype"/>

The following is the Logger class.

package com.platform.utils;


import com.platform.services.AsyncLoggingService;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

public class OurLogger
{

  private static Logger logger;

  @Autowired
  private AsyncLoggingervice asyncLoggingService;

  public OurLogger(Class Clazz)
  {
    logger = LoggerFactory.getLogger(Clazz);
  }


  public void trace(TraceableObject object, String message)
  { 
    //Do nothing yet
  }

}

I then make use of the Logger in another service in order to log whats going on. (The reason I am writing another logger is to make use of an RabbitMQ server) In the service I instantiate a new instance of the Logger and then use it accordingly.

@Service
public class AsyncAccountServiceImplementation implements AsyncAccountService
{
  private static final String GATEWAY_IP_BLOCK = "1";

  private static OurLogger logger = new      OurLogger(AsyncAccountServiceImplementation.class);

...
}

The null pointer occurs in the OurLogger when I try to call any method on the asyncLoggingService.

I then am trying to test the AsyncAccountService using JUnit. I make sure I add the different application context but it still seems to result in the null pointer exception.

If you need further information please let me know. I have seen ways to fix this but they don't seem to work so perhaps I have made a mistake somewhere or I am not understanding this all quite correctly.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
robinjohnobrien
  • 1,092
  • 2
  • 11
  • 19
  • 1
    How should spring autowire an instance it doesn't know about. You are creating new instances yourself whereas you should use the one managed by spring. – M. Deinum Sep 23 '14 at 09:18
  • As far as I am aware (and please correct me if I am wrong as I probably am) the bean is specified in the application context and then used (with the same name) in the OurLogger instance. The only grey area I see there is the creation of the new OurLogger instance, but surely it must be ok to use an autowired service inside an instance of an object like this. Thanks for your comment thus far. – robinjohnobrien Sep 23 '14 at 09:22
  • 1
    No it isn't... That will only work for spring defined and managed beans. As soon as you do a `new OurLogger` nothing is going to happen. Unless you use AspectJ combined with `@Configurable`. – M. Deinum Sep 23 '14 at 09:23
  • But just out of curiosity why not simply create a appender for your logging framework? That way you can simply use SLF4J in your code and have a RabbitMQ based appender (Logback or Log4j) to do the actual logging. – M. Deinum Sep 23 '14 at 09:24
  • Ok thanks that does make sense. I will look deeper into configurable now. As for your appender suggestion. That makes great sense. It perhaps will simplify what I am doing at the moment. – robinjohnobrien Sep 23 '14 at 09:27
  • 1
    It will greatly simplify what you are doing. Spring AMQP provides a [implementation for Log4j](http://docs.spring.io/spring-amqp/docs/current/api/org/springframework/amqp/rabbit/log4j/AmqpAppender.html) (and I just registered [an issue](https://jira.spring.io/browse/AMQP-428) for a Logback one :) ). – M. Deinum Sep 23 '14 at 09:34
  • Definitly will look into that, however I still want to solve this problem even if just to learn something new so I dont make a mistake like this again. Thanks for the suggestions and help! – robinjohnobrien Sep 23 '14 at 09:40

5 Answers5

9

When you create an object by new, autowire\inject don't work...

See this link and this link for some workaround.

Anyway if you would inject a logger i suggest you this my answer to another topic.

Community
  • 1
  • 1
Xstian
  • 8,184
  • 10
  • 42
  • 72
  • Thanks, I believe your work around will work but as far as I am aware its considered bad practice to get the bean directly from the context in that way. http://stackoverflow.com/questions/812415/why-is-springs-applicationcontext-getbean-considered-bad – robinjohnobrien Sep 23 '14 at 09:32
  • Isn't a real bad practice, is only a different way to solve a problem :) but are different sides of the same thing :) Anyway have you considered the inject logger as i suggest you? – Xstian Sep 23 '14 at 09:40
4

Just want to add my 2 cents.

I once encountered the same issue when I was not quite used to the life in the IoC world. The @Autowired field of one of my beans is null at runtime.

The root cause is, instead of using the auto-created bean maintained by the Spring IoC container (whose @Autowired field is indeed properly injected), I am newing my own instance of that bean and using it. Of course this one's @Autowired field is null because Spring has no chance to inject it.

smwikipedia
  • 61,609
  • 92
  • 309
  • 482
  • I think if you create a new final instance of the class, this is the best solution for this kind of problem. – Arefe Apr 28 '21 at 01:56
2

If you are using AspectJ you can use @Configurable:

@Configurable
public class OurLogger {
  ..
}

See: Using AspectJ to dependency inject domain objects with Spring

micha
  • 47,774
  • 16
  • 73
  • 80
  • Thanks for the help, Just as M. Deinum suggested. I am going to look into this as it seems quite promising (and quite interesting) – robinjohnobrien Sep 23 '14 at 09:30
1

Within your application context you have to do reference your Logger :

<context:component-scan base-package="com.platform"/>
<bean id="asyncLoggingService" class="com.platform.services.AsyncLoggingServiceImplementation" scope="prototype"/>
<bean id="ourLogger" class="com.platform.utils.OurLogger"/>

Then you've to inject it into your service :

@Service
public class AsyncAccountServiceImplementation implements AsyncAccountService
{

 private static final String GATEWAY_IP_BLOCK = "1";

 @Autowired
 private OurLogger logger;

}
Joffrey Hernandez
  • 1,809
  • 3
  • 21
  • 39
Abderrazak BOUADMA
  • 1,526
  • 2
  • 16
  • 38
  • This would be my approach however, I want the logger to instantiate the slf4j logger with a class name. So I think the @Configrable way should allow me to follow much the same method as you describe here. – robinjohnobrien Sep 23 '14 at 09:38
1

Use spring framework Unit test instead of JUnit test to inject your spring bean.

May be that will help you.