2017-04-14 2 views
1

J'ai une classe existante nommée Legacy qui est la plupart du temps écrite en style singleton old school. Maintenant, je veux introduire un nouveau champ et j'aimerais utiliser Guice. Legacy elle-même n'est pas contrôlée par Guice, elle est utilisée par une autre classe Service (à l'intérieur de la classe Service, elle appelle la classe getInstance() de Legacy pour récupérer l'objet Legacy) et cette classe Service a été créée à l'aide de Guice injector.Comment utiliser l'injection de guice pour une classe singleton existante?

public class Legacy { 

    public synchronized static Legacy getInstance() { 
     if(sInstance == null) { 
      sInstance = new Legacy(); 
     } 
     return sInstance; 
    } 

    private Legacy() { 
     legacyObj = LegacyField.getInstance(); // get a singleton 
    } 

    private static Legacy sInstance; 

    private LegacyField legacyObj; 

    private NewField newObj; // this is the new dependency I would like to add using Guice 
} 

Ce que j'ai essayé est que j'ai essayé de mettre la méthode Injecter dans la classe Legacy

@Inject 
public void setNewField(NewField newObj) { 
    this.newObj = newObj; 
} 

Et dans le fichier module du service, je lie l'objet NewField, mais quand je lance le programme, il a jeté une exception NullPointer. Donc, l'injection ne fonctionne pas. Toute idée de comment faire de NewField injecter dans mon programme, mais garder le paradigme de la vieille école singleton et ne pas trop changer à propos de tout le reste?

EDIT Il existe au moins trois solutions ci-dessous et je ne sais pas quel est le meilleur ou sont-ils équivalents.

+1

Les 3 solutions sont plus ou moins équivalentes. Ils appellent tous (directement ou indirectement) 'injectMembers' sur votre singleton. –

Répondre

1

Voici une solution quelque peu hackish.

Dans le bootstrapping de votre demande, peut-être dans la méthode public static void main(String[] args), vous devriez déjà avoir un code similaire à ceci:

Injector injector = Guice.createInjector(yourModule); 

A cet endroit, ajoutez la ligne suivante:

injector.injectMembers(Legacy.getInstance()); 

En faisant ainsi, tous les @Inject s dans votre Legacy singleton devraient être résolus. Voir aussi le javadoc Injector.injectMembers.

2

Bien que légèrement plus propre que la réponse de Thomas, vous pouvez configurer l'injection de votre singleton à partir de votre module en utilisant requestInjection ou requestStaticInjection.

// In your Module: 
requestInjection(Legacy.getInstance()); // for an instance field, or 
requestStaticInjection(Legacy.class); // for a static field. 

Le docs on the wiki mettent en garde contre les inconvénients, cependant:

Cette API n'est pas recommandé pour un usage général, car il souffre beaucoup des mêmes problèmes que les usines statiques: il est maladroit de tester, il fait des dépendances opaque, et il repose sur l'état global.

3

Je viens de trouver une autre solution:

// put in the module 
bind(Legacy.class).toInstance(Legacy.getInstance()); 

Dans cet exemple, le module lui-même, et non Guice, prend la responsabilité d'obtenir une instance de Legacy, demande alors Guice toujours utiliser cette instance unique pour répondre à tous Demandes d'injection héritées. Mais selon le javadoc Lorsque l'injecteur est créé, il effectue automatiquement l'injection de champ et de méthode pour cette instance, mais tout constructeur injectable sur Legacy est simplement ignoré. Notez que l'utilisation de cette approche entraîne un comportement de «chargement hâtif» que vous ne pouvez pas contrôler.

+0

Cette solution présente l'avantage supplémentaire que vous pouvez même utiliser '@Inject Legay legacy;' pour injecter votre singleton dans d'autres classes. –