Kann ich zur Laufzeit eine Spring Bean-Definition ersetzen?

68

Stellen Sie sich das folgende Szenario vor. Ich habe einen Spring - Anwendungskontext mit einer Bohne , deren Eigenschaften sollte konfigurierbar sein, denken DataSourceoder MailSender. Die veränderbare Anwendungskonfiguration wird von einer separaten Bean verwaltet. Nennen wir es configuration.

Ein Administrator kann jetzt die Konfigurationswerte wie E-Mail-Adresse oder Datenbank-URL ändern, und ich möchte die konfigurierte Bean zur Laufzeit neu initialisieren.

Angenommen, ich kann nicht einfach die Eigenschaft der oben konfigurierbaren Bean ändern (z. B. erstellt durch FactoryBeanoder Konstruktorinjektion), sondern muss die Bean selbst neu erstellen.

Irgendwelche Gedanken darüber, wie dies erreicht werden kann? Ich würde mich freuen, Ratschläge zu erhalten, wie auch die gesamte Konfigurationssache organisiert werden kann. Nichts ist behoben. :-)

BEARBEITEN

Um die Dinge ein wenig zu klären: Ich frage nicht, wie die Konfiguration aktualisiert oder statische Konfigurationswerte eingefügt werden sollen. Ich werde ein Beispiel versuchen:

<beans>
    <util:map id="configuration">
        <!-- initial configuration -->
    </util:map>

    <bean id="constructorInjectedBean" class="Foo">
        <constructor-arg value="#{configuration['foobar']}" />
    </bean>

    <bean id="configurationService" class="ConfigurationService">
        <property name="configuration" ref="configuration" />
    </bean>
</beans>

Es gibt also eine Bohne constructorInjectedBean, die Konstruktorinjektion verwendet. Stellen Sie sich vor, die Konstruktion der Bohne ist sehr teuer, daher ist die Verwendung eines Prototyp-Oszilloskops oder eines Factory-Proxys keine Option DataSource.

Was ich tun möchte, ist, dass jedes Mal, wenn die Konfiguration aktualisiert wird (über configurationServicedie Bean constructorInjectedBeanwird sie neu erstellt und erneut in den Anwendungskontext und die abhängigen Beans eingefügt).

Wir können davon ausgehen, dass constructorInjectedBeaneine Schnittstelle verwendet wird, sodass Proxy-Magie in der Tat eine Option ist.

Ich hoffe, die Frage etwas klarer gestellt zu haben.

Philipp Jardas
quelle
Die configurationBean muss also zur Laufzeit aktualisiert werden - oder jedes Mal, wenn der Administrator die Werte ändert? Ich das deine Frage? Oder möchten Sie, dass die DataSource/ MailSenderBeans zur Laufzeit die aktualisierte Konfiguration verwenden? Oder ist es beides?
Madhurtanwani
Es ist das zweite: Ich möchte die injizierten Konfigurationswerte zur Laufzeit aktualisieren (siehe Bearbeiten in OP).
Philipp Jardas

Antworten:

12

Ich kann mir einen "Holder Bean" -Ansatz vorstellen (im Wesentlichen ein Dekorateur), bei dem die Holder Bean an Holdee delegiert und die Holder Bean als Abhängigkeit in andere Bohnen injiziert wird. Niemand außer dem Inhaber hat einen Hinweis auf den Inhaber. Wenn nun die Konfiguration der Holder-Bean geändert wird, wird der Holdee mit dieser neuen Konfiguration neu erstellt und an sie delegiert.

shrini1000
quelle
Ich öffne diese Seite, um diese Antwort zu finden.
Msangel
1
Wissen Sie was, nach fast einem Jahr finde ich diese Antwort und sie spiegelt wider, wie ich das Problem am Ende umgesetzt habe. Der Dekorateur implementiert die Schnittstelle des erforderlichen Objekts, registriert sich als Beobachter beim Konfigurationsdienst und erstellt das betreffende Objekt bei Bedarf neu.
Philipp Jardas
@Philipp Jardas, vielen Dank, dass Sie sich die Zeit genommen haben, zurück zu kommen und ein Update mit der von Ihnen gewählten tatsächlichen Implementierung zu geben. Sehr geschätzt!
Shrini1000
29

