Spring @Transactional özniteliği özel bir yöntemle çalışır mı?


196

Bir varsa @Transactional bir bahar fasulye özel bir yöntem üzerinde -annotation, ek açıklama bir etkiye sahip midir?

Eğer @Transactionalek açıklama bir kamu yönteme, bu işler ve hareketi açabilir.

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

  }
}

...

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

Yanıtlar:


163

Soru özel veya herkese açık değildir, soru şudur: Nasıl başlatılır ve hangi AOP uygulamasını kullanırsınız!

(Varsayılan) Spring Proxy AOP kullanırsanız, Spring (gibi @Transational) tarafından sağlanan tüm AOP işlevleri yalnızca çağrı proxy'den geçerse dikkate alınır. - Bu, açıklamalı yöntem başka bir fasulyeden çağrıldığında normalde geçerlidir .

Bunun iki anlamı vardır:

  • Özel yöntemler başka bir fasulyeden çağrılmaması gerektiğinden (istisna yansımadır), @TransactionalEk Açıklamaları dikkate alınmaz.
  • Yöntem herkese açık, ancak aynı fasulyeden çağrılırsa, bu da dikkate alınmaz (bu ifade yalnızca (varsayılan) Yay Proxy AOP kullanılıyorsa doğrudur).

@See Spring Referansı: Bölüm 9.6 9.6 Proxy mekanizmaları

IMHO, sorunun üstesinden gelecek olan Spring Proxy'ler yerine aspectJ modunu kullanmalısınız. Ve AspectJ İşlemsel Yönler özel yöntemlere bile dokunur (Bahar 3.0 için kontrol edilir).


4
Her iki nokta da mutlaka doğru değildir. Özel yöntemler - İlk yanlıştır olabilir Düşünceli çağrılabilir, ancak vekil bunu değil mantık tercihleri de keşfetme. İkinci nokta sadece arayüz tabanlı JDK proxy'leri için geçerlidir, CGLIB alt sınıf tabanlı proxy'ler için geçerli değildir.
skaffman

@skaffman: 1 - ifademi daha kesin hale getiriyorum, 2. Ama varsayılan Proxy, Arayüz tabanlı - değil mi?
Ralph

2
Bu, hedefin arabirim kullanıp kullanmamasına bağlıdır. Aksi takdirde CGLIB kullanılır.
skaffman

bana rezon veya cglib neden olamaz ama aspectj bazı referans söyleyebilir?
phil

1
Spring Proxies'i [varsayılan ortam] kullanmak istiyorsanız, yanıt bloğundaki bağlantıdan referans olarak doStuff () öğesine ek açıklama koyun ve ((Bean) AopContext.currentProxy ()) kullanarak doPrivateStuff () öğesini çağırın. DoPrivateStuff (); Çoğaltma [varsayılan ortam] yeniden oluşturulmuşsa, her iki yöntemi de aynı işlemde yürütür.
Michael Ouyang

219

Sorunuzun cevabı hayır - @Transactionalözel yöntemlere açıklama eklemek için kullanılırsa hiçbir etkisi olmayacaktır. Proxy oluşturucu onları yok sayar.

Bu, Bahar Kılavuzu bölüm 10.5.6'da belgelenmiştir :

Yöntem görünürlüğü ve @Transactional

Proxy'leri kullanırken @Transactionalek açıklamayı yalnızca genel görünürlüğü olan yöntemlere uygulamalısınız . Ek açıklama ile korumalı, özel veya paket tarafından görülebilir yöntemlere açıklama eklerseniz @Transactionalhata oluşmaz, ancak ek açıklama yöntemi yapılandırılmış işlem ayarlarını göstermez. Herkese açık olmayan yöntemlere açıklama eklemeniz gerekiyorsa AspectJ (aşağıya bakın) kullanımını düşünün.


Bundan emin misin? Bir fark yaratmasını beklemem.
willcodejavaforfood

proxy stilinin Cglib olup olmadığına ne dersiniz?
lily

32

Varsayılan olarak @Transactionalöznitelik yalnızca applicationContext öğesinden alınan bir başvuruda açıklamalı bir yöntem çağrıldığında çalışır.

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

  }
}

Bu bir işlem açar:

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

Bu olmayacak:

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

Yay Referansı: @Transactional Kullanma

Not: Proxy modunda (varsayılan değerdir), yalnızca proxy üzerinden gelen 'harici' yöntem çağrıları durdurulur. Bu, "kendini çağırma" ifadesinin, yani hedef nesnenin içinde, hedef nesnenin başka bir yöntemini çağıran bir yöntemin, çağrılan yöntemle işaretlenmiş olsa bile, çalışma zamanında gerçek bir işleme yol açmayacağı anlamına gelir @Transactional!

Kendi kendine çağırma işlemlerinin de işlemlerle sarılmasını bekliyorsanız AspectJ modunu (aşağıya bakın) kullanmayı düşünün. Bu durumda, ilk etapta proxy olmayacak; bunun yerine, @Transactionalherhangi bir yöntemde çalışma zamanı davranışına dönüştürmek için hedef sınıf 'örülecektir' (yani bayt kodu değiştirilecektir) .


Şunu mu demek istedin bean = new Bean () ;?
willcodejavaforfood

Hayır! Yeni Bean () ile fasulye oluşturursam, açıklama en azından Aspect-J kullanılmadan asla çalışmayacaktır.
Juha Syrjälä

2
Teşekkürler! Bu gözlemlediğim garip davranışı açıklıyor. Oldukça counter sezgisel bu dahili yöntem çağırma kısıtlaması ...
manuel aldana

