Spring构造函数注入SLF4J logger - 如何获取注入目标类?

Ale*_*ing 15 java spring slf4j constructor-injection

我正在尝试使用Spring将SLF4J记录器注入类中,如下所示:

@Component
public class Example {

  private final Logger logger;

  @Autowired
  public Example(final Logger logger) {
    this.logger = logger;
  }
}
Run Code Online (Sandbox Code Playgroud)

我找到了这个FactoryBean课程,我已经实现了.但问题是我无法获得有关注射目标的任何信息:

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? */);
    }
}   
Run Code Online (Sandbox Code Playgroud)

FactoryBean甚至是正确的方法吗?当使用picocontainers 工厂注入时,你会得到Type传入的目标.在guice中它有点棘手.但是你如何在Spring中实现这一目标?

wax*_*wax 20

这是您的解决方案的替代方案.您可以使用BeanFactoryPostProcessor实现来实现您的目标.

假设你想要一个带日志的类.这里是:

  package log;
  import org.apache.log4j.Logger;

  @Loggable
  public class MyBean {

     private Logger logger;
  }
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,这个类什么都不做,只是为了简单而创建了一个logger容器.这里唯一值得注意的是@Loggable注释.这是它的源代码:

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 {
}
Run Code Online (Sandbox Code Playgroud)

此注释仅是进一步处理的标记.这是一个最有趣的部分:

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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它搜索所有bean,如果bean被标记为@Loggable,它会使用名称logger初始化其私有字段.您可以更进一步,并在@Loggable注释中传递一些参数.例如,它可以是与记录器对应的字段的名称.

我在这个例子中使用了Log4j,但我想它应该与slf4j完全相同.


Ale*_*ing 10

我用自定义BeanFactory解决了它.如果有人想出更好的解决方案,我会很高兴听到它.无论如何,这是豆工厂:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

XML配置的示例用法:

        CustomBeanFactory customBeanFactory = new CustomBeanFactory();      
        GenericApplicationContext ctx = new GenericApplicationContext(customBeanFactory);
        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
        xmlReader.loadBeanDefinitions(new ClassPathResource("beans.xml"));
        ctx.refresh();
Run Code Online (Sandbox Code Playgroud)

编辑:

您可以在下面找到Arend v.Reinersdorffs的改进版本(请参阅注释以获得解释).

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.");
    }
}
Run Code Online (Sandbox Code Playgroud)


mag*_*ter 6

为了使你的代码更容易Spring知道使用InjectionPoint定义记录器,即:

@Bean
@Scope("prototype")
public Logger logger(InjectionPoint ip) {
    return Logger.getLogger(ip.getMember().getDeclaringClass());
}
Run Code Online (Sandbox Code Playgroud)

@Scope("prototype") 这里需要在每次调用方法时创建'logger'bean实例.