So habe ich es in der Vergangenheit gemacht: Ausführen von Diensten, die von der Konfiguration abhängen und im laufenden Betrieb geändert werden können, Implementieren einer Lebenszyklusschnittstelle: IRefreshable:

public interface IRefreshable {
  // Refresh the service having it apply its new values.
  public void refresh(String filter);

  // The service must decide if it wants a cache refresh based on the refresh message filter.
  public boolean requiresRefresh(String filter);
}

Controller (oder Dienste), die eine Konfigurationsübertragung an ein JMS-Thema ändern können, das von der Konfiguration geändert wurde (unter Angabe des Namens des Konfigurationsobjekts). Eine nachrichtengesteuerte Bean ruft dann den IRefreshable-Schnittstellenvertrag für alle Beans auf, die IRefreshable implementieren.

Das Schöne an spring ist, dass Sie automatisch jeden Dienst in Ihrem Anwendungskontext erkennen können, der aktualisiert werden muss, sodass sie nicht mehr explizit konfiguriert werden müssen:

public class MyCacheSynchService implements InitializingBean, ApplicationContextAware {
 public void afterPropertiesSet() throws Exception {
  Map<String, ?> refreshableServices = m_appCtx.getBeansOfType(IRefreshable.class);
  for (Map.Entry<String, ?> entry : refreshableServices.entrySet() ) {
   Object beanRef = entry.getValue();
   if (beanRef instanceof IRefreshable) {
    m_refreshableServices.add((IRefreshable)beanRef);
   }
  }
 }
}

Dieser Ansatz funktioniert besonders gut in einer Clusteranwendung, in der einer von vielen App-Servern möglicherweise die Konfiguration ändert, die alle dann kennen müssen. Wenn Sie JMX als Mechanismus zum Auslösen der Änderungen verwenden möchten, kann Ihre JMX-Bean dann an das JMS-Thema senden, wenn eines ihrer Attribute geändert wird.

Justin
quelle
Ich sollte auch erwähnen, dass Sie bei der Verwendung von JMX zusätzliche Arbeit leisten müssen, damit die Sicherheit von JMX an das Sicherheitsmodell Ihrer Anwendung delegiert wird. Mit dem oben beschriebenen Ansatz ist dies weniger problematisch, da die Konfigurationsänderung über Ihre Web-GUI vorgenommen wird (vorausgesetzt, Sie haben eine) und Ihr vorhandenes App-Sicherheitsmodell erneut verwendet. Da die Aktualisierung lediglich über JMS "vorgeschlagen" wird, muss sie (zu diesem Zweck) nicht wirklich gesichert werden.
Justin
Das Problem bei diesem Ansatz besteht darin, dass die Aktualisierung eine atomare Operation in Bezug auf andere Anforderungen sein sollte, die von der Anwendung verarbeitet werden. Andernfalls würde eine Anforderung mit einer geänderten Konfiguration in der Mitte ihrer Verarbeitung verarbeitet. Dies kann zu unvorhersehbarem Verhalten führen. Im Wesentlichen müssten Sie also Anforderungen blockieren, die den Anwendungskontext auf irgendeine Weise verwenden. Aber dann können Sie einfach den Anwendungskontext selbst aktualisieren.
SpaceTrucker
11

Sie sollten sich JMX ansehen . Auch hierfür unterstützt der Frühling.

