如何在Spring中无需显式配置即可注入Log4j Logger
解决Spring XML中重复注入Log4j Logger的问题
我太懂你被一堆重复的Logger构造注入配置烦到的感觉了,下面给你几个实用方案,从简单改造到全自动注入,彻底摆脱重复代码:
方案1:自定义FactoryBean + 抽象父Bean(快速落地)
先写一个专门生成Logger的FactoryBean,它会根据传入的目标类创建对应Logger:
import org.apache.log4j.Logger; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; public class LoggerFactoryBean implements FactoryBean<Logger>, InitializingBean { private Class<?> targetClass; private Logger logger; public void setTargetClass(Class<?> targetClass) { this.targetClass = targetClass; } @Override public Logger getObject() { return logger; } @Override public Class<?> getObjectType() { return Logger.class; } @Override public boolean isSingleton() { return true; } @Override public void afterPropertiesSet() { if (targetClass == null) { throw new IllegalArgumentException("必须设置targetClass属性"); } this.logger = Logger.getLogger(targetClass); } }
接着在Spring XML里定义一个抽象父Bean,把Logger注入的逻辑抽离出来:
<bean id="loggerAwareBean" abstract="true"> <constructor-arg> <bean class="com.myProject.LoggerFactoryBean"> <property name="targetClass" value="#{this.class}"/> </bean> </constructor-arg> </bean>
之后所有需要Logger的业务Bean,只需要继承这个父Bean就行,不用再重复写Logger的配置:
<bean id="myClass" class="com.myProject.MyClass" parent="loggerAwareBean"> <!-- 这里只需要写其他构造参数或属性配置 --> </bean>
这个方案改动小,不需要复杂的Spring扩展,适合快速解决问题。
方案2:BeanDefinitionRegistryPostProcessor(全自动注入,彻底解放双手)
如果想完全去掉所有Logger相关的显式配置,这个方案最适合——它会自动识别所有带Logger构造参数的Bean,自动注入对应Logger实例。
先写一个处理器类:
import org.apache.log4j.Logger; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.AbstractBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; public class LoggerAutoInjectProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // 遍历所有已注册的Bean定义 String[] beanNames = registry.getBeanDefinitionNames(); for (String beanName : beanNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (!(beanDef instanceof AbstractBeanDefinition)) { continue; } AbstractBeanDefinition abstractBeanDef = (AbstractBeanDefinition) beanDef; // 加载Bean对应的Class,检查是否有接受Logger的构造方法 Class<?> beanClass; try { beanClass = Class.forName(abstractBeanDef.getBeanClassName()); } catch (ClassNotFoundException e) { continue; // 跳过无法加载的类 } boolean needsLogger = false; for (var constructor : beanClass.getConstructors()) { for (var paramType : constructor.getParameterTypes()) { if (paramType == Logger.class) { needsLogger = true; break; } } if (needsLogger) break; } // 如果需要Logger,自动添加构造参数 if (needsLogger) { BeanDefinition loggerBeanDef = BeanDefinitionBuilder .rootBeanDefinition(Logger.class) .setFactoryMethod("getLogger") .addConstructorArgValue(beanClass.getName()) .getBeanDefinition(); abstractBeanDef.getConstructorArgumentValues().addGenericArgumentValue(loggerBeanDef); } } } @Override public void postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory beanFactory) throws BeansException { // 无需额外处理 } }
然后在Spring XML里只需要注册这个处理器:
<bean class="com.myProject.LoggerAutoInjectProcessor"/>
现在所有带Logger构造参数的Bean,Spring都会自动注入对应的Logger,完全不用写任何Logger相关配置!
方案3:注解配合简化(适合已用注解的项目)
如果能接受在业务类里加少量注解,也可以用@Autowired配合自定义Logger Bean:
- 在Spring XML里注册一个原型作用域的Logger工厂:
<bean id="logger" class="org.apache.log4j.Logger" factory-method="getLogger" scope="prototype"> <constructor-arg value="#{targetClass}"/> </bean>
- 开启Spring注解支持:
<context:annotation-config/>
- 在业务类的构造方法上添加
@Autowired:
public class MyClass { private final Logger logger; @Autowired public MyClass(Logger logger) { this.logger = logger; } }
这个方案适合已经在项目中使用注解的场景,配置更简洁。
内容的提问来源于stack exchange,提问作者stop-cran
相关产品推荐
相关产品推荐

