如何将记录器注入示例 Spring Boot 应用程序的字段中?

Posted

技术标签:

【中文标题】如何将记录器注入示例 Spring Boot 应用程序的字段中?【英文标题】:How do I inject a logger into a field in the sample spring boot application? 【发布时间】:2014-12-18 11:38:04 【问题描述】:

我想要的是让 spring autowire 成为一个记录器。所以,换句话说,我想让这个工作:

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MainController 

    @Autowired
    private Logger logger;
    
    @RequestMapping("/")
    public String enterSite(HttpServletResponse response) 
        logger.info("site entered");
        return "welcome";
    

现在它在启动时抛出一个异常:“没有为依赖找到类型 [org.slf4j.Logger] 的合格 bean...”。

我的 pom.xml 依赖项:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.0.M1</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.1-901.jdbc4</version>
        </dependency>
        <!-- <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> 
            </dependency> -->
    </dependencies>

我读到这个:http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-logging

它说如果您使用其中一个启动器 pom(我会)使用 Logback - 但用于内部日志记录。它可以在我的课程中自动装配吗?

【问题讨论】:

【参考方案1】:

可以让 Spring 自动装配一个 Logger 实例,但这将是一件非常不寻常的事情(您需要一个 Logger 类型的 bean 才能在您的应用程序上下文中)。更常用的方法是在声明它的位置初始化记录器,并使用将用于记录的类对其进行配置:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MainController 

    private final Logger logger = LoggerFactory.getLogger(MainController.class);


【讨论】:

不回答问题并鼓励在 DI 上下文中使用不良行为。【参考方案2】:

虽然这不是通常的方式,但您可以直接在上下文中添加记录器 bean 来重现经典绑定:

private final Logger logger = LoggerFactory.getLogger(MainController.class);

只需在 spring 上下文中插入:

<bean id="logger" scope="prototype" class="org.slf4j.LoggerFactory" factory-method="getLogger">
    <constructor-arg name="name" value="youLoggerName" />
</bean>

那么你可以简单地注入你的记录器:

@Autowired
private Logger logger;

【讨论】:

如果我们通过 bean 创建记录器,它将在执行器记录器端点中不可用。所以,我无法配置记录器运行时。有什么方法可以在执行器记录器端点中使用它?【参考方案3】:

如果这里的目标是减少代码,请尝试Project Lombok。然后,您甚至不需要声明记录器 - 只需添加注释并使用 log 而不是 logger

所以你上面的代码现在看起来像这样:

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
// import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class MainController 

    @RequestMapping("/")
    public String enterSite(HttpServletResponse response) 
        log.info("site entered");
        return "welcome";
    

【讨论】:

如果我们为此控制器编写测试用例,如何注入记录器对象? @InjectMock 似乎不起作用 这里没有注入机制。 Project Lombok 只是为您插入记录器启动代码,即:private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MainController.class); 我不确定您为什么要注入记录器。如果要禁用它或重新定向输出,则可以在记录器的配置文件中完成。您的测试配置将覆盖正常配置,因此不会与任何内容发生冲突。 这不是真正回答问题。该问题要求提供可注入的记录器解决方案,答案解释了如何使用 Lombok 注释使用静态记录器。 “这不是真正回答问题” [sic]。你是对的。因此,我在评论中的警告是:这里没有注入机制。以及我在解决方案之前的推测性假设声明:如果这里的目标是减少代码......我是试图猜测需求背后的动机并提供帮助。我同意它没有回答法律条文的问题。【参考方案4】:

Solution 使用@Bean

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class LoggerConfiguration 

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Logger logger(InjectionPoint injectionPoint)
        return LoggerFactory.getLogger(injectionPoint.getMethodParameter().getContainingClass());
    


之后,只需使用构造函数注入注入 Logger(字段注入将不起作用):

@Service
class SomeService 

    private Logger logger;

    public SomeService(Logger logger;) 
        this.logger = logger;
    

    public void someMethod() 
        logger.error("Some log");
      

【讨论】:

能否在代码示例中包含导入语句?这将为希望采用此解决方案的开发人员消除任何歧义。 @Hiru,再看一次。此策略仅适用于构造函数注入。我的最后一个答案是错误的表明它可以与现场注入一起使用。 请注意,InjectionPoint 可以通过字段注入或参数注入来构造,因此您应该进行 NULL 检查以确保返回记录器以供正确使用 有关处理方法参数注入和字段注入的示例,请参阅本文:medium.com/simars/…【参考方案5】:

这就是我如何让它为现场注入工作

LoggingConfiguration.java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

    @Configuration
    public class LoggingConfiguration 

        @Bean
        public Logger log() 
            var log = LoggerFactory.getLogger("com.bitsmonkey.dummy");
            return log;
        
    

现在在控制器内部

DummyController.java

@Controller
public class DummyController 

@Autowired
private Logger log;

//......

【讨论】:

当我尝试在我的班级中使用日志时,我收到一个错误 - 无法对非静态字段日志进行静态引用。

以上是关于如何将记录器注入示例 Spring Boot 应用程序的字段中?的主要内容,如果未能解决你的问题,请参考以下文章

将单例注入 Spring Boot 应用程序上下文

Spring-Boot 如何正确注入 javax.validation.Validator

多部分表单数据输入 Java 模型属性未在请求类中注入元素 - Spring Boot

记录Spring Boot大坑一个,在bean中如果有@Test单元测试,不会注入成功

如何将 application.yml 中的地图注入 Spring Boot 中的字段?

Spring boot - 如何获取 WARN 的框架日志记录和 DEBUG 的应用程序日志记录