mR_fr0g
quelle
Danke für den Vorschlag. Ich muss zugeben, dass ich ein ziemlicher Neuling bin, wenn es um JMX geht. Könnten Sie bitte einen kleinen Hinweis geben, wie Sie das gesuchte Verhalten erreichen können?
Philipp Jardas
@Philipp: JMX mit Spring ist ziemlich einfach. Sie erstellen einige javabean-ähnliche Methoden (dh get / set-Paare), mit denen Sie den Wert bearbeiten können, den Sie optimieren möchten, und Sie kommentieren die Klasse mit @ManagedResourceund die Bean-Methoden mit @ManagedAttribute. Dann können Sie mit einem JMX-Client eine Verbindung zur laufenden Instanz herstellen (ich verwende sie jvisualvmmit den entsprechenden Plugins).
Donal Fellows
@Donal: Danke für die Antwort. Ich kann jedoch immer noch nicht sehen, wie ich das oben beschriebene Problem lösen kann. Wie würde das JMX-Management beim Ändern der Instanzen der Klassen in meinem Anwendungskontext helfen?
Philipp Jardas
@Philipp: JMX kann Methoden für (entsprechend kommentierte) Objekte erreichen und direkt aufrufen. Wenn Sie nicht sehen können, wie dies für die Neukonfiguration zur Laufzeit nützlich sein könnte, entgehen mir Worte ...
Donal Fellows
@Donal: Ich kann deutlich sehen, wie JMX mir helfen würde, eigenschaftskonfigurierte Beans neu zu konfigurieren, ebenso wie die in einigen anderen Antworten beschriebenen Methoden. Ich habe jedoch speziell nach einer Möglichkeit gefragt, vom Konstruktor injizierte oder vom Bean Factory erstellte Beans neu zu konfigurieren, für die JMX nicht geeigneter zu sein scheint als jede der anderen genannten Methoden. Verstehe ich das falsch?
Philipp Jardas
2

Weitere aktualisierte Antwort auf geskriptete Bean

Ein weiterer Ansatz, der von Spring 2.5.x + unterstützt wird, ist der der Scripted Bean. Sie können eine Vielzahl von Sprachen für Ihr Skript verwenden - BeanShell ist wahrscheinlich die intuitivste, da es dieselbe Syntax wie Java hat, jedoch einige externe Abhängigkeiten erfordert. Die Beispiele sind jedoch in Groovy.

In Abschnitt 24.3.1.2 der Frühjahrsdokumentation wird beschrieben, wie dies konfiguriert wird. Hier sind jedoch einige wichtige Auszüge, die den Ansatz veranschaulichen, den ich bearbeitet habe, um sie für Ihre Situation besser anwendbar zu machen:

<beans>

    <!-- This bean is now 'refreshable' due to the presence of the 'refresh-check-delay' attribute -->
    <lang:groovy id="messenger"
          refresh-check-delay="5000" <!-- switches refreshing on with 5 seconds between checks -->
          script-source="classpath:Messenger.groovy">
        <lang:property name="message" value="defaultMessage" />
    </lang:groovy>

    <bean id="service" class="org.example.DefaultService">
        <property name="messenger" ref="messenger" />
    </bean>

</beans>

Das Groovy-Skript sieht folgendermaßen aus:

package org.example

class GroovyMessenger implements Messenger {

    private String message = "anotherProperty";

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message
    }
}

Da der Systemadministrator Änderungen vornehmen möchte, können sie (oder Sie) den Inhalt des Skripts entsprechend bearbeiten. Das Skript ist nicht Teil der bereitgestellten Anwendung und kann auf einen bekannten Speicherort verweisen (oder auf einen, der beim Start über einen Standard-PropertyPlaceholderConfigurer konfiguriert wird).

Obwohl im Beispiel eine Groovy-Klasse verwendet wird, kann die Klasse Code ausführen, der eine einfache Eigenschaftendatei liest. Auf diese Weise bearbeiten Sie das Skript nie direkt. Berühren Sie es einfach, um den Zeitstempel zu ändern. Diese Aktion löst dann das Neuladen aus, was wiederum die Aktualisierung von Eigenschaften aus der (aktualisierten) Eigenschaftendatei auslöst, wodurch schließlich die Werte im Spring-Kontext aktualisiert werden und los geht's.

In der Dokumentation wird darauf hingewiesen, dass diese Technik für die Konstruktorinjektion nicht funktioniert, aber vielleicht können Sie das umgehen.

Die Antwort wurde aktualisiert, um dynamische Eigenschaftsänderungen abzudecken

