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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With