IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Spring Java Discussion :

Singletons avec Spring


Sujet :

Spring Java

  1. #1
    Nouveau membre du Club Avatar de Hyperion99
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    62
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mai 2006
    Messages : 62
    Points : 35
    Points
    35
    Par défaut Singletons avec Spring
    Bonjour a tous

    Je débute avec spring (pour ne pas dire avec les avec applications servers...) et j'aurai quelques précisions à vous demander :

    J'utilise Spring avec les annotations ...

    Dans mon cas je voudrais créer un cas d'école : producteur-consommateur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    @Component("faxPile")
    @Scope(value = BeanDefinition.SCOPE_SINGLETON)
    public class FaxPile implements IFaxPile {
     
        private List<IFaxModel> faxsPile;
        private FaxThread faxThread;
     
     
        public FaxPile() {
            faxsPile = new ArrayList<IFaxModel>();
            System.out.println("FaxPile = "+this);
            start();
        }
     
        public void start() {
            if (getFaxThread() == null) {
                setFaxThread(new FaxThread());
                getFaxThread().setDaemon(true);
                getFaxThread().start();
                System.out.println("<<<<< FaxThread Started >>>>>>");
            }
        }
     
        public class FaxThread extends Thread {
            @Override
            public void run() {
                System.out.println("<<<<< FaxThread starting >>>>>>");
                while (!isInterrupted()) {
                    try {
                        IFaxModel faxModel = getFax();
                        getFaxManager().generateFax(faxModel);
                    } catch (InterruptedException e) {
                        getMailService().sendException(e);
                    }
                }
                System.out.println("<<<<< FaxThread Stopped >>>>>>")     getMailService().sendMailSystemError(IFaxResult.TypeError.INTERRUPTED_ERROR);
            }
        }
    }

    J'ai donc crée une classe FaxPile qui lance un thread. Ce thread va récupérer les valeurs à "consommer" dans une liste.
    Mon problème est que bien que j'ai mis l'annotation
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    @Scope(value = BeanDefinition.SCOPE_SINGLETON)
    Ma classe FaxPile est instanciée plusieurs fois...


    Lorsque qu'on l'on definit singleton=true ou scope=singleton dans les dernières versions de Spring, on considère que le Bean n'est justement PAS sous forme de Singleton, c'est le conteneur léger Spring qui ne va instancier qu'une seule fois le bean, il sera unique mais rien n'empécherai en théorie d'instancier par ailleurs ce bean.
    Au risque de passer pour un noobs ( ce que je suis ceci dit) pourrait on m'expliquer cette nuance entre l'instanciation par le conteneur léger de spring et l'instanciation " par ailleurs [de] ce bean."

    Si j'ai bien compris les explications de JeanDavidKevin, mon annotation ne suffit pas à faire en sorte que la classe ne soit instancié qu'une seule fois dans mon application ?

    - Comment puis y remedier car cette classe FaxPile contenant la liste des objets a consommer est référencée (par injection ) dans plusieurs autres classes (il faut donc que ma classe FaxPile soit la même partout)

    - De plus le lancement du thread est fait dans le constructeur de ma classe FaxPile. Est une erreur ?

    Merci d'avance pour vos réponses
    a+
    Medice Cura Te Ipsum

  2. #2
    Expert éminent
    Avatar de djo.mos
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    4 666
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2004
    Messages : 4 666
    Points : 7 679
    Points
    7 679
    Par défaut
    Bonjour,
    Citation Envoyé par Hyperion99 Voir le message
    Au risque de passer pour un noobs ( ce que je suis ceci dit) pourrait on m'expliquer cette nuance entre l'instanciation par le conteneur léger de spring et l'instanciation " par ailleurs [de] ce bean."
    Quand c'est Spring qui instancie les beans, il tient compte des règles que t'as défini (singleton ou pas, les champs à renseigner pour que le bean soit prêt, etc.).
    Quand c'est toi qui instancie le bean via un new, rien ne t'oblige à respecter les régles destinées à Spring

    Autre chose : l'instantiation faite par Spring se fait généralement derrière les scènes (tu l'invoques pas explicitement).


    Si j'ai bien compris les explications de JeanDavidKevin, mon annotation ne suffit pas à faire en sorte que la classe ne soit instancié qu'une seule fois dans mon application ?
    Seulement si c'est Spring qui l'instancie. Rien ne t'empêche de faire 15000 instances avec new.

    - Comment puis y remedier car cette classe FaxPile contenant la liste des objets a consommer est référencée (par injection ) dans plusieurs autres classes (il faut donc que ma classe FaxPile soit la même partout)
    T'as rien à faire, le problème n'existe pas. Si effectivement tu indiques à Spring d'injecter FaxPile dans plusieurs autres beans, tu finiras avec une seule isntance de FaxPile.
    Après, si ça marche pas dans ton cas, montres nous les autres classes qui dépendent de FaxPile ainsi que ta config Spring (et le code de bootstrapping de spring).

    - De plus le lancement du thread est fait dans le constructeur de ma classe FaxPile. Est une erreur ?
    ça compile et ça exécute, mais c'est considéré une mauvaise pratique. Un constructeur est fait, comme son nom l'indique, pour construire une instance, pas pour induire un comportement.

  3. #3
    Nouveau membre du Club Avatar de Hyperion99
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    62
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mai 2006
    Messages : 62
    Points : 35
    Points
    35
    Par défaut
    Salut djo.mos

    tout d'abord merci pour tes précision qui me permettent de commencer à mieux comprendre tout ça ...

    En fait j'ai résolu (contourné ??) mon problème en utilisant la technique de Baptiste Wicht (http://blog.developpez.com/wichtoune...e-exhaustive-/ qui permet de configurer Spring via une classe et non plus un fichier xml (je suis pas fan du tout du xml )

    de cette manière j'ai ma classe de configuration suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
     
     
    @Configuration(defaultLazy = Lazy.TRUE)
    @ComponentScan("fax")
    @ImportXml(locations = "applicationContext-fax2u-test.xml")
    @AnnotationDrivenTx(transactionManager = "transactionManager")
     
    public class ApplicationConfigTest {
     
        @Bean
        public FaxPile faxPile() {
            FaxPile faxPile=  FaxPile.createInstance();
            faxPile.start();
            return faxPile;
        }
     
        @Bean
        public ILogService logService() {
            return new LogService();
        }
     
        @Bean
        public IFaxTools faxTools() {
            return new FaxTools();
        }
     
        @Bean
        public IFaxService faxServiceTarget() {
            return new FaxService();
        }
     
        @Bean
        public IFaxDao faxDao() {
            return new FaxDaoImpl(sessionFactory());
        }
     
        @Bean
        public IFileManager fileManager() {
            return new FileManager();
        }
     
        @Bean
        public IJobHandler jobHandler() {
            return new JobHandler();
        }
     
        @Bean
        public IMailService mailService() {
            return new MailService();
        }
     
        @Bean
        public FaxServiceInterceptor faxServiceInterceptor() {
            return new FaxServiceInterceptor();
        }
     
        @Bean
        public ProxyFactoryBean faxService() {
            ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
            try {
                proxyFactoryBean.setProxyInterfaces(new Class[]{IFaxService.class});
                proxyFactoryBean.setTarget(faxServiceBean());
                proxyFactoryBean.setInterceptorNames(new String[]{"faxServiceInterceptor"});
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return proxyFactoryBean;
        }
     
     
        public TransactionProxyFactoryBean faxServiceBean() {
            TransactionProxyFactoryBean transactionProxyFactoryBean = transactionProxy();
            transactionProxyFactoryBean.setTarget("faxServiceTarget");
            transactionProxyFactoryBean.setTransactionAttributeSource(new AnnotationTransactionAttributeSource());
            return transactionProxyFactoryBean;
        }
     
     
        @Bean
        public HibernateTransactionManager transactionManager() {
            HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager();
            hibernateTransactionManager.setSessionFactory(sessionFactory());
            return hibernateTransactionManager;
        }
     
     
        public TransactionProxyFactoryBean transactionProxy() {
            TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
            transactionProxyFactoryBean.setTransactionManager(transactionManager());
            Properties properties = new Properties();
            properties.put("*", "PROPAGATION_REQUIRED");
            transactionProxyFactoryBean.setTransactionAttributes(properties);
            return transactionProxyFactoryBean;
        }
     
        @Bean(scope = "singleton")
        public SessionFactory sessionFactory() {
            AnnotationSessionFactoryBean annotationSessionFactoryBean = new AnnotationSessionFactoryBean();
            annotationSessionFactoryBean.setConfigLocation(new ClassPathResource("test.hibernate.cfg.xml"));
            Properties props = new Properties();
            props.put("hibernate.c3p0.autoCommitOnClose", true);
            props.put("hibernate.autoCommitOnClose", true);
            props.put("hibernate.autocommit", true);
            props.put("hibernate.connection.release_mode", "auto");
            props.put("hibernate.transaction.auto_close_session", true);
            annotationSessionFactoryBean.setHibernateProperties(props);
            try {
                annotationSessionFactoryBean.afterPropertiesSet();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return annotationSessionFactoryBean.getConfiguration().buildSessionFactory();
        }
    }
    L'avantage de cette technique (si tu peux me confirmer que c'est réellement un avantage au sens "bonne pratique de la théorie" ) c'est que ma classe FaxPile accéssible via le bean faxPile, définie de la manière suivante, ne peut être instancié qu'une seule fois

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
         @Bean
        public FaxPile faxPile() {
            FaxPile faxPile=  FaxPile.createInstance();
            faxPile.start();
            return faxPile;
        }
    avec un la classe FaxPile suivante

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    @Scope(value = BeanDefinition.SCOPE_SINGLETON)
    public class FaxPile implements IFaxPile {
     
        protected static FaxPile instance;
     
        private List<IFaxModel> faxsPile;   
        private FaxThread faxThread;
     
         private FaxPile() {
         }
     
        public static FaxPile createInstance() {
            if (instance == null) {
                instance = new FaxPile();
                System.out.println("FaxPile = " + instance);
            }
            return instance;
        }
     
         public void start() {
            if (getFaxThread() == null) {
                setFaxThread(new FaxThread());
                getFaxThread().setDaemon(true);
                getFaxThread().start();
                System.out.println("<<<<< FaxThread Started >>>>>>");
            }
        }
     
        public class FaxThread extends Thread {
            @Override
            public void run() {
                System.out.println("<<<<< FaxThread starting >>>>>>");
                while (!isInterrupted()) {
                    try {
                        IFaxModel faxModel = getFax();
                        getFaxManager().generateFax(faxModel);
                    } catch (InterruptedException e) {
     
                        getMailService().sendException(e);
                    }
                }
                System.out.println("<<<<< FaxThread Stopped >>>>>>");          getMailService().sendMailSystemError(IFaxResult.TypeError.INTERRUPTED_ERROR);
            }
        }
     
    [...]
    }
    Mon bean faxPile est injecté dans d'autre classe de la manière suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public class FaxService implements IFaxService {
     
        private IFaxPile faxPile;
     
        @Autowired
        public void setFaxPile(@Qualifier("faxPile") IFaxPile faxPile) {
            this.faxPile = faxPile;
        }
    Ce qui m'embête un peu, c'est que c'est que j'ai du créer explicitement dans la classe FaxPile la méthode getInstance et rendre le constructeur privé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
     private FaxPile() {
        }
     
        public static FaxPile createInstance() {
            if (instance == null) {
                instance = new FaxPile();
                System.out.println("FaxPile = " + instance);
            }
            return instance;
        }
    Mais peut être est le prix à payer pour pouvoir configurer spring via une classe et non via un fichier xml ?

    De plus (j'en profite ) avec cette méthode de configuration de spring je n'arrive pas à utiliser les interceptors (cf post "Propagation en Full annotation" http://www.developpez.net/forums/d80...n/#post4672943

    En gros pour acceder à mon service faxService Je suis obligé d'appeler le bean faxServiceTarget
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     @Bean
        public IFaxService faxServiceTarget() {
            return new FaxService();
        }
    je n'arrive pas à utiliser directement le bean "faxService" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    @Bean
        public ProxyFactoryBean faxService() {
            ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
            try {
                proxyFactoryBean.setProxyInterfaces(new Class[]{IFaxService.class});
                proxyFactoryBean.setTarget(faxServiceBean());
                proxyFactoryBean.setInterceptorNames(new String[]{"faxServiceInterceptor"});
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return proxyFactoryBean;
        }
     
     
        public TransactionProxyFactoryBean faxServiceBean() {
            TransactionProxyFactoryBean transactionProxyFactoryBean = transactionProxy();
            transactionProxyFactoryBean.setTarget("faxServiceTarget");
            transactionProxyFactoryBean.setTransactionAttributeSource(new AnnotationTransactionAttributeSource());
            return transactionProxyFactoryBean;
        }
     
     @Bean
        public FaxServiceInterceptor faxServiceInterceptor() {
            return new FaxServiceInterceptor();
        }
    avec mon interceptor

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public class FaxServiceInterceptor implements MethodInterceptor {
     
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            return methodInvocation.proceed();
     
            }
    }
    cela me lève une exception :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: No BeanFactory available anymore (probably due to serialization) - cannot resolve interceptor names [faxServiceInterceptor]


    Merci pour ton aide et tes précieux conseilles !
    Medice Cura Te Ipsum

  4. #4
    Expert éminent
    Avatar de djo.mos
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    4 666
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2004
    Messages : 4 666
    Points : 7 679
    Points
    7 679
    Par défaut
    L'avantage de cette technique (si tu peux me confirmer que c'est réellement un avantage au sens "bonne pratique de la théorie" ) c'est que ma classe FaxPile accéssible via le bean faxPile, définie de la manière suivante, ne peut être instancié qu'une seule fois
    Hum ... oui et non.
    Tout dépend de la sémantique de FaxPile et de tes besoins futurs.
    Y'a des classes qui sont intrinsèquement singleton, genre une DatabaseManager par exemple qui contralise l'accès à la BDD, je ne sais pas si c'est le cas pour FaxPile.

    Et que va il arriver si dans le futur tu auras besoin de gérer plusieurs piles de fax ?

    Bref, perso, je préfère utiliser Spring et pas mes classes pour contrôler l'aspect singleton ou pas.

    Autre note : ta classes FaxPile n'est en aucun cas garantie d'être un singleton dans le cas d'accès concurrent.
    Une façon plus solide de gérer ça serait de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    private static final FaxPile INSTANCE=new FaxPile;
     
         private FaxPile() {
         }
     
        public static FaxPile getInstance() {
            return INSTANCE;
        }
    Mais tu perds l'aspect lazy avec cette méthode. Y'a aussi une autre façon (beacoup plus compliquée) pour créer un singleton bulletproof et de façon lazy (fais signe si ça t'intéresse).

    Sinon, n'ayant jamais utilisé Spring Java Config, je ne peux pas t'aider sur ce point.

  5. #5
    Nouveau membre du Club Avatar de Hyperion99
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    62
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mai 2006
    Messages : 62
    Points : 35
    Points
    35
    Par défaut
    Et que va il arriver si dans le futur tu auras besoin de gérer plusieurs piles de fax ?
    Dans mon cas cela ne risque pas d'arriver mais effectivement dans le cas d'autres projets le problème sera d'actualité

    Mais tu perds l'aspect lazy avec cette méthode. Y'a aussi une autre façon (beacoup plus compliquée) pour créer un singleton bulletproof et de façon lazy (fais signe si ça t'intéresse).
    Chaque chose en son temps, si j'arrive déjà a finir ce projet cela sera un bon début (mais j'espère bien revenir vers toi lorsque le moment sera venu ...)

    J'ai bien pris note de ta remarque concernant la création de mon instance faxpile et t'en remercie !

    Bref, perso, je préfère utiliser Spring et pas mes classes pour contrôler l'aspect singleton ou pas.
    Comment faire cela alors ??? (quitte à ne pas utiliser spring java config ...)
    Medice Cura Te Ipsum

Discussions similaires

  1. [Web Services] [Axis]déployer un WS avec Spring et axis
    Par totoranky dans le forum Spring
    Réponses: 4
    Dernier message: 29/05/2007, 14h40
  2. Créer son bean de config avec Spring
    Par progamer54 dans le forum Spring
    Réponses: 1
    Dernier message: 10/04/2007, 17h02
  3. [Data] utiliser Hibernate avec Spring
    Par badi082 dans le forum Spring
    Réponses: 4
    Dernier message: 13/03/2007, 09h40
  4. Réponses: 5
    Dernier message: 12/05/2006, 22h02
  5. [Framework] HelloWorld avec Spring AOP
    Par LaJavanaise dans le forum Spring
    Réponses: 8
    Dernier message: 03/03/2006, 10h16

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo