4

I found a lot informations about the usage of @Autowired with a non-empty constructor here. But I'm even not able to resolve my current problem.

I have an interface for logging. I'm logging every entry in the database. For this reason for me it is important to know the class name, which class uses the logger.

My first idea: Create a new implementation of the Logger, which has a non-empty constructor. So, while creating a new instance of this logger, you have to set a name.

My Interface looks like this:

public interface Logger{
  public void log(String name, String msg);
}

The implementation of that looks like this:

@Service
public class LoggerImpl implements Logger{
 private String className;

 LoggerImpl(String className){ this.className = className }

 @Override
 public void log(String name, String msg) { // print and save in the database }
}

And everywhere of my application I want to use this logger - Interface. For example in some business classes I'm using the Logger in this way:

@Service
public class ServiceName {
 private Logger logger;

 @Autowired
 public ServiceName(){
   logger = new LoggerImpl("ServiceName");
 }

 public void someMethod(){
    logger.log("name", "this is a log!");
 }
}

But my application tells me, that he has no database-communication and throw a NullPointerException, while saving the log in the database. Before creating a non-empty constructor this communication worked. So my guess is, while I creating this LoggerImpl manually, this object is not in the spring application context any more. Any ideas to resolve? Thank you. Edit: I'm working with Spring Boot and I do not have any XML-files.

1
  • You are instantiating your logger outside spring (logger = new LoggerImpl("ServiceName");). Why? Commented Nov 4, 2016 at 14:06

2 Answers 2

5

I think that in this case annotating with @Service is not enough. Spring will not be able to create instance of LoggerImpl.

You must tell Spring how to create instance. When using XML configuration you should do

<bean id="loggerForX" class="LoggerImpl ">
  <constructor-arg index="0" value="X" />
</bean>

When using javaconfig

@Configuration
public class LoggerConfiguration {

  @Bean
  public Logger loggerForX() {
    return new LoggerImpl("X");
  }

}

Then you will be able to @Autowire Logger as

@Autowire  Logger loggerForX;

If this does not work, try autowiring loggerForX variable ab

@Autowire @Qualifier("loggerForX") Logger loggerForX;

With this approach you have to create separate bean for every class that wants logger. It may be better to create LoggerFactory as bean, and inject that LoggerFactory. LoggerFactory must have method to create Logger.

@Service
class LoggerFactory  {

  public Logger loggerFor(String name) {
    return new LoggerImpl(name);
    }

}

LoggerFactory has default constructor and can easily be @Autowired everywhere you want.

Sign up to request clarification or add additional context in comments.

7 Comments

First I want to thank you for investing time to answer. The usage of @Configuration will force me to do for every class a new Bean. In my current situation, I have more than 15 classes. So I would create as less 15 Beans manually. What do you mean with @Qualifier? Can you give me an example or an link for that?
See my updated edit (LoggerFactory). I agree that creating separate Bean every time makes no sense in your situation. For Qualifier, also see edit to post.
Just two more comments. There are many loging frameworks around. Do you really need your own? Also, Logger usually has no dependencies and cant be created directly from constructor or static factory method. In that case it is not managed by Spring. No big deal, because usually there is no need to mock Logger. So dependency on static factory is perfectly fine.
Sounds well. But not resolve the problem. When have a closer look. Then it is clear, why it do not work. I just created a workaround with the same result. In the new LoggerFactory I'm also creating a new instance, even not in the application context.
Yes, there are lots of Logging API's and they are pretty well. But I need to do my own. :-( I loved the slf4j logger. But in my case, I can't use it.
|
5

You may want to have a look at the new InjectionPoint

@Configuration
public class LoggerConfig {

  @Bean
  @Scope("prototype")
  public Logger logger(InjectionPoint injectionPoint) {
    //this is the class where the logger will be used     
    Class<?> theClassYouAreLookingFor = injectionPoint.getMember().getDeclaringClass();
    return findOrCreateYourLogger(theClassYouAreLookingFor);
  }
}

To use the Logger just inject it as you would normally do with any other Bean.

2 Comments

This is not clear for me. When I'm use the @Autowired to the Logger, where I set the DeclaredType of IP? I get just the name of the Interface Logger. That is not what I want to know.
@MissBonbon you are right, sorry. I used the wrong method of InjectionPoint. With injectionPoint.getMember().getDeclaringClass() you should now get the class that is getting the Logger injected (edited the answer)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.