Ein Ansatz aus diesem Artikel , der den vollständigen Quellcode enthält , lautet:

* a factory bean that detects file system changes
* an observer pattern for Properties, so that file system changes can be propagated
* a property placeholder configurer that remembers where which placeholders were used, and updates singleton beans’ properties
* a timer that triggers the regular check for changed files

Das Beobachtermuster wird von den Schnittstellen und Klassen ReloadableProperties, ReloadablePropertiesListener, PropertiesReloadedEvent und ReloadablePropertiesBase implementiert. Keiner von ihnen ist besonders aufregend, nur normaler Umgang mit Hörern. Die Klasse DelegatingProperties dient zum transparenten Austausch der aktuellen Eigenschaften, wenn Eigenschaften aktualisiert werden. Wir aktualisieren nur die gesamte Eigenschaftskarte auf einmal, damit die Anwendung inkonsistente Zwischenzustände vermeiden kann (dazu später mehr).

Jetzt kann die ReloadablePropertiesFactoryBean geschrieben werden, um eine ReloadableProperties-Instanz zu erstellen (anstelle einer Properties-Instanz, wie dies bei der PropertiesFactoryBean der Fall ist). Wenn der RPFB dazu aufgefordert wird, überprüft er die Änderungszeiten der Dateien und aktualisiert bei Bedarf seine ReloadableProperties. Dies löst die Beobachtermustermaschinerie aus.

In unserem Fall ist der einzige Listener der ReloadingPropertyPlaceholderConfigurer. Es verhält sich wie ein PropertyPlaceholderConfigurer für Standardfedern, mit der Ausnahme, dass alle Verwendungen von Platzhaltern verfolgt werden. Wenn nun Eigenschaften neu geladen werden, werden alle Verwendungen jeder geänderten Eigenschaft gefunden und die Eigenschaften dieser Singleton-Beans erneut zugewiesen.

Die ursprüngliche Antwort unten bezieht sich auf statische Eigenschaftsänderungen:

Klingt so, als ob Sie nur externe Eigenschaften in Ihren Spring-Kontext einfügen möchten. Das PropertyPlaceholderConfigurerist für diesen Zweck ausgelegt:

  <!-- Property configuration (if required) -->
  <bean id="serverProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
      <list>
        <!-- Identical properties in later files overwrite earlier ones in this list -->
        <value>file:/some/admin/location/application.properties</value>
      </list>
    </property>
  </bean>

Anschließend verweisen Sie mit Platzhaltern für die Ant-Syntax auf die externen Eigenschaften (die verschachtelt werden können, wenn Sie dies ab Spring 2.5.5 möchten).

  <bean id="example" class="org.example.DataSource">
    <property name="password" value="${password}"/>
  </bean>

Anschließend stellen Sie sicher, dass auf die Datei application.properties nur der Administrator und der Benutzer zugreifen können, der die Anwendung ausführt.

Beispiel application.properties:

Passwort = Erdferkel

Gary Rowe
quelle
Danke für die Antwort. Ich erkundige mich tatsächlich, wie die Eigenschaften zur Laufzeit aktualisiert werden können (siehe Bearbeiten in OP).
Philipp Jardas
@Philipp OK, in diesem Fall wird diese Diskussion wahrscheinlich helfen (es ist für eine ältere Version von Spring, könnte aber aktualisiert werden). Lesen Sie die ausführlichen Kommentare, um Hinweise zu erhalten, wie es mit Karteneinträgen
Gary Rowe
@ Gary, danke für die Antwort. Ich habe den von Ihnen erwähnten Blog überprüft und festgestellt, dass ich so etwas bereits selbst erstellt habe. Ich habe jedoch immer noch keine Ahnung, wie ich nicht nur die Eigenschaften von Beans aktualisieren, sondern auch die Bean-Instanzen selbst ersetzen könnte. Nehmen wir zur Diskussion an, dass es sich um eine Eigenschaft von a DataSourcehandelt, die mit a erstellt wurde FactoryBean. Der erwähnte Ansatz würde nur die Werte der Factory Bean aktualisieren, was überhaupt keine Hilfe ist. :-(
Philipp Jardas
@Philipp Keine Probleme. Als letzten Versuch, Ihr Problem zu lösen, könnten Sie sich Bean Scripting ansehen (siehe meine Änderungen in der Antwort für Details)
Gary Rowe
@ Gary, vielen Dank für die ausführliche Antwort und den interessanten Ansatz mit Scripting. Buuuuuut, Sie wissen, dass dies kommen wird: "Funktioniert nicht für die Konstruktorinjektion" - Sie haben es selbst gesagt. :-) Was Sie jetzt vorgeschlagen haben, ist eine ausgefallene Methode, um die Eigenschaften vorhandener Bohnen festzulegen. Aber wie verwalte ich Instanzen, die mit Konstruktorinjektion oder Factory Beans erstellt wurden?
Philipp Jardas
2

