I'm trying to use Spring to inject a SLF4J logger into a class like so:
@Component
public class Example {
private final Logger logger;
@Autowired
public Example(final Logger logger) {
this.logger = logger;
}
}
I've found the FactoryBean
class, which I've implemented. But the problem is that I cannot get any information about the injection target:
public class LoggingFactoryBean implements FactoryBean<Logger> {
@Override
public Class<?> getObjectType() {
return Logger.class;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public Logger getObject() throws Exception {
return LoggerFactory.getLogger(/* how do I get a hold of the target class (Example.class) here? */);
}
}
Is FactoryBean even the right way to go? When using picocontainers factory injection, you get the Type
of the target passed in. In guice it is a bit trickier. But how do you accomplish this in Spring?
Here is an alternative to your solution. You could achieve your goal with BeanFactoryPostProcessor implementation.
Let's assume you want to have a class with logging. Here it is:
package log;
import org.apache.log4j.Logger;
@Loggable
public class MyBean {
private Logger logger;
}
As you could see this class does nothing and created just to be a logger container for simplicity. The only remarkable thing here is @Loggable annotation. Here its source code:
package log;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Loggable {
}
This annotation is only a marker for further processing. And here is a most interesting part:
package log;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import java.lang.reflect.Field;
public class LoggerBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] names = beanFactory.getBeanDefinitionNames();
for(String name : names){
Object bean = beanFactory.getBean(name);
if(bean.getClass().isAnnotationPresent(Loggable.class)){
try {
Field field = bean.getClass().getDeclaredField("logger");
field.setAccessible(true);
field.set(bean, Logger.getLogger(bean.getClass()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
It searches through all beans, and if a bean is marked as @Loggable, it initialize its private field with name logger. You could go even further and pass some parameters in @Loggable annotation. For example, it could be a name of field corresponding to logger.
I used Log4j in this example, but I guess it should work exactly the same way with slf4j.