2010-11-10 17 views
1

j'ai écrit cet exemple simple:printemps et @transactional, est-ce normal?

//file TestController.java 
public interface TestController { 

    public List<Test> findAll(); 

} 

//file TestControllerImp.java 
@Controller 
public class TestControllerImp implements TestController{ 

    @Autowired 
    private SessionFactory sessionFactory; 

    public void setSessionFactory(SessionFactory sessionFactory) { 
     this.sessionFactory=sessionFactory; 
    } 

    public List<Test> findAll() { 
     return sessionFactory.getCurrentSession().createQuery("from Test").list(); 
    } 

} 

//file TestService.java 
@Service 
public class TestService { 

    @Autowired 
    private TestController controller; 

    public boolean flag=true; 

    public void setController(TestController controller){ 
     this.controller=controller; 
    } 

    @Transactional 
    public List<Test> useController(){ 
     flag=false; 
     return controller.findAll(); 
    } 

} 

Et voici mon essai:

TestService s1=context.getBean(TestService.class); 
TestService s2=context.getBean(TestService.class); 
List<Test> list=s1.useController(); 
System.out.println(s1.flag+" "+s2.flag); 

Maintenant, le comportement étrange (im très nouveau avec ressort):

  1. Si je déclare @Transactional la méthode "useController()", la sortie est: true vrai
  2. Si je me déplace @Transactional de TestService à TestControllerImp, et je déclare "findAll()" avec @Transactional, la sortie est: false false.

Pourquoi j'ai ce comportement? Je sais par défaut @Autowired classes sont singletone, mais pourquoi dans le premier cas, le drapeau reste toujours vrai?

Merci à tous.

Répondre

5

Le mécanisme @Transactional fonctionne sur les proxys JDK par défaut et ceux-ci ne fonctionnent que sur les interfaces. Donc si vous laissez TestService être une interface et TestServiceImpl être son implémentation, alors le code ci-dessus devrait fonctionner.

par exemple. modifier la déclaration de classe à ceci:

@Service 
public class TestServiceImpl implements TestService { 

mais le code d'essai doit faire référence à l'interface, pas la classe:

// this code remains unchanged 
TestService s1=context.getBean(TestService.class); 
TestService s2=context.getBean(TestService.class); 

Référence:

+0

Alors @Transactional ne fonctionne que si vous l'utilisez dans une implémentation d'interface? – chzbrgla

+0

Oui, sauf si vous utilisez ''. Puis CGLIB sera utilisé pour créer des sous-classes dynamiques –

+0

@seanizer: Merci, donc pour chaque classe de service, je dois écrire l'interface et sa mise en œuvre? Puis-je changer le mécanisme @Transactional par défaut? Vous dites travailler sur JDK mais débogage s1 et s2 je vois TestService $$ EnhancerByCGLIB $$ 51486891 donc je pense que cela fonctionne sur CGLIB à la place, n'est pas vrai? – blow