2010-12-09 44 views
145

Si j'ai une annotation @Transactional sur une méthode privée dans un bean Spring, l'annotation a-t-elle un effet?Est-ce que l'attribut Spring @Transactional fonctionne sur une méthode privée?

Si l'annotation @Transactional est une méthode publique, cela fonctionne et ouvre une transaction.

public class Bean { 
    public void doStuff() { 
    doPrivateStuff(); 
    } 
    @Transactional 
    private void doPrivateStuff() { 

    } 
} 

... 

Bean bean = (Bean)appContext.getBean("bean"); 
bean.doStuff(); 

Répondre

172

La réponse à votre question est non - @Transactional aura aucun effet si elle est utilisée pour annoter des méthodes privées. Le générateur de proxy les ignorera.

Ceci est documenté dans Spring Manual chapter 10.5.6:

visibilité de la méthode et @Transactional

Lors de l'utilisation des procurations, vous devez appliquer l'annotation @Transactional uniquement aux méthodes avec une visibilité publique. Si -vous annoter protégées, des méthodes privées ou package visible avec l'annotation @Transactional, aucune erreur est soulevée, mais la méthode annotée ne présente pas les paramètres configurés transactionnels. Considérez l'utilisation d'AspectJ (voir ci-dessous) si vous avez besoin de pour annoter des méthodes non publiques.

+0

Êtes-vous sûr de cela? Je ne m'attendrais pas à ce que cela fasse une différence. – willcodejavaforfood

+27

Cela fonctionnera avec mode = "aspectj" cependant. – waxwing

+1

@willcodejavaforfood: Voir la citation – skaffman

3

La réponse est non. S'il vous plaît voir Spring Reference: Using @Transactional :

L'annotation @Transactional peut être placé avant une définition d'interface, une méthode sur une interface, une définition de classe, ou une méthode publique sur une classe

23

Par défaut, le L'attribut @Transactional ne fonctionne que lors de l'appel d'une méthode annotée sur une référence obtenue à partir de applicationContext.

public class Bean { 
    public void doStuff() { 
    doTransactionStuff(); 
    } 
    @Transactional 
    public void doTransactionStuff() { 

    } 
} 

Cela ouvrira une transaction:

Bean bean = (Bean)appContext.getBean("bean"); 
bean.doTransactionStuff(); 

Ce ne sera pas:

Bean bean = (Bean)appContext.getBean("bean"); 
bean.doStuff(); 

Spring Reference: Using @Transactional

Remarque: En mode proxy (qui est la valeur par défaut), seuls les appels de méthode «externes» venant à travers le proxy être intercepté. Cela signifie que l'auto-invocation, c'est-à-dire une méthode dans l'objet cible appelant une autre méthode de l'objet cible, n'entraînera pas une transaction réelle au moment de l'exécution même si la méthode invoquée est marquée @Transactional! Envisagez l'utilisation du mode AspectJ (voir ci-dessous) si vous vous attendez à ce que les auto-invocations soient également entourées de transactions. Dans ce cas, il n'y aura pas de proxy en premier lieu; à la place, la classe cible sera «tissée» (c'est-à-dire que son code d'octet sera modifié) afin de transformer @Transactional en comportement d'exécution sur tout type de méthode.

+0

Voulez-vous dire bean = new Bean() ;? – willcodejavaforfood

+0

Non. Si je crée des beans avec new Bean(), l'annotation ne fonctionnera jamais au moins sans utiliser Aspect-J. –

+1

merci! Ceci explique le comportement étrange que j'observais. Tout à fait contre-intuitif cette restriction d'invocation de méthode interne ... –

3

Plese voir cette doc:

En mode proxy (qui est la valeur par défaut), seule méthode externe les appels entrants par le proxy sont interceptés. Cela signifie que l'auto-invocation, en effet, une méthode dans l'objet cible appelant une autre méthode de l'objet cible, ne mènera pas à une transaction réelle au moment de l'exécution même si la méthode invoquée est marquée avec @Transactional. Pensez à l'utilisation du mode AspectJ (voir l'attribut de mode dans le tableau ci-dessous) si vous pensez que les auto-invocations seront également encapsulées dans des transactions. Dans ce cas, il n'y aura pas de proxy en premier lieu; à la place, la classe cible sera tissée (c'est-à-dire que son code d'octet sera modifié) afin de transformer @Transactional en comportement d'exécution sur tout type de méthode.

---------------------------- sur

donc, anthères manière est facile à utiliser BeanSelfAware

84

La Question n'est pas privée ou publique, la question est: Comment est-elle invoquée et quelle implémentation AOP vous utilisez!

Si vous utilisez (par défaut) Spring Proxy AOP, toutes les fonctionnalités AOP fournies par Spring (comme @Transational) ne seront prises en compte que si l'appel passe par le proxy. - Ceci est normalement le cas si la méthode annotée est appelée à partir de un autre bean.

Cela a deux conséquences:

  • Parce que les méthodes privées ne doivent pas être appelés à partir d'un autre bean (l'exception est la réflexion), leur @Transactional Annotation ne sont pas prises en compte.
  • Si la méthode est publique, mais qu'elle est invoquée à partir du même bean, elle ne sera pas non plus prise en compte (cette instruction n'est correcte que si (par défaut) Spring Proxy AOP est utilisé).

@see Spring Reference: Chapter 9.6 9.6 Proxying mechanisms

à mon humble avis, vous devez utiliser le mode AspectJ, au lieu du printemps proxies, qui permettra de surmonter le problème. Et les aspects transactionnels AspectJ sont tissés même dans des méthodes privées (vérifié pour Spring 3.0).

+0

gentil (+1) mais j'ai dû éditer l'orthographe et la grammaire –

+0

Les deux points ne sont pas nécessairement vrai. Le premier est incorrect - les méthodes privées * peuvent * être appelées de manière réfléchie, mais la logique de découverte du proxy choisit de ne pas le faire. Le second point n'est vrai que pour les proxys JDK basés sur l'interface, mais pas pour les proxies basés sur la sous-classe CGLIB. – skaffman

+0

@skaffman: 1 - Je rends ma déclaration plus précise, 2. Mais le proxy par défaut est basé sur l'interface - n'est-ce pas? – Ralph

8

Oui, il est possible d'utiliser @Transactional sur des méthodes privées, mais comme d'autres l'ont mentionné, cela ne marchera pas. Vous devez utiliser AspectJ. Il m'a fallu du temps pour comprendre comment le faire fonctionner. Je partagerai mes résultats. J'ai choisi d'utiliser le tissage à la compilation au lieu du tissage à temps de chargement parce que je pense que c'est une meilleure option globale. En outre, j'utilise Java 8, vous devrez peut-être ajuster certains paramètres.

D'abord, ajoutez la dépendance pour aspectjrt.

<dependency> 
    <groupId>org.aspectj</groupId> 
    <artifactId>aspectjrt</artifactId> 
    <version>1.8.8</version> 
</dependency> 

Ensuite, ajoutez le plugin AspectJ pour faire le tissage bytecode réelle Maven (ce ne peut pas être un exemple minimal).

<plugin> 
    <groupId>org.codehaus.mojo</groupId> 
    <artifactId>aspectj-maven-plugin</artifactId> 
    <version>1.8</version> 
    <configuration> 
     <complianceLevel>1.8</complianceLevel> 
     <source>1.8</source> 
     <target>1.8</target> 
     <aspectLibraries> 
      <aspectLibrary> 
       <groupId>org.springframework</groupId> 
       <artifactId>spring-aspects</artifactId> 
      </aspectLibrary> 
     </aspectLibraries> 
    </configuration> 
    <executions> 
     <execution> 
      <goals> 
       <goal>compile</goal> 
      </goals> 
     </execution> 
    </executions> 
</plugin> 

Enfin ajouter à votre classe de configuration

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ) 

Maintenant, vous devriez être en mesure d'utiliser @Transactional sur les méthodes privées. Une mise en garde à cette approche: Vous devrez configurer votre IDE pour être conscient de AspectJ sinon, si vous exécutez l'application via Eclipse par exemple, il peut ne pas fonctionner. Assurez-vous de tester par rapport à une construction Maven directe comme une vérification de santé mentale.