lo resolví con un BeanFactory personalizado. Si a alguien se le ocurre una solución mejor, me gustaría escucharla. De todos modos, aquí está la fábrica de beans:
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
public class CustomBeanFactory extends DefaultListableBeanFactory {
public CustomBeanFactory() {
}
public CustomBeanFactory(DefaultListableBeanFactory delegate) {
super(delegate);
}
@Override
public Object resolveDependency(DependencyDescriptor descriptor,
String beanName, Set<String> autowiredBeanNames,
TypeConverter typeConverter) throws BeansException {
//Assign Logger parameters if required
if (descriptor.isRequired()
&& Logger.class.isAssignableFrom(descriptor
.getMethodParameter().getParameterType())) {
return LoggerFactory.getLogger(descriptor.getMethodParameter()
.getDeclaringClass());
} else {
return super.resolveDependency(descriptor, beanName,
autowiredBeanNames, typeConverter);
}
}
}
Ejemplo de uso con una configuración XML:
CustomBeanFactory customBeanFactory = new CustomBeanFactory();
GenericApplicationContext ctx = new GenericApplicationContext(customBeanFactory);
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.loadBeanDefinitions(new ClassPathResource("beans.xml"));
ctx.refresh();
EDIT:
A continuación puede encontrar Arend v Reinersdorffs versión mejorada (ver los comentarios para una. explicación).
import java.lang.reflect.Field;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.MethodParameter;
public class CustomBeanFactory extends DefaultListableBeanFactory {
public CustomBeanFactory() {
}
public CustomBeanFactory(DefaultListableBeanFactory delegate) {
super(delegate);
}
@Override
public Object resolveDependency(DependencyDescriptor descriptor,
String beanName, Set<String> autowiredBeanNames,
TypeConverter typeConverter) throws BeansException {
//Assign Logger parameters if required
if (Logger.class == descriptor.getDependencyType()) {
return LoggerFactory.getLogger(getDeclaringClass(descriptor));
} else {
return super.resolveDependency(descriptor, beanName,
autowiredBeanNames, typeConverter);
}
}
private Class<?> getDeclaringClass(DependencyDescriptor descriptor) {
MethodParameter methodParameter = descriptor.getMethodParameter();
if (methodParameter != null) {
return methodParameter.getDeclaringClass();
}
Field field = descriptor.getField();
if (field != null) {
return field.getDeclaringClass();
}
throw new AssertionError("Injection must be into a method parameter or field.");
}
}
¿Realmente vale la pena, solo para evitar decir '= LoggerFactory.getLogger()'? – skaffman
No me gusta el enlace estático a LoggerFactory, por las mismas razones que describe Martin Fowler en http://martinfowler.com/articles/injection.html. Una LoggerFactory inyectada es una solución aceptable (siguiendo el patrón de localizador de servicio), pero un poco detallada. Supongo que se podría argumentar que la inyección de Log necesita usar un localizador de servicio, ya que una dependencia pura debe ser un objetivo independiente. Pero la solución del localizador es prolija, otros marcos lo soportan y espero que Spring pueda proporcionar algún tipo de información sobre el objetivo. Me pregunto si esto realmente no es posible. –
Es decir, esta información se pasa a BeanPostProcessors: http://www.tzavellas.com/techblog/2007/03/31/implementing-seam-style-logger-injection-with-spring/. ¿No se puede lograr lo mismo con la inyección del constructor? –