Oder Sie könnten den Ansatz aus dieser ähnlichen Frage und damit auch meine Lösung verwenden :

Der Ansatz besteht darin, Beans zu haben, die über Eigenschaftendateien konfiguriert werden, und die Lösung besteht darin, entweder

  • Aktualisieren Sie den gesamten applicationContext (automatisch mithilfe einer geplanten Aufgabe oder manuell mithilfe von JMX), wenn sich die Eigenschaften geändert haben oder
  • Verwenden Sie ein dediziertes Objekt des Objektanbieters, um auf alle Eigenschaften zuzugreifen. Dieser Eigenschaftenanbieter überprüft die Eigenschaftendateien weiterhin auf Änderungen. Registrieren Sie für Beans, bei denen eine prototypbasierte Eigenschaftensuche nicht möglich ist, ein benutzerdefiniertes Ereignis , das Ihr Immobilienanbieter auslöst, wenn er eine aktualisierte Eigenschaftendatei findet. Ihre Bohnen mit komplizierten Lebenszyklen müssen auf dieses Ereignis warten und sich selbst aktualisieren.
Sean Patrick Floyd
quelle
1

Dies habe ich nicht versucht, ich versuche, Hinweise zu geben.

Angenommen, Ihr Anwendungskontext ist eine Unterklasse von AbstractRefreshableApplicationContext (Beispiel XmlWebApplicationContext, ClassPathXmlApplicationContext). AbstractRefreshableApplicationContext.getBeanFactory () gibt Ihnen eine Instanz von ConfigurableListableBeanFactory. Überprüfen Sie, ob es sich um eine Instanz von BeanDefinitionRegistry handelt. In diesem Fall können Sie die Methode 'registerBeanDefinition' aufrufen. Dieser Ansatz wird eng mit der Implementierung von Spring verbunden sein.

Überprüfen Sie den Code von AbstractRefreshableApplicationContext und DefaultListableBeanFactory (dies ist die Implementierung, die Sie erhalten, wenn Sie 'AbstractRefreshableApplicationContext getBeanFactory ()' aufrufen.)

Adisesha
quelle
Dies könnte durchaus eine nette Idee sein. Die Schnittstelle für registerBeanDefinition()ist org.springframework.beans.factory.support.BeanDefinitionRegistryübrigens. Ich werde das untersuchen, danke.
Philipp Jardas
Ja. Vergessen Sie auch nicht, 'allowBeanDefinitionOverriding' im Anwendungskontext festzulegen.
Adisesha
1

Sie können einen benutzerdefinierten Bereich mit dem Namen "rekonfigurierbar" im ApplicationContext erstellen. Es werden Instanzen aller Beans in diesem Bereich erstellt und zwischengespeichert. Bei einer Konfigurationsänderung wird der Cache geleert und die Beans werden beim ersten Zugriff mit der neuen Konfiguration neu erstellt. Damit dies funktioniert, müssen Sie alle Instanzen rekonfigurierbarer Beans in einen Proxy mit AOP-Gültigkeitsbereich einbinden und mit Spring-EL auf die Konfigurationswerte zugreifen: Fügen Sie eine aufgerufene Map configin den ApplicationContext ein und greifen Sie wie folgt auf die Konfiguration zu #{ config['key'] }.

