Bahar - @Transactional - Arka planda ne olur?


334

Bir yönteme açıklama eklediğinizde gerçekte ne olduğunu bilmek istiyorum @Transactional? Tabii ki, biliyorum ki Spring bu yöntemi bir İşlemde sarar.

Ancak, aşağıdaki şüphelerim var:

  1. Ben Bahar bir oluşturur duydum vekil sınıf ? Birisi bunu daha derinlemesine açıklayabilir mi ? O proxy sınıfta gerçekte ne var? Gerçek sınıfa ne olur? Ve Spring'in yarattığı proxy sınıfını nasıl görebilirim
  2. İlkbahar dokümanlarında şunları da okudum:

Not: Bu mekanizma proxy'lere dayandığından, 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!

Kaynak: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Neden yalnızca dış yöntem çağrıları kendi kendine çağrı yapma yöntemleri yerine İşlem altında olacak?


2
İlgili tartışma burada: stackoverflow.com/questions/3120143/…
dma_k

Yanıtlar:


255

Bu büyük bir konu. Bahar referans dokümanı, birden fazla bölüm ayırır. Spring'in bildirimsel işlem desteği, kuruluşunda AOP kullandığından , Unsur Odaklı Programlama ve İşlemler'i okumanızı tavsiye ederim .

Ancak çok yüksek bir seviyede Spring , sınıfın kendisi veya üyeleri üzerinde @Tacactional ilan eden sınıflar için proxy'ler oluşturur . Proxy çoğunlukla çalışma zamanında görünmez. Spring'in proxy uygulanan nesneye yöntem çağrılarından önce, sonra veya etrafındaki davranışları enjekte etmesinin bir yolunu sağlar. İşlem yönetimi, bağlanılabilecek davranışlara yalnızca bir örnektir. Güvenlik denetimleri başka bir yöntemdir. Ve günlük kaydı gibi şeyler için kendinizinkini de sağlayabilirsiniz. Bu nedenle, @Transactional ile bir yönteme açıklama eklediğinizde , Spring, dinamik olarak açıklama eklediğiniz sınıfla aynı arabirim (ler) i uygulayan bir proxy oluşturur. İstemciler nesnenize çağrı yaptığında, çağrılar durdurulur ve davranışlar proxy mekanizması aracılığıyla enjekte edilir.

EJB'deki işlemler bu arada benzer şekilde çalışır.

Gözlemlediğiniz gibi, proxy mekanizması yalnızca bazı harici nesnelerden çağrı geldiğinde çalışır. Nesnenin içinde dahili bir çağrı yaptığınızda , proxy'yi atlayan " bu " referans üzerinden gerçekten bir çağrı yaparsınız . Bununla birlikte, bu soruna geçici bir çözüm bulmanın yolları vardır. Proxy örneğini çalışma zamanında "kendini referans" sınıflarına enjekte etmek için bir BeanFactoryPostProcessor kullandığım bu forum sonrası bir yaklaşım açıklar . Bu başvuruyu " ben " adlı bir üye değişkene kaydediyorum . Sonra iş parçacığının işlem durumunda bir değişiklik gerektiren iç arama yapmak gerekirse, ben proxy (örneğin " me.someMethod ()".) Forum gönderisi daha ayrıntılı olarak açıklıyor. BeanFactoryPostProcessor kodunun şimdi Bahar 1.x zaman diliminde yazıldığı gibi biraz farklı olacağını unutmayın . Ama umarım size bir fikir verir. Muhtemelen müsait olabilirdim.


4
>> Proxy çoğunlukla çalışma zamanında görünmez Oh !! Onları görmeyi merak ediyorum :) Dinlen .. Cevabınız çok kapsamlıydı. Bu bana ikinci kez yardım ediyorsun .. Tüm yardım için teşekkürler.
peakit

17
Sorun değil. Bir hata ayıklayıcı ile adım attığınızda proxy kodunu görebilirsiniz. Muhtemelen bu en kolay yol. Sihir yok; onlar sadece Spring paketleri içindeki sınıflardır.
Rob H

