2

i have a controller for my http url with autowired event for my database (everything work fine)

@RestController public class CalculateDistance {

@Autowired MyDatabase mydb

some code

@GetMapping(value = "/url")
public Strng get() {
    return mydb.fetch("my query");
}

now i have the same autowired but its not working, i get null instead of my object

 @Component public class PrometheusMonitor {

     @Autowired MyDatabase mydb

     public PrometheusMonitor(MeterRegistry registry) {
         meterRegistry = registry;

         mydb =  null ...

i get an exception becuase mydb = null

but it works for my http controller

yenk
  • 219
  • 2
  • 12
  • Do you create a PrometheusMonitor with `new`? If so, then it's a duplicate of https://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null – JB Nizet Nov 27 '19 at 17:47
  • no since springboot does it, i followed this https://blog.autsoft.hu/defining-custom-metrics-in-a-spring-boot-application-using-micrometer/ – yenk Nov 27 '19 at 17:49
  • Ah. OK. I see it now. You can't expect Spring to have autowired the field of an object if the object doesn't exist yet. And since you're trying to access `mydb` in the constructor, the object isn't constructed yet. Stop using field injection. Everything would be much clearer if you posted actual code, instead of pseudo-code. – JB Nizet Nov 27 '19 at 17:54
  • @JBNizet so what should I do? – yenk Nov 27 '19 at 17:55
  • Stop using field injection. Use constructor injection. Inject the mydb using the constructor, just as you're doing it to inject registry. – JB Nizet Nov 27 '19 at 17:56
  • @JBNizet sorry, I am not an expert, can you give an example, im no familar with consutrct injector – yenk Nov 27 '19 at 17:57
  • Remove the Autowired annotation on the field. Add an argument of type MyDatabase to your constructor, just like you're already doing it with MeterRegistry. It's just a constructor. You construct an object by passing what it needs as constructor arguments. That's constructor injection. – JB Nizet Nov 27 '19 at 17:59
  • thanks ill try that – yenk Nov 27 '19 at 18:01

2 Answers2

1

To put into works what @JB said,

Constructor Injection will:

  • Supports immutability
  • State Safe. The object is instantiated to a full state or is not instantiated at all.
  • Easy to tests and mocks object injections
  • Constructors are more suitable for mandatory dependencies

IntelliJ IDEA support:

Intellij support

So for your example, you need to pass it in the constructor like this:

 @Component public class PrometheusMonitor {

 @Autowired
 public PrometheusMonitor(MeterRegistry registry, MyDatabase mydb) {
     meterRegistry = registry;

     assertNotNull(mydb);

     // rest of code

Read more about this:

https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/

Book Of Zeus
  • 49,509
  • 18
  • 174
  • 171
0

First make sure you dont doing the following:

PrometheusMonitor monitor = new PrometheusMonitor(registry);

that will not property autowire your database and would give you errors if you try to autowire the PrometheusMonitor unless you have a bean created for MeterRegistry

Here are things you can do

1) Have a no-argument constructor for PrometheusMonitor

 @Component public class PrometheusMonitor {

   @Autowired MyDatabase mydb

   public PrometheusMonitor() {}

   public void initializeMonitor(MeterRegistry registry) {
     meterRegistry = registry;
   }
}

Then in your class you can do something like:

@Service class MyService {
  @Autowire
  private PrometheusMonitor monitor;

  @PostConstruct
  privte void init() {
    MeterRegistry registry = getRegistry();
    monitor.initializeMonitor(registry);
  }
}

2) Create a MeterRegistry Bean for your argumented constructor

@Bean
public MeterRegistry registry() {
   return getRegistry();
}

Then during creation of your PrometheusMonitor it will autowire the registry for the argument when you autowire the mintor

locus2k
  • 2,802
  • 1
  • 14
  • 21