@Value mit bestimmt Werten, sonst default

Diskutiere @Value mit bestimmt Werten, sonst default im Allgemeine Java-Themen Bereich.
S

Schuriko

Ist es möglich für @Value über SpEL einen bestimmt Wert aus einem property file zu ermitteln, und wenn kein erlaubter Wert ist, dann wird der default Wert verwendet.
 
S

Schuriko

Diese Seite habe ich mir auch schon angeschaut, bin allerdings noch nicht weitergekommen, wie man NUR bestimmte Werte zulässt und wenn es KEIN erlaubter Wert ist, dann soll der Default-Wert verwendet werden.
 
T

thecain

Achso, kein erlaubter... Das wird wohl nicht gehen.

Ich würde entweder im Code den Wert auf default setzen, wenn ungültig, oder direkt mit einer Exception abbrechen.
 
S

Schuriko

Ich habe es jetzt mal wie folgt realisiert:

Code:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;

import com.editor.helpers.generator;

@Configuration
@PropertySource("classpath:app.properties")
public class Config {

    @Value("${generator_name:null}")
    String generator_name;

    Generator generator = null;
   
    @Bean
    @Scope(value="singleton")
    public Generator getGenerator() {
        if ( generator == null ) {
            if ( generator_name == null ) {
                throw new IllegalArgumentException();
            }

            switch ( generator_name.toLowerCase() ) {
                case "a" : generator = new GeneratorA();
                                break;
                             
                case "b" : generator = new GeneratorB();
                               break;
            }
        }
       
        return generator;
    }

}
Oder wie würdet ihr es realisieren? Und wo sollte ich die InvalidArgumentException abfangen bzw. bearbeiten?
Ich sollte noch dazu erwähnen es wird eine Web-Anwendung, die Thymeleaf verwendet, ist.
 
J

JustNobody

Ich frage mich gerade nur, wieso Du nicht die GeneratorA und GeneratorB zu Beans machst um dann im Config File halt den Bean entsprechend zu definieren, also sowas wie
XML:
<beans>
    <bean id="generator" class="my.namespace.GeneratorA">
        <!-- maybe more options go in here -->
    </bean>
</beans>
Dann hättest Du ein Config File, in dem Du jederzeit einen anderen Generator angeben kannst. Und Du musst keinen Config Bean anpassen ... da gibt es dann keine Abhängigkeit zu den existierenden Generatoren.

Oder - so Du unbedingt das in der Art haben willst: Da es ja um eine einmalige Sache geht, könntest Du den kompletten Klassennamen hinterlegen und dann eine neue Instanz erzeugen. Das wäre dann evtl. sowas in der Art (Fehlerbehandlung müsste man noch einfügen):

Java:
    @Value("${generator_class:null}")
    String generatorClass;

    Generator generator = null;
  
    @Bean
//    @Scope(value="singleton") ==> Singleton ist Standard in Spring, daher ist diese Annotation unnötig.
    public Generator getGenerator() {
          if (generator == null) {
            Class c = Class.forName(generatorClass);
            generator = (Generator) c.newInstance();
          }
        return generator;
    }
Bei beiden Varianten hast Du keine Abhängigkeit von einer Klasse zu allen bekannten Generator Implementationen.

Und die Exception wirst du bei Dir und bei meiner letzteren Methode dann nur da abfangen können, wo getGenerator aufgerufen wird.

