如何在 Spring Boot 2 中检索应用程序上下文
How to retrieve the Application Context in Spring Boot 2
我将此 ApplicationContextProvider
class 与 MyApplication.java
(应用程序 运行 的入口点)一起定义:
package com.company.my.app;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
让包 restapi
包含两个 class(Greeting
只是一个 class 来保存数据):
package com.company.my.app.restapi;
import com.company.my.app.ApplicationContextProvider;
import io.micrometer.core.instrument.Counter;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final Logger LOG = LoggerFactory.getLogger(GreetingController.class);
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
ApplicationContextProvider acp = new ApplicationContextProvider();
ApplicationContext context = acp.getContext();
if (context == null) LOG.info("app context is NULL");
Counter bean = context.getBean(Counter.class);
bean.increment();
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
最后 MyApplication
class 是:
package com.company.my.app;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.MeterBinder;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Counter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@SpringBootApplication
public class MyApplication {
@Bean
public MeterBinder exampleMeterBinder() {
return (meterRegistry) -> Counter.builder("my.counter")
.description("my simple counter")
.register(meterRegistry);
}
@Configuration
public class CounterConfig {
@Bean
public Counter simpleCounter(MeterRegistry registry) {
return registry.counter("my.counter");
}
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
当我 运行 应用程序并在我的浏览器中调用 http://localhost:8081/greeting
时,它在打印 app context is NULL
时崩溃了。如何获取应用程序上下文?我需要它来检索简单的计数器 bean。
tl;dr:你不需要上下文;有更好的方法。
ApplicationContextAware
是 Spring 旧版本的产物,在许多 now-standard 功能可用之前。在现代 Spring 中,如果您需要 ApplicationContext
,只需像任何其他 bean 一样注入它。但是,您几乎可以肯定不应该直接与它交互,尤其是对于 getBean
,应该将其替换为注入您得到的任何东西。
一般来说,当你需要一个Spring bean时,你应该将它声明为构造函数参数。 (如果你有多个构造函数,你需要用 @Autowired
注释一个,但如果只有一个构造函数,Spring 足够聪明,知道如何使用它。)如果你使用 Lombok,你可以使用@Value
自动编写构造函数,Groovy和Kotlin有类似的特性。
在您在这里展示的 Micrometer 的特定情况下,将单个指标声明为 bean 是不常见的,因为它们是 fine-grained 旨在应用于特定代码路径的工具。 (某些服务可能有 10 个单独的指标来跟踪各种可能的情况。)相反,您将 MeterRegistry
和 select 作为构造函数的一部分所需的计数器或其他指标注入。在这里,您的控制器 class 应该如下所示。 (我已经删除了重复项 AtomicLong
,但如果您有特殊原因需要它,您可以按照您展示的那样将其添加回去。)
@RestController
public class GreetingController {
private static final Logger LOG = LoggerFactory.getLogger(GreetingController.class);
private static final String template = "Hello, %s!";
private final Counter counter;
public GreetingController(MeterRegistry meterRegistry) {
counter = meterRegistry.counter("my.counter");
}
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
counter.increment();
long count = (long) counter.count();
return new Greeting(count, String.format(template, name));
}
}
我将此 ApplicationContextProvider
class 与 MyApplication.java
(应用程序 运行 的入口点)一起定义:
package com.company.my.app;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
让包 restapi
包含两个 class(Greeting
只是一个 class 来保存数据):
package com.company.my.app.restapi;
import com.company.my.app.ApplicationContextProvider;
import io.micrometer.core.instrument.Counter;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final Logger LOG = LoggerFactory.getLogger(GreetingController.class);
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
ApplicationContextProvider acp = new ApplicationContextProvider();
ApplicationContext context = acp.getContext();
if (context == null) LOG.info("app context is NULL");
Counter bean = context.getBean(Counter.class);
bean.increment();
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
最后 MyApplication
class 是:
package com.company.my.app;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.MeterBinder;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Counter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@SpringBootApplication
public class MyApplication {
@Bean
public MeterBinder exampleMeterBinder() {
return (meterRegistry) -> Counter.builder("my.counter")
.description("my simple counter")
.register(meterRegistry);
}
@Configuration
public class CounterConfig {
@Bean
public Counter simpleCounter(MeterRegistry registry) {
return registry.counter("my.counter");
}
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
当我 运行 应用程序并在我的浏览器中调用 http://localhost:8081/greeting
时,它在打印 app context is NULL
时崩溃了。如何获取应用程序上下文?我需要它来检索简单的计数器 bean。
tl;dr:你不需要上下文;有更好的方法。
ApplicationContextAware
是 Spring 旧版本的产物,在许多 now-standard 功能可用之前。在现代 Spring 中,如果您需要 ApplicationContext
,只需像任何其他 bean 一样注入它。但是,您几乎可以肯定不应该直接与它交互,尤其是对于 getBean
,应该将其替换为注入您得到的任何东西。
一般来说,当你需要一个Spring bean时,你应该将它声明为构造函数参数。 (如果你有多个构造函数,你需要用 @Autowired
注释一个,但如果只有一个构造函数,Spring 足够聪明,知道如何使用它。)如果你使用 Lombok,你可以使用@Value
自动编写构造函数,Groovy和Kotlin有类似的特性。
在您在这里展示的 Micrometer 的特定情况下,将单个指标声明为 bean 是不常见的,因为它们是 fine-grained 旨在应用于特定代码路径的工具。 (某些服务可能有 10 个单独的指标来跟踪各种可能的情况。)相反,您将 MeterRegistry
和 select 作为构造函数的一部分所需的计数器或其他指标注入。在这里,您的控制器 class 应该如下所示。 (我已经删除了重复项 AtomicLong
,但如果您有特殊原因需要它,您可以按照您展示的那样将其添加回去。)
@RestController
public class GreetingController {
private static final Logger LOG = LoggerFactory.getLogger(GreetingController.class);
private static final String template = "Hello, %s!";
private final Counter counter;
public GreetingController(MeterRegistry meterRegistry) {
counter = meterRegistry.counter("my.counter");
}
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
counter.increment();
long count = (long) counter.count();
return new Greeting(count, String.format(template, name));
}
}