如何在Spring Boot 2中检索应用程序上下文

时间:2019-06-28 16:03:41

标签: java spring-boot applicationcontext

我在ApplicationContextProvider(运行应用程序的入口点)中定义了这个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包,其中包含两个类(Greeting只是一个用于保存数据的类):

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类是:

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。

1 个答案:

答案 0 :(得分:0)

tl; dr:您不需要上下文;有更好的方法。

ApplicationContextAware是Spring的许多早期版本中的产物,在许多现在标准的功能可用之前。在现代Spring中,如果您需要ApplicationContext,只需像注入其他任何bean一样注入它即可。但是,几乎可以肯定,您不应该直接与它进行交互,尤其是对于getBean,应该用注入您得到的任何东西代替它。

通常,当您需要Spring bean时,应将其声明为构造函数参数。 (如果有多个构造函数,则需要用@Autowired进行注释,但是如果只有一个构造函数,Spring足够聪明地知道可以使用它。)如果您使用的是Lombok,则可以使用{{ 1}}自动编写构造函数,并且Groovy和Kotlin具有类似的功能。

在这里显示的Micrometer的特定情况下,将单个度量标准声明为bean是不常规的,因为它们是旨在应用于特定代码路径的细粒度工具。 (某些服务可能具有10个单独的指标来跟踪各种可能的情况。)相反,您注入@Value并选择作为构造函数一部分所需的计数器或其他指标。在这里,您的控制器类应如下所示。 (我已经删除了重复的MeterRegistry,但是如果有特殊原因,您可以按照显示的方式将其重新添加。)

AtomicLong