Ve eğer @Transaction açıklama transactionalisation enjekte etmek dinamik vekil API'sini kullanır ve bahar bir arabirim uyguladığını sahiptir yöntem değil yakınlık kullanın. İşlemsel sınıflarımın her durumda arabirim uygulamasını tercih ederim.
Michael Wiles

1
“Ben” şemasını da buldum (düşündüğüm şekilde yapmak için açık bir kablo kullanarak), ancak bence bu şekilde yaparsanız, yeniden düzenlemeden daha iyi olursunuz. zorunda. Ama evet, bu bazen çok garip olabilir!
Donal Fellows

2
2019: Bu yanıt eskidi gibi, anılan forum sonrası zaman davayı tarif edersiniz ki artık mevcut nesnenin içinde dahili arama yapmak zorunda kalmadan , vekil atlayarak kullanarakBeanFactoryPostProcessor . Ancak, bu cevapta açıklanan (bence) çok benzer bir yöntem var: stackoverflow.com/a/11277899/3667003 ... ve tüm iş parçacığında da diğer çözümler.
Z3d4s

196

Spring, fasulye tanımlarınızı yüklediğinde ve @Transactionalek açıklamaları arayacak şekilde yapılandırıldığında , bu proxy nesnelerini gerçek çekirdeğiniz etrafında oluşturur . Bu proxy nesneleri, çalışma zamanında otomatik olarak oluşturulan sınıfların örnekleridir. Bir yöntem çağrıldığında bu proxy nesnelerinin varsayılan davranışı, yalnızca "hedef" çekirdeğinde (yani çekirdeğinizde) aynı yöntemi çağırmaktır.

Bununla birlikte, proxy'ler önleyicilerle de sağlanabilir ve mevcut olduğunda bu önleyiciler, hedef fasulyenizin yöntemini başlatmadan önce proxy tarafından çağrılacaktır. Ek açıklamalı hedef fasulye için @Transactional, Spring bir a TransactionInterceptoroluşturur ve oluşturulan proxy nesnesine iletir. Bu nedenle, yöntemi istemci kodundan çağırdığınızda, proxy nesnesindeki TransactionInterceptoryöntemi çağırırsınız. Çağırma bittiğinde, TransactionInterceptorişlem yapılır / geri alınır. İstemci koduna şeffaftır.

"Dış yöntem" şey gelince, eğer fasulye kendi yöntemlerinden birini çağırırsa, o zaman proxy üzerinden bunu yapmayacaktır. Unutmayın, Spring fasulyenizi proxy'ye sarar, fasulyeniz hakkında hiçbir bilgisi yoktur. Sadece çekirdeğinizin "dışından" gelen çağrılar vekilden geçer.

Bu yardımcı olur mu?


36
> Unutmayın, Spring fasulyenizi proxy'ye sarar, fasulyeniz hakkında hiçbir bilgisi yoktur Bu her
peakit

Proxy ve yakalayıcılar için harika bir açıklama. Şimdi bahar hedef fasulye çağrıları durdurmak için bir proxy nesne uygulamak anlamak. Teşekkür ederim!
dharag

Bahar belgelerinin bu resmini anlatmaya çalıştığınızı ve bu resmin bana çok yardımcı olduğunu düşünüyorum: docs.spring.io/spring/docs/4.2.x/spring-framework-reference/…
WesternGun

44

Görsel bir insan olarak, vekil kalıbın bir dizi diyagramıyla tartmayı seviyorum. Okları nasıl okuyacağınızı bilmiyorsanız, ilkini şu şekilde okurum: Clientyürütür Proxy.method().

  1. İstemci hedefe bakış açısından bir yöntem çağırır ve proxy tarafından sessizce durdurulur
  2. Önceki bir boyut tanımlanmışsa, proxy onu yürütür
  3. Ardından, gerçek yöntem (hedef) yürütülür
  4. Sonradan dönen ve sonradan atanan yöntem, yöntem döndükten sonra ve / veya yöntem bir istisna atarsa ​​yürütülen isteğe bağlı özelliklerdir
  5. Bundan sonra, proxy sonraki yönü çalıştırır (tanımlanmışsa)
  6. Sonunda proxy arayan istemciye geri döner

Proxy Desen Dizisi Diyagramı (Fotoğrafın kökenlerinden bahsetmiş olmam şartıyla yayınlanmasına izin verildi. Yazar: Noel Vaes, web sitesi: www.noelvaes.eu)


27

En basit cevap:

Hangi yöntemde olursa olsun @Transactional, işlemin sınırı başlar ve yöntem tamamlandığında sınır sona erer.

JPA çağrısı kullanıyorsanız, tüm işlem sınırları bu işlem sınırındadır .

Varlık1, varlık2 ve varlık3'ü kaydettiğinizi varsayalım. Şimdi varlık3 kaydedilirken bir istisna oluşur , sonra enitiy1 ve varlık2 aynı işleme girer, böylece varlık1 ve varlık2 varlık3 ile geri alınır.

İşlem:

  1. entity1.save
  2. entity2.save
  3. entity3.save

Herhangi bir istisna DB ile tüm JPA işlemlerinin geri alınmasına neden olur. Dahili JPA işlemi Spring tarafından kullanılır.


2
"A̶n̶y̶ istisnası, DB ile yapılan tüm JPA işlemlerinin geri alınmasına neden olacaktır." Not Yalnızca RuntimeException geri alma ile sonuçlanır. İşaretli istisnalar, geri dönüşle sonuçlanmaz.
Arjun

2

Geç olabilir, ancak proxy ile ilgili endişenizi açıklayan bir şeyle karşılaştım (yalnızca proxy üzerinden gelen 'harici' yöntem çağrıları kesilecek).

Örneğin, şuna benzer bir sınıfınız var

@Component("mySubordinate")
public class CoreBusinessSubordinate {

    public void doSomethingBig() {
        System.out.println("I did something small");
    }

    public void doSomethingSmall(int x){
        System.out.println("I also do something small but with an int");    
  }
}

ve bir yönü var, şuna benzer:

@Component
@Aspect
public class CrossCuttingConcern {

    @Before("execution(* com.intertech.CoreBusinessSubordinate.*(..))")
    public void doCrossCutStuff(){
        System.out.println("Doing the cross cutting concern now");
    }
}

Bu şekilde yürüttüğünüzde:

 @Service
public class CoreBusinessKickOff {

    @Autowired
    CoreBusinessSubordinate subordinate;

    // getter/setters

    public void kickOff() {
       System.out.println("I do something big");
       subordinate.doSomethingBig();
       subordinate.doSomethingSmall(4);
   }

}

Yukarıda verilen kodu yukarıdaki kickOff çağrısı sonuçları.

I do something big
Doing the cross cutting concern now
I did something small
Doing the cross cutting concern now
I also do something small but with an int

ancak kodunuzu

@Component("mySubordinate")
public class CoreBusinessSubordinate {

    public void doSomethingBig() {
        System.out.println("I did something small");
        doSomethingSmall(4);
    }

    public void doSomethingSmall(int x){
       System.out.println("I also do something small but with an int");    
   }
}


public void kickOff() {
  System.out.println("I do something big");
   subordinate.doSomethingBig();
   //subordinate.doSomethingSmall(4);
}

Gördüğünüz gibi, yöntem dahili olarak başka bir yöntemi çağırır, böylece yakalanmayacak ve çıktı aşağıdaki gibi görünecektir:

I do something big
Doing the cross cutting concern now
I did something small
I also do something small but with an int

Bunu yaparak bunu atlayabilirsiniz.

public void doSomethingBig() {
    System.out.println("I did something small");
    //doSomethingSmall(4);
    ((CoreBusinessSubordinate) AopContext.currentProxy()).doSomethingSmall(4);
}

Kod snippet'leri alınan adres: https://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy/


1

Mevcut tüm cevaplar doğrudur, ancak sadece bu karmaşık konuyu veremiyorum.

Kapsamlı, pratik bir açıklama için, çok sayıda kod örneği ile ~ 4000 basit kelimeyle işlem yönetimini kapsamak için elinden geleni yapan bu Spring @Transactional In-Depth kılavuzuna göz atmak isteyebilirsiniz .


Harikaydı ...
Alpit Anand
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.