Как получить контекст приложения в Spring Boot 2 - PullRequest
0 голосов
/ 28 июня 2019

У меня есть этот класс 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.Как мне получить контекст приложения?Мне нужно, чтобы получить простой встречный бин.

1 Ответ

0 голосов
/ 28 июня 2019

tl; dr: вам не нужен контекст; есть лучший способ.

ApplicationContextAware - это артефакт из более старых версий Spring, еще до того, как стали доступны многие стандартные функции. В современной Spring, если вам нужен ApplicationContext, просто введите его, как любой другой боб. Тем не менее, вы почти наверняка не должны взаимодействовать с ним напрямую, особенно для getBean, который следует заменить введением того, что вы получили.

В общем, когда вам нужен bean-компонент Spring, вы должны объявить его как параметр конструктора. (Если у вас есть несколько конструкторов, вам нужно аннотировать один с помощью @Autowired, но если есть только один конструктор, Spring достаточно умен, чтобы знать, как его использовать.) Если вы используете Lombok, вы можете использовать @Value для автоматически напишите конструктор, и Groovy и Kotlin имеют схожие функции.

В конкретном случае Micrometer, который вы здесь показываете, не принято объявлять отдельные метрики в виде bean-компонентов, поскольку они являются детализированными инструментами, предназначенными для применения к конкретным путям кода. (Некоторые службы могут иметь 10 отдельных метрик для отслеживания различных возможных сценариев.) Вместо этого вы вводите MeterRegistry и выбираете счетчики или другие метрики, которые вам нужны, как часть вашего конструктора. Здесь ваш класс контроллера должен выглядеть следующим образом. (Я удалил дубликат 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));
  }
}
...