web-dev-qa-db-fra.com

Façon correcte de transformer un singleton en haricot du printemps

Je suis en train de convertir un singleton en bean Spring. Ainsi, si le singleton ne parvient pas à s'initialiser, le contexte de printemps de l'application Web entière ne se charge pas correctement. 

L'avantage de ne pas charger correctement le contexte Spring est que les utilisateurs en prendront note et corrigeront la configuration pendant le déploiement. Contrairement à l'utilisation de singleton 'non-spring bean': quand cela lève une exception lors de l'initialisation, personne ne le remarque .. jusqu'à ce qu'un utilisateur réel se plaint de la fonctionnalité manquante.

Mes changements fonctionnent comme prévu .. mais je ne suis pas sûr de faire la bonne chose.
Des pensées?

Le code ressemble à ceci:

public class MySingleton {

    private static MySingleton INSTANCE = null;
    private MySingleton(){}


public static MySingleton getInstance(){
    if(INSTANCE == null){
        synchronized(MySingleton.class){
            if(INSTANCE == null){
                try{
                    doWork()
                }catch(Exception e){
                    throw new IllegalStateException("xyz", e);
                }
                INSTANCE = new MySingleton();
            }
        }
    }

    return INSTANCE;
}

private static void doWork() {
    // do some work
    }

}

Et au printemps config xml, le haricot sera défini comme suit:

<bean id="MySingletonBean"
    class="com.MySingleton"
    factory-method="getInstance" lazy-init="false" singleton="true">
</bean>

Remarque: La plupart de ces opérations sont similaires à la stratégie décrite dans cet article: http://springtips.blogspot.com/2007/06/configuration-hell-remedy-with.html


Éditer 1:

Les classes qui utilisent ce singleton ne sont pas des haricots de printemps, mais des pojos non printaniers, que je ne peux pas convertir en printemps. Ils doivent compter sur la méthode getInstance () pour obtenir le Singleton.


Edit 2: (en copiant un commentaire que j'ai fait ci-dessous dans cette section de description) J'essaie de cibler deux choses:

  1. Je veux que Spring initialise le singleton. Si l'échec de l'initialisation .__, le chargement de l'application échoue. 
  2. Je veux que les autres classes puissent utiliser des classes sans avoir à s'appuyer sur contextAwareObj.getBean ("MySingleton")


EDIT 3 (FINAL): J’ai décidé de faire de cette classe un singleton .. et je n’en ferai pas un haricot de printemps. Si elle ne parvient pas à s'initialiser, elle enregistrera quelque chose dans le fichier journal. J'espère que la personne effectuant le déploiement en tiendra compte ... J'ai abandonné l'approche que j'ai mentionnée plus tôt, car je sens que cela va créer un cauchemar de maintenance à l'avenir. choisissez entre - singleton - ou - haricot de printemps. J'ai choisi singleton.

20
rk2010

Vous devez déclarer le champ INSTANCE comme volatile pour que le verrouillage coché double fonctionne correctement.

Voir Effective Java , Point 71 .

18
Matt Ball

Pourquoi utilisez-vous le motif singleton en premier lieu? Laissez simplement Spring créer un haricot pour vous (avec la portée par défaut singleton) et ... utilisez-le. Bien sûr, toujours quelqu'un peut créer le haricot à la main, mais cela n'a jamais été un problème dans mon cas.

L'injection de dépendance et le cycle de vie des haricots gérés par Spring faciliteront considérablement votre vie (voyez juste combien de pièges vous pouvez éviter). Notez également que les exceptions émises par la méthode c-tor ou @PostContruct se propagent et entraînent également l'échec du démarrage du contexte de l'application.

UPDATE: Je comprends ce que vous voulez dire. C'est ce qui m'est venu à l'esprit:

@Service
public class Singleton {

    private static AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

    public Singleton() {
        final Singleton previous = INSTANCE.getAndSet(this);
        if(previous != null)
            throw new IllegalStateException("Second singleton " + this + " created after " + previous);
    }

    public static Singleton getInstance() {
        return INSTANCE.get();
    }

}

Et laissez Spring faire son travail. Vous pouvez utiliser DI lorsque cela est possible et Singleton.getInstance() où vous devez.

En outre, il existe de plus en plus de solutions de base telles que le tissage AspectJ au moment de la compilation et l'injection de beans Spring essentiellement à tout.

12

Je ne sais pas pourquoi tu voudrais faire ça. Lorsque vous indiquez à Spring qu’un haricot doit être un singleton, la classe correspondante n’a pas besoin d’être un singleton ni une usine. Spring crée simplement une seule instance.

L'article lié n'a aucun sens pour moi, puisqu'il n'y a PAS d'injection en cours, ce que je peux voir: "AnyService" appelle la méthode singleton factory; que le singleton soit référencé dans le contexte de l'application est sans importance jusqu'à ce qu'il soit référencé, et il semble qu'aucun autre bean ne le mentionne.

6
Rodney Gitzel

Les vrais singleton sont difficiles à obtenir.

Le verrouillage à double vérification volatile ne fonctionne pas non plus. Lisez à ce sujet sur wiki http://en.wikipedia.org/wiki/Double-checked_locking

Votre meilleur pari est simplement de le faire

public class MySingleton {

    private static MySingleton INSTANCE = new MySingleton();

C'est-à-dire si vous n'avez aucun paramètre de constructeur dans votre code réel.

5
Denis

Selon moi, c'est une solution ceinture et bretelles.

Si vous créez un bean et le déclarez en tant que singleton dans la configuration, il ne devrait pas être nécessaire de le protéger contre la création multiple.

Vous vous protégez maintenant essentiellement contre les erreurs de configuration du haricot.

Personnellement, je "résoudre" cela par la documentation dans la configuration de printemps et Javadoc.

3
Peter Tillemans

Pour exécuter le code au démarrage (et échouer en cas d’erreur), utilisez l’un des nombreux moyens d’enregistrer les événements de démarrage, par exemple. voir http://www.baeldung.com/running-setup-logic-on-startup-in-spring

Exemple:

@Component
public class InitializingBeanExampleBean implements InitializingBean {

    private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class);

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}
1
tkruse