Bei der ersten Version von mir würde beim Start der Applikation der Bean nicht erzeugt werden können (sowie alle davon abhängigen Beans). Da muss man also beim App Server entsprechend ein Monitoring haben (oder eben bei Updates immer verifizieren
 
S

Schuriko

Ich frage mich gerade nur, wieso Du nicht die GeneratorA und GeneratorB zu Beans machst um dann im Config File halt den Bean entsprechend zu definieren, also sowas wie
XML:
<beans>
    <bean id="generator" class="my.namespace.GeneratorA">
        <!-- maybe more options go in here -->
    </bean>
</beans>
Dann hättest Du ein Config File, in dem Du jederzeit einen anderen Generator angeben kannst. Und Du musst keinen Config Bean anpassen ... da gibt es dann keine Abhängigkeit zu den existierenden Generatoren.

Oder - so Du unbedingt das in der Art haben willst: Da es ja um eine einmalige Sache geht, könntest Du den kompletten Klassennamen hinterlegen und dann eine neue Instanz erzeugen. Das wäre dann evtl. sowas in der Art (Fehlerbehandlung müsste man noch einfügen):

Java:
    @Value("${generator_class:null}")
    String generatorClass;

    Generator generator = null;
 
    @Bean
//    @Scope(value="singleton") ==> Singleton ist Standard in Spring, daher ist diese Annotation unnötig.
    public Generator getGenerator() {
          if (generator == null) {
            Class c = Class.forName(generatorClass);
            generator = (Generator) c.newInstance();
          }
        return generator;
    }
Bei beiden Varianten hast Du keine Abhängigkeit von einer Klasse zu allen bekannten Generator Implementationen.

Und die Exception wirst du bei Dir und bei meiner letzteren Methode dann nur da abfangen können, wo getGenerator aufgerufen wird.

Bei der ersten Version von mir würde beim Start der Applikation der Bean nicht erzeugt werden können (sowie alle davon abhängigen Beans). Da muss man also beim App Server entsprechend ein Monitoring haben (oder eben bei Updates immer verifizieren
Wie sollte man am besten die ClassNotFoundException behandeln für
Code:
Class c = Class.forName(generatorClass);
wenn z.B. eine nicht existende Klasse angibt?

Außerdem wird mir für
Code:
generator = (Generator) c.newInstance();
newInstance als deprecated angegeben.
 
T

thecain

Würde ich auch nicht so machen.

Ich denke für dich würde es sich anbieten via Spring Profiles die richtigen Beans zu laden. Mit newInstance sind sie mMn auch nicht sauber im Spring Context.
Siehe: https://www.baeldung.com/spring-profiles

/e xml configs würde ich in aktuellem Versionen auch nicht mehr unbedingt verwenden
 
J

JustNobody

Die Frage ist doch: kannst du da sinnvoll etwas machen? Wenn du Generator A und B hast und jemand gibt C an?

Ist das eine Art Eingabe die ständig kommt oder ist das einmalige Konfiguration?

Einmalige Konfiguration solltest du beim Start dem Admin um die Ohren hauen ... so wie es bei der Definition als Bean direkt passiert. Das ist halt etwas, das ordentlich und sorgfältig gemacht werden muss ...
 
S

Schuriko

Würde ich auch nicht so machen.

Ich denke für dich würde es sich anbieten via Spring Profiles die richtigen Beans zu laden. Mit newInstance sind sie mMn auch nicht sauber im Spring Context.
Siehe: https://www.baeldung.com/spring-profiles

/e xml configs würde ich in aktuellem Versionen auch nicht mehr unbedingt verwenden
Ich habe mir https://www.baeldung.com/spring-profiles. Diese bezieht sich allerdings darauf wann eine Bean verwendet werden soll. Deshalb verstehe ich gerad nicht so recht, wie du von meinem Problem hierauf kommst????
 
S

Schuriko

Die Frage ist doch: kannst du da sinnvoll etwas machen? Wenn du Generator A und B hast und jemand gibt C an?

Ist das eine Art Eingabe die ständig kommt oder ist das einmalige Konfiguration?

Einmalige Konfiguration solltest du beim Start dem Admin um die Ohren hauen ... so wie es bei der Definition als Bean direkt passiert. Das ist halt etwas, das ordentlich und sorgfältig gemacht werden muss ...
Im Augenblick soll es erstmal eine einmalige Konfiguration sein, aber ich könnte mir später auch Vorstellen, dass die Eingabe gewechselt wird.
 
T

thecain

So wie ich deinen Code sehe willst du einen bestimmten Generator anhand eines Properties laden.
Property = Profile
Generator = Bean
 
mrBrown

mrBrown

Erstmal zu deinem Code:

Oder wie würdet ihr es realisieren?
Lass in jedem Fall den Check auf null weg. Das ist nicht Thread-Sicher, und wird sowieso von Spring behandelt.

Und wo sollte ich die InvalidArgumentException abfangen bzw. bearbeiten?
Gar nicht - wenn der Entwickler die Anwendung nicht korrekt konfiguriert, muss die nicht starten, sondern kann ruhig mit Exception beendet werden.
Die Exception sollte aber sinnvoller gewählt werden, mindestens mit einer vernünftigen Message, tendenziell aber auch mit anderem Typ (da ja an der Stelle kein Argument übergeben wird).



Dann wie man es anders lösen könnte:

Generell Type-safe Configuration Properties dafür nutzen.
Statt magischen Strings einen Enum dafür nutzen.
Der enum kann dann auch einfach eine public Generator getGenerator bekommen, dann ist kein switch oä mehr nötig.
Ein von der Properties-Klasse getrennter Provider, der die Properties-Klasse injiziert bekommt, und über [USER=22881]@Bean[/USER] den jeweiligen Generator verfügbar macht.
Den Provider mit @ConditionalOnMissingBean annotieren, damit man die Bean einfach überschrieben kann.


Grob so: (vermutlich voller Fehler, aber als Idee sollte das reichen)
Java:
@ConfigurationProperties("...")
public class GeneratorProperties {
    Type generator;

    public enum Type {
        A {
            Generator getGenerator() {
                return ...;
            }
        }, B {
            @Override
            Generator getGenerator() {
                return ...
            }
        };

        abstract Generator getGenerator();
    }
}
Java:
public class GeneratorProvider {
    @Autowired
    GeneratorProperties generatorProperties;

    @Bean
    @ConditionalOnMissingBean(Generator.class) 
    public Generator generator() {
        return generatorProperties.type.getGenerator();
    }
}
 
T

thecain

Test, dev, prod sind ja nur Beispiele. das könnte auch hund, katze oder huhn sein...

Aber mit dem Vorschlag von @mrBrown solltest du ja jetzt genug Ideen und Ansätze haben um es so umzusetzen wie du willst.

Sonst musst du den Use-Case noch genauer vorstellen.
 
S

Schuriko

Erstmal zu deinem Code:


Lass in jedem Fall den Check auf null weg. Das ist nicht Thread-Sicher, und wird sowieso von Spring behandelt.


Gar nicht - wenn der Entwickler die Anwendung nicht korrekt konfiguriert, muss die nicht starten, sondern kann ruhig mit Exception beendet werden.
Die Exception sollte aber sinnvoller gewählt werden, mindestens mit einer vernünftigen Message, tendenziell aber auch mit anderem Typ (da ja an der Stelle kein Argument übergeben wird).



Dann wie man es anders lösen könnte:

Generell Type-safe Configuration Properties dafür nutzen.
Statt magischen Strings einen Enum dafür nutzen.
Der enum kann dann auch einfach eine public Generator getGenerator bekommen, dann ist kein switch oä mehr nötig.
Ein von der Properties-Klasse getrennter Provider, der die Properties-Klasse injiziert bekommt, und über [USER=22881]@Bean[/USER] den jeweiligen Generator verfügbar macht.
Den Provider mit @ConditionalOnMissingBean annotieren, damit man die Bean einfach überschrieben kann.


Grob so: (vermutlich voller Fehler, aber als Idee sollte das reichen)
Java:
@ConfigurationProperties("...")
public class GeneratorProperties {
    Type generator;

    public enum Type {
        A {
            Generator getGenerator() {
                return ...;
            }
        }, B {
            @Override
            Generator getGenerator() {
                return ...
            }
        };

        abstract Generator getGenerator();
    }
}
Java:
public class GeneratorProvider {
    @Autowired
    GeneratorProperties generatorProperties;

    @Bean
    @ConditionalOnMissingBean(Generator.class)
    public Generator generator() {
        return generatorProperties.type.getGenerator();
    }
}
Ich kann aus deinem Beispiel nicht erkennen, an welcher Stelle aus der Datei "app.properties" der Wert gelesen wird und anhand dessen entschieden wird welcher Generator benutzt werden soll.
 
mrBrown

mrBrown

Ich kann aus deinem Beispiel nicht erkennen, an welcher Stelle aus der Datei "app.properties" der Wert gelesen wird und anhand dessen entschieden wird welcher Generator benutzt werden soll.
Guck die mal den Link an, der dabei steht :)

Aus @ConfigurationProperties und dem Attribut-Namen wird der Key in der properties Datei gebildet, Der Wert des Attributes ist dann der dort eingetragene Wert.
(Wenn man das "..." dort ersetzt "xyz" führt ein "xyz.generator=A" dazu, dass da Generator-Feld zur Laufzeit Type.A enthält).

In GeneratorProvider wird das dann benutzt, in generator() wird einfach nur die getGenerator-Methode des Enums aufgerufen, der gibt dann den Generator zurück. (alle "..." müssen dafür natürlich noch sinnvoll gefühlt werden)
 
S

Schuriko

Test, dev, prod sind ja nur Beispiele. das könnte auch hund, katze oder huhn sein...

Aber mit dem Vorschlag von @mrBrown solltest du ja jetzt genug Ideen und Ansätze haben um es so umzusetzen wie du willst.

Sonst musst du den Use-Case noch genauer vorstellen.
Ok, also ich arbeite an einem Editor, dieser verwendet verschiedene Generatoren, abhängig von der Konfiguration. Aus den jeweiligen Generatoren erhalte ich bestimmte feste Werte und Funktionen. Diese Funktionen verwendet gleiche Funktionen, die allerdings unterschiedliches zurückliefern.

Diese Generatoren werden dann von Funktionen einer Service-Klasse verwendet.

Am Anfang ist es erst einmal angedacht, dass diese beim starten der Web-Anwendung durchgeführt am Anfang zur Verfügung gestellt wird. Es ist allerdings später auch denkbar, dass während der Laufzeit die Generatoren gewechselt werden können.

Ich hoffe ich konnte mich einigermassen verständlich ausdrücken ;)
 
J

JustNobody

Also erst einmal zu dem newInstance - das ist tatsächlich ab Java9 deprecated (zeigt, wie lange ich das nicht genutzt habe.... man nutzt in der Regel andere Lösungen) - aber das ist doch trivial zu lösen und Google liefert da schnell den neuen weg:
Und mittels getConstructor().newInstance() fuktioniert es dann ab Java 9.

Ich habe nicht genau verstanden, was der Generator im Editor erzeugen soll, aber wenn Du da einen Editor hast und es diverse Generatoren gibt:
a) Einmalige Konfiguration scheint mir da ungünstig. Angenommen, das generiert Textbausteine. Dann will ich vielleicht Java Bausteine und jemand anderes C# Bausteine. Ich würde sogar so weit gehen, dass ich das nicht einmal für mich festgelegt haben möchte: Ich brauche in einem Tab Java Bausteine und in einem anderen XML Bausteine....
b) Wenn Du so ein tolles Interface hast, dann klingt das gerade zu nach einer tollen Möglichkeit, dies erweitern zu können. Nehmen wir das Beispiel oben: Du hast Generatoren für Java und XML. C# und so interessieren Dich nicht. Dann wäre es doch super, wenn Andere auch Generatoren entwickeln und hinzufügen könnten ohne Deinen Code ändern zu müssen!

(Die Idee von @mrBrown ist die Lösung / Idee, die sehr schön ist, wenn man eine klare, feste Menge an Elementen hat. Aber es muss halt diese feste Menge an Elementen geben. Aber die Frage ist immer, was die genauen Anforderungen sind um daraus abzuleiten, was man machen möchte der muss. Wenn das neue Generatoren hinzufügen für Dich erst einmal egal ist, dann brauchst Du diese Ideen nicht. Aber wie gesagt: Die Festlegung auf einen Generator scheint mit auf Applikationsebene nicht logisch - oder ich habe nicht verstanden, was Du da genau editieren / generieren willst....)

Aber generell hast Du jetzt ganz viele Optionen und Möglichkeiten aus denen Du auswählen kannst. Welche Variante zu Deinen Anforderungen am Besten passt musst Du entscheiden / wissen.
 
Thema: 

@Value mit bestimmt Werten, sonst default

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben