Rozwiązałem go za pomocą niestandardowego BeanFactory. Jeśli ktoś wymyśli lepsze rozwiązanie, byłbym szczęśliwy, słysząc to. Tak czy inaczej, oto fabryka fasola:
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);
}
}
}
Przykład użycia z config XML:
CustomBeanFactory customBeanFactory = new CustomBeanFactory();
GenericApplicationContext ctx = new GenericApplicationContext(customBeanFactory);
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.loadBeanDefinitions(new ClassPathResource("beans.xml"));
ctx.refresh();
EDIT:
Poniżej można znaleźć Arend v Reinersdorffs poprawiona wersja (patrz komentarze do An. wyjaśnienie).
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.");
}
}
Czy to naprawdę jest tego warte, aby uniknąć mówienia '= LoggerFactory.getLogger()'? – skaffman
Nie podoba mi się statyczne powiązanie z LoggerFactory, z tych samych powodów, które Martin Fowler opisuje w http://martinfowler.com/articles/injection.html. Wstrzykiwany LoggerFactory jest akceptowalnym rozwiązaniem (zgodnie z wzorcem lokalizatora usług), ale nieco bardziej gadatliwy. Przypuszczam, że można się spierać, że wtrysk dziennika musi korzystać z lokalizatora usług, ponieważ czysta zależność powinna być agnostyczna. Ale rozwiązanie lokalizatora jest pełne, inne wspierają go i spodziewam się, że Spring będzie w stanie podać jakieś informacje na temat celu. Zastanawiam się, czy to naprawdę nie jest możliwe. –
Mam na myśli, że ta informacja jest przekazywana do BeanPostProcessors: http://www.tzavellas.com/techblog/2007/03/31/implementing-seam-style-logger-injection-with-spring/. Czy tego samego nie można zrobić dla wtrysku konstruktora? –