Hans-Peter Störr
quelle
0

Option 1 :

  1. Injizieren Sie die configurableBohne in das DataSourceoder MailSender. Rufen Sie die konfigurierbaren Werte immer aus der Konfigurations-Bean in diesen Beans ab.
  2. configurableFühren Sie innerhalb der Bean einen Thread aus, um die extern konfigurierbaren Eigenschaften (Datei usw.) regelmäßig zu lesen. Auf diese Weise aktualisiert sich die configurableBean selbst, nachdem der Administrator die Eigenschaften geändert hat, und DataSourceerhält die aktualisierten Werte automatisch.

Option 2 (schlecht, denke ich, aber vielleicht nicht - hängt vom Anwendungsfall ab):

  1. Erstellen Sie immer neue Beans für Beans vom Typ DataSource/ MailSender- mit dem prototypeGültigkeitsbereich. Lesen Sie im Init der Bean die Eigenschaften erneut.

Option 3: Ich denke, @ mR_fr0g Vorschlag zur Verwendung von JMX ist möglicherweise keine schlechte Idee. Was Sie tun könnten, ist:

  1. Stellen Sie Ihre Konfigurations-Bean als MBean bereit (lesen Sie http://static.springsource.org/spring/docs/2.5.x/reference/jmx.html ).
  2. Bitten Sie Ihren Administrator, die Konfigurationseigenschaften in der MBean zu ändern (oder eine Schnittstelle in der Bean bereitzustellen, um Eigenschaftsaktualisierungen von ihrer Quelle aus auszulösen).
  3. Diese MBean (ein neuer Java-Code, den Sie schreiben müssen) MUSS Referenzen von Beans behalten (diejenigen, in die Sie die geänderten Eigenschaften ändern / einfügen möchten). Dies sollte einfach sein (über Setter-Injection oder Laufzeitabruf von Bean-Namen / -Klassen).
    1. Wenn die Eigenschaft in der MBean geändert (oder ausgelöst) wird, muss sie die entsprechenden Setter für die jeweiligen Beans aufrufen. Auf diese Weise ändert sich Ihr Legacy-Code nicht. Sie können weiterhin Änderungen der Laufzeit-Eigenschaften verwalten.

HTH!

madhurtanwani
quelle
Ich hoffe auch, dass Sie stackoverflow.com/questions/2008175/…
madhurtanwani
Option 1 funktioniert wahrscheinlich nicht, wenn ich keine Kontrolle über die konfigurierten Klassen habe. Und wenn ich die Kontrolle über sie hätte, würden sie durch Konfigurationscode "verschmutzt", was für mich nicht gut klingt. Option 2 ist in meinem Fall keine Option, da der Bean-Lebenszyklus teuer ist. Sie möchten keinen Prototypbereich auf eine DataSource anwenden. Eine Bearbeitung mit Erläuterungen finden Sie unter OP.
Philipp Jardas
Die <a href=" stackoverflow.com/questions/2008175/… Frage</a> scheint keine Antwort auf mein Problem zu enthalten ...
Philipp Jardas
Ich bin nicht sicher, ob "verschmutzt" überhaupt richtig ist - Sie haben Klassen, die eine extern definierte Konfiguration benötigen. Jetzt müssen Sie entweder die Eigenschaften (wenn sie sich ändern) in diese Klassen "injizieren" (in diesem Fall muss die Klasse, die die Requisite injiziert, wissen, dass solche Klassen die Konfiguration benötigen), oder diese Klassen müssen die Änderungen ziehen, sobald sie eintreten - wahrscheinlich über einen zentralen Konfigurationsmanager
madhurtanwani
Ich habe über den Verschmutzungsaspekt nachgedacht und denke, Sie haben Recht. Es wird wahrscheinlich keine Möglichkeit geben, dieses Problem zu lösen, ohne Konfigurationsaspekte in die Klassen zu mischen. Ich habe jedoch noch keine Ahnung, wie Beans aktualisiert werden sollen, die mit Konstruktorinjektion oder Factory-Beans erstellt wurden. :-(
Philipp Jardas
0

Vielleicht möchten Sie einen Blick auf den Spring Inspector werfen, eine steckbare Komponente, die zur Laufzeit programmgesteuerten Zugriff auf jede Spring-basierte Anwendung bietet. Mit Javascript können Sie Konfigurationen ändern oder das Anwendungsverhalten zur Laufzeit verwalten.

Julio
quelle
0

Hier ist die gute Idee, einen eigenen PlaceholderConfigurer zu schreiben, der die Verwendung von Eigenschaften verfolgt und diese bei jeder Konfigurationsänderung ändert. Dies hat jedoch zwei Nachteile:

  1. Es funktioniert nicht mit der Konstruktorinjektion von Eigenschaftswerten.
  2. Sie können Race-Bedingungen erhalten, wenn die neu konfigurierte Bean eine geänderte Konfiguration erhält, während sie einige Dinge verarbeitet.
Hans-Peter Störr
quelle
0

Meine Lösung bestand darin, das ursprüngliche Objekt zu kopieren. Faust Ich habe eine Schnittstelle erstellt

/**
 * Allows updating data to some object.
 * Its an alternative to {@link Cloneable} when you cannot 
 * replace the original pointer. Ex.: Beans 
 * @param <T> Type of Object
 */
public interface Updateable<T>
{
    /**
     * Import data from another object
     * @param originalObject Object with the original data
     */
    public void copyObject(T originalObject);
}

Um die Implementierung der Funktion faust zu vereinfachen, erstellen Sie einen Konstruktor mit allen Feldern, damit die IDE mir ein bisschen helfen kann. Dann können Sie einen Kopierkonstruktor erstellen, der dieselbe Funktion verwendet Updateable#copyObject(T originalObject). Sie können auch vom Code des von der IDE erstellten Konstruktors profitieren , um die zu implementierende Funktion zu erstellen:

public class SettingsDTO implements Cloneable, Updateable<SettingsDTO>
{
    private static final Logger LOG = LoggerFactory.getLogger(SettingsDTO.class);

    @Size(min = 3, max = 30)  
    private String id;

    @Size(min = 3, max = 30)
    @NotNull 
    private String name;

    @Size(min = 3, max = 100)
    @NotNull 
    private String description;

    @Max(100)
    @Min(5) 
    @NotNull
    private Integer pageSize;

    @NotNull 
    private String dateFormat; 

    public SettingsDTO()
    { 
    }   

    public SettingsDTO(String id, String name, String description, Integer pageSize, String dateFormat)
    {
        this.id = id;
        this.name = name;
        this.description = description;
        this.pageSize = pageSize;
        this.dateFormat = dateFormat;
    }

    public SettingsDTO(SettingsDTO original)
    {
        copyObject(original);
    }

    @Override
    public void copyObject(SettingsDTO originalObject)
    {
        this.id = originalObject.id;
        this.name = originalObject.name;
        this.description = originalObject.description;
        this.pageSize = originalObject.pageSize;
        this.dateFormat = originalObject.dateFormat;
    } 
}

Ich habe es in einem Controller verwendet, um die aktuellen Einstellungen für die App zu aktualisieren:

        if (bindingResult.hasErrors())
        {
            model.addAttribute("settingsData", newSettingsData);
            model.addAttribute(Templates.MSG_ERROR, "The entered data has errors");
        }
        else
        {
            synchronized (settingsData)
            {
                currentSettingData.copyObject(newSettingsData);
                redirectAttributes.addFlashAttribute(Templates.MSG_SUCCESS, "The system configuration has been updated successfully");
                return String.format("redirect:/%s", getDao().getPath());
            }
        }

So ist die , currentSettingsDatadie die Konfiguration der Anwendung hat im Begriff , die aktualisierten Werte haben, in newSettingsData. Diese Methode ermöglicht die Aktualisierung jeder Bean ohne hohe Komplexität.

EliuX
quelle