Ben "proxy üzerinden gelen sadece dış yöntem çağrıları kesilecek" öğrendim zor yoldan
18:18

13

Evet, @Tacacactional'ı özel yöntemlerde kullanmak mümkündür, ancak diğerlerinin de belirttiği gibi bu kutudan çıkmaz. AspectJ kullanmanız gerekir. Nasıl çalıştıracağımı anlamak biraz zaman aldı. Sonuçlarımı paylaşacağım.

Yükleme zamanı dokuma yerine derleme zamanı dokuma kullanmayı tercih ettim çünkü bence genel olarak daha iyi bir seçenek. Ayrıca, Java 8 kullanıyorum, bu yüzden bazı parametreleri ayarlamanız gerekebilir.

İlk olarak, aspectjrt bağımlılığını ekleyin.

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

Sonra Maven gerçek baytkod dokuma yapmak için AspectJ eklentisi ekleyin (bu minimal bir örnek olmayabilir).

<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>

Son olarak bunu yapılandırma sınıfınıza ekleyin

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)

Şimdi @Transactional'ı özel yöntemlerde kullanabilmelisiniz.

Bu yaklaşım için bir uyarı: IDE'nizi AspectJ'den haberdar olacak şekilde yapılandırmanız gerekecektir, aksi takdirde uygulamayı Eclipse aracılığıyla çalıştırırsanız çalışmayabilir. Bir sağlık kontrolü olarak doğrudan bir Maven yapısına karşı test ettiğinizden emin olun.


proxy yöntemi cglib ise, yöntemin herkese açık olması gereken bir arabirim uygulamaya gerek yoktur, o zaman özel yöntemlerde @ Transactional kullanabilir mi?
lily

Evet, özel yöntemlerle ve arayüzler olmadan çalışıyor! AspectJ düzgün yapılandırıldığı sürece, temel olarak çalışma yöntemi dekoratörlerini garanti eder. Ve user536161 cevabında, kendi kendine çağrılmalarda bile çalışacağına dikkat çekti. Gerçekten harika ve sadece biraz korkutucu.
James Watkins

12

Özel bir yöntemi bir işlemin içine sarmanız gerekiyorsa ve aspectj kullanmak istemiyorsanız, TransactionTemplate'i kullanabilirsiniz .

@Service
public class MyService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    private void process(){
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                processInTransaction();
            }
        });

    }

    private void processInTransaction(){
        //...
    }

}

TransactionTemplateKullanımı göstermek güzel ama lütfen bunun ..RequiresTransactionyerine ikinci yöntemi çağırın ..InTransaction. Her zaman bir yıl sonra nasıl okumak istediğinizi söyleyin. Ayrıca gerçekten ikinci bir özel yöntem gerektiriyorsa düşünmek istiyorum: İçeriğini doğrudan anonim executeuygulamaya koyun veya dağınık hale gelirse, uygulamanın açıklama ekleyebileceğiniz başka bir hizmete bölünmesinin bir göstergesi olabilir @Transactional.
Stuck

@Stuck, 2. yöntem gerçekten gerekli değildir, ancak özel bir yönteme bir bahar işlemi nasıl uygulanacağı orijinal soruyu cevaplar
loonis

evet, cevabı zaten onayladım, ancak nasıl uygulanacağına dair bazı bağlamları ve düşünceleri paylaşmak istedim, çünkü mimari açıdan bu durumun bir tasarım kusuru için potansiyel bir gösterge olduğunu düşünüyorum.
Stuck

5

Spring Docs bunu açıklıyor

Proxy modunda (varsayılan değerdir), yalnızca proxy üzerinden gelen harici yöntem çağrıları durdurulur. Bu, kendini çağırma işlevinin, aslında, hedef nesnenin içinde, hedef nesnenin başka bir yöntemini çağıran bir yöntemin, çağrılan yöntem @Transactional ile işaretlenmiş olsa bile çalışma zamanında gerçek bir işleme yol açmayacağı anlamına gelir.

Kendi kendine çağırma işlemlerinin de işlemlerle sarılmasını bekliyorsanız, AspectJ modunun kullanımını düşünün (aşağıdaki tablodaki mod özniteliğine bakın). Bu durumda, ilk etapta vekil olmayacak; bunun yerine, hedef sınıf, @Tacacactional'ı herhangi bir yöntemde çalışma zamanı davranışına dönüştürmek için örülecektir (yani bayt kodu değiştirilecektir).

Başka bir yol kullanıcı BeanSelfAware


referans ekleyebilir misiniz BeanSelfAware? Bir Spring'in sınıfına
benzemiyor

@asgs Farz edin ki, kendi kendine enjeksiyonla ilgilidir (bir çekirdeğe proxy'ye sarılmış bir örneği sağlayın). Örnekleri stackoverflow.com/q/3423972/355438 adresinde görebilirsiniz .
Lu55


1

@ Loonis'in TransactionTemplate'i kullanması önerilen şekilde, bu yardımcı bileşen (Kotlin) kullanılabilir:

@Component
class TransactionalUtils {
    /**
     * Execute any [block] of code (even private methods)
     * as if it was effectively [Transactional]
     */
    @Transactional
    fun <R> executeAsTransactional(block: () -> R): R {
        return block()
    }
}

Kullanımı:

@Service
class SomeService(private val transactionalUtils: TransactionalUtils) {

    fun foo() {
        transactionalUtils.executeAsTransactional { transactionalFoo() }
    }

    private fun transactionalFoo() {
        println("This method is executed within transaction")
    }
}

TransactionTemplateMevcut işlemi tekrar kullanıp kullanmadığınızı bilmiyorum ama bu kod kesinlikle yapıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.