Git Product home page Git Product logo

Comments (2)

hoangdt84 avatar hoangdt84 commented on May 31, 2024 1

@rbygrave thanks a lot for your explanation. It makes perfect sense to me.

from avaje-config.

rbygrave avatar rbygrave commented on May 31, 2024

Could we do this - yes, the question is should we do this.

So for example:

@Singleton
public class EngineImpl implements Engine {

    @Value("${my.engine.cylinders:6}")  
    protected int cylinders;
    ...
}

Could be done today as:

import io.avaje.config.Config;

@Singleton
public class EngineImpl implements Engine {

    protected int cylinders = Config.getInt("my.engine.cylinders", 6);

... and in fact we can make that a final field:

    protected final int cylinders = Config.getInt("my.engine.cylinders", 6);

There is some background design thinking around this so we need to outline that here.


Background

Both Spring and Micronaut have a @Value and by implication they have chosen to combine "external configuration" in with "dependency injection". With avaje-inject we could also do that but we (mostly I) have desired to keep these 2 things separate for what I believe are fairly good reasons. So lets look at those reasons.

We could for implement this via source code generation with avaje-inject as:

public class EngineImpl$Proxy extends EngineImpl {

  public EngineImpl$Proxy() { // match super constructor, 
    super();
    this.cylinders = Config.getInt("my.engine.cylinders", 6);
  }
  
}

1. Timing of getting the configuration

We can see that cylinders is only set after the super(). Any code that tries to use cylinders before that would get a 0 (or null with Integer etc). This is relatively obvious to experienced devs but it is a source of bugs for less experienced devs. That is, if we don't use @Value and instead use Config.getInt() the field is initialised just like any normal field. That is, @Value fields have delayed initialisation and this can trip people up / be a source of bugs.

2. Dynamic configuration

If we go from needing the configuration read and set ONCE AT STARTUP to being read each time and potentially changing (aka dynamic configuration). Then we either need to change away from using @Value OR use the "Refreshable scope" concept.

I'd argue that when using Config.getInt() we can just use it anywhere - field, final field, static final field, in a method (dynamic configuration). There isn't a big shift between static configuration and dynamic configuration.

2.B "Refreshable scope"

As a consequence of "dynamic configuration", both Spring and Micronaut have the concept of "refreshable beans" / "refreshable context". With avaje-inject we explicitly do NOT have that concept as we'd get no value from "re-wiring the graph". Spring and Micronaut somewhat need it to re-read the @Value configuration - I'd argue that is because they have combined "external configuration" with "dependency injection wiring" ... so any dynamic configuration need with @Value means the bean needs to be refreshed.

That is, avaje-inject doesn't need "refreshable beans" because we expect "external dynamic configuration" to be done independently (for example, by using avaje-config). Instead, avaje-inject creates effectively immutable BeanScope.

3. avaje-config

I really like avaje-config. It's simple, extendable, and pretty mature. It was originally part of ebean orm and I extracted it into it's own project. If we supported @Value in avaje-inject then it would have to pick a "configuration implementation" and that would be avaje-config. However, currently avaje-inject users are free to do whatever they like here with external configuration and I know that some of them choose other configuration libraries.


Q: So could we support @Value?

A: Yes we could, it means that we need a $Proxy (just like we do to support AOP) and the generated code would pretty much be:

public class EngineImpl$Proxy extends EngineImpl {

  public EngineImpl$Proxy() { // match constructor of EngineImpl 
    super();
    this.cylinders = Config.getInt("my.engine.cylinders", 6);
  }
  
}

Q: So should we do this ?

Well, we could ... and then ideally document it in such a way that people think about their choices.

One of the interesting things about the code generation approach is that adding something like this can often result in ZERO added complexity to the runtime part of the library .. and instead just extra complexity is added to the code generator. I suspect that is the case here.

from avaje-config.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.