क्या स्प्रिंग @ ट्रेंसेक्शनल विशेषता एक निजी पद्धति पर काम करती है?


196

अगर मैं एक है @Transactional एक वसंत सेम में एक निजी पद्धति पर -annotation, एनोटेशन कोई असर पड़ता है?

यदि @Transactionalएनोटेशन एक सार्वजनिक विधि पर है, तो यह काम करता है और एक लेनदेन खोलता है।

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

  }
}

...

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

जवाबों:


163

प्रश्न निजी या सार्वजनिक नहीं है, सवाल यह है: यह कैसे लागू किया गया है और आप किस AOP कार्यान्वयन का उपयोग करते हैं!

यदि आप (डिफ़ॉल्ट) स्प्रिंग प्रॉक्सी AOP का उपयोग करते हैं, तो स्प्रिंग द्वारा दिए गए सभी AOP कार्यक्षमता (जैसे @Transational) कॉल को प्रॉक्सी के माध्यम से जाने पर ही ध्यान में रखा जाएगा। - यह आम तौर पर मामला है यदि एनोटेट विधि को दूसरे सेम से लागू किया जाता है ।

इसके दो निहितार्थ हैं:

  • क्योंकि निजी तरीकों को किसी अन्य बीन से आह्वान नहीं किया जाना चाहिए (अपवाद प्रतिबिंब है), उनके @Transactionalएनोटेशन को ध्यान में नहीं रखा जाता है।
  • यदि विधि सार्वजनिक है, लेकिन इसे उसी बीन से प्राप्त किया जाता है, तो इसे ध्यान में नहीं लिया जाएगा (यह कथन केवल तभी सही है यदि (डिफ़ॉल्ट) स्प्रिंग प्रॉक्सी AOP का उपयोग किया जाता है)।

@See स्प्रिंग संदर्भ: अध्याय 9.6 9.6 आसन्न तंत्र

IMO में आपको स्प्रिंग प्रॉक्सिज़ की जगह, एसपीजे मोड का उपयोग करना चाहिए, जो समस्या को दूर करेगा। और AspectJ Transactional Aspects भी निजी तरीकों (बुना वसंत 3.0 के लिए जाँच) में बुने हुए हैं।


4
दोनों बिंदु जरूरी नहीं हैं। पहला गलत है - निजी तरीकों को प्रतिबिंबित किया जा सकता है, लेकिन प्रॉक्सी की खोज तर्क ऐसा नहीं करने का विकल्प चुनता है। दूसरा बिंदु केवल इंटरफ़ेस-आधारित JDK परदे के पीछे का सच है, लेकिन CGLIB उपवर्ग-आधारित प्रॉक्सी के लिए नहीं।
स्कफमैन

@skaffman: 1 - मैं अपनी प्रतिमा को अधिक सटीक बनाता हूं, 2. लेकिन डिफ़ॉल्ट प्रॉक्सी इंटरफ़ेस आधारित है - है ना?
राल्फ

2
यह निर्भर करता है कि लक्ष्य इंटरफेस का उपयोग करता है या नहीं। यदि यह नहीं है, CGLIB का उपयोग किया जाता है।
स्कैफ़मैन

canu मुझे अनुनाद या कुछ संदर्भ बता सकते हैं कि cglib, लेकिन एस्पज नहीं कर सकता है?
फिला

1
उत्तर ब्लॉक में लिंक से संदर्भ, यदि आप स्प्रिंग प्रॉक्सीज़ [डिफ़ॉल्ट वातावरण] का उपयोग करना चाहते हैं, तो doStuff () और कॉल doPStreetStuff () का उपयोग करके एनोटेशन डालें (बीन) AopContext.currentProxy ()) .PPPStStuff ()। यह एक ही लेन-देन में दोनों तरीकों को निष्पादित करेगा यदि प्रचार को फिर से तय किया गया है [डिफ़ॉल्ट वातावरण]।
माइकल Ouyang

219

यदि आपके प्रश्न का उत्तर नहीं है - @Transactionalनिजी तरीकों का उपयोग करने के लिए उपयोग किए जाने पर कोई प्रभाव नहीं पड़ेगा। प्रॉक्सी जनरेटर उन्हें अनदेखा करेगा।

यह स्प्रिंग मैनुअल अध्याय 10.5.6 में प्रलेखित है :

विधि दृश्यता और @Transactional

प्रॉक्सी का उपयोग करते समय, आपको @Transactionalएनोटेशन को केवल सार्वजनिक दृश्यता वाले तरीकों पर लागू करना चाहिए । यदि आप @Transactionalएनोटेशन के साथ संरक्षित, निजी या पैकेज-दृश्य विधियों को एनोटेट करते हैं, तो कोई त्रुटि नहीं उठाई जाती है, लेकिन एनोटेट विधि कॉन्फ़िगर की गई ट्रांसेक्शनल सेटिंग्स को प्रदर्शित नहीं करती है। यदि आपको गैर-सार्वजनिक विधियों को एनोटेट करने की आवश्यकता है, तो AspectJ (नीचे देखें) के उपयोग पर विचार करें।


क्या तुम इसके बारे में निश्चित हो? मैं इससे फर्क करने की उम्मीद नहीं करूंगा।
.codejavaforfood

कैसे के बारे में अगर proxying शैली Cglib है?
लिली

32

डिफ़ॉल्ट रूप से @Transactionalविशेषता केवल तभी काम करती है जब एप्लिकेशनकनेक्ट से प्राप्त संदर्भ पर एनोटेट पद्धति को कॉल किया जाता है।

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

  }
}

इससे एक लेन-देन खुलेगा:

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

यह नहीं होगा:

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

स्प्रिंग संदर्भ: @ ट्रांसट्रैशनल का उपयोग करना

नोट: प्रॉक्सी मोड में (जो कि डिफ़ॉल्ट है), प्रॉक्सी के माध्यम से आने वाली केवल 'बाहरी' विधि कॉल को इंटरसेप्ट किया जाएगा। इसका मतलब है कि 'सेल्फ-इनवोकेशन', यानी टारगेट ऑब्जेक्ट के भीतर एक ऐसी विधि जिसे टारगेट ऑब्जेक्ट का कोई और तरीका बताया जाता है, तब भी रनटाइम पर वास्तविक ट्रांजेक्शन नहीं होगा, भले ही इनवॉइस मेथड चिह्नित हो @Transactional!

यदि आप सेल्फ-इनवोकेशन के साथ-साथ लेनदेन के साथ लिपटे होने की अपेक्षा करते हैं, तो AspectJ मोड के उपयोग पर विचार करें (नीचे देखें)। इस मामले में, पहली जगह में एक छद्म नहीं होगा; इसके बजाय, @Transactionalकिसी भी प्रकार की विधि पर रनटाइम व्यवहार में बदलने के लिए लक्ष्य वर्ग को 'बुना हुआ' (यानी इसके बाइट कोड को संशोधित किया जाएगा) किया जाएगा ।


क्या आपका मतलब बीन = नया बीन ()??
.codejavaforfood

नहीं। यदि मैं नए बीन () के साथ बीन्स बनाता हूं, तो एनोटेशन कभी भी एस्पेक्ट-जे का उपयोग किए बिना कम से कम काम नहीं करेगा।
जूहा सिरजला

2
धन्यवाद! यह अजीब व्यवहार की व्याख्या करता है जो मैं देख रहा था। काफी काउंटर इस आंतरिक विधि मंगलाचरण को सहज ज्ञान युक्त ...
मैनुअल अलादाना

मैंने सीखा "प्रॉक्सी के माध्यम से आने वाली केवल बाहरी विधि कॉल को इंटरसेप्ट किया जाएगा" कठिन तरीका है
9

13

हां, निजी तरीकों पर @ ट्रेंसेक्शनल का उपयोग करना संभव है, लेकिन जैसा कि दूसरों ने उल्लेख किया है कि यह बॉक्स से बाहर काम नहीं करेगा। आपको AspectJ का उपयोग करने की आवश्यकता है। मुझे यह पता लगाने में कुछ समय लगा कि मुझे यह कैसे काम करना है। मैं अपने परिणाम साझा करूंगा।

मैंने लोड-टाइम बुनाई के बजाय संकलन-समय बुनाई का उपयोग करना चुना क्योंकि मुझे लगता है कि यह एक समग्र बेहतर विकल्प है। इसके अलावा, मैं जावा 8 का उपयोग कर रहा हूं, इसलिए आपको कुछ मापदंडों को समायोजित करने की आवश्यकता हो सकती है।

सबसे पहले, पहलू के लिए निर्भरता जोड़ें।

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

फिर मावेन में वास्तविक बायटेकोड बुनाई करने के लिए एस्पेक्ट जे प्लगइन जोड़ें (यह एक न्यूनतम उदाहरण नहीं हो सकता है)।

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

अंत में इसे अपने कॉन्फिग क्लास में जोड़ें

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)

अब आपको निजी तरीकों पर @Transactional का उपयोग करने में सक्षम होना चाहिए।

इस दृष्टिकोण के लिए एक चेतावनी: आपको अपने IDE को AspectJ के बारे में पता होने के लिए कॉन्फ़िगर करने की आवश्यकता होगी अन्यथा यदि आप ऐप को एक्लिप्स के माध्यम से चलाते हैं उदाहरण के लिए यह काम नहीं कर सकता है। सुनिश्चित करें कि आप एक सीधा मावेन निर्माण के खिलाफ एक पवित्रता की जाँच के रूप में परीक्षण करें।


यदि समीपता विधि cglib है, तो ऐसे इंटरफ़ेस को लागू करने की कोई आवश्यकता नहीं है जिसके लिए विधि सार्वजनिक होनी चाहिए, फिर यह निजी तरीकों पर @Transactional का उपयोग करने में सक्षम है?
लिली

हाँ, यह निजी तरीकों पर काम करता है, और इंटरफेस के बिना! जब तक AspectJ ठीक से कॉन्फ़िगर किया गया है, यह मूल रूप से काम करने की विधि सज्जाकारों की गारंटी देता है। और उपयोगकर्ता 536161 ने अपने जवाब में बताया कि यह स्व-चालान पर भी काम करेगा। यह वास्तव में अच्छा है, और सिर्फ एक छोटा सा डरावना है।
जेम्स वाटकिंस

12

यदि आपको किसी लेन-देन के अंदर एक निजी विधि को लपेटने की आवश्यकता है और आप एस्पेज़ का उपयोग नहीं करना चाहते हैं, तो आप TransactionTemplate का उपयोग कर सकते हैं ।

@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(){
        //...
    }

}

TransactionTemplateउपयोग दिखाने के लिए अच्छा है , लेकिन कृपया उस ..RequiresTransactionबजाय दूसरी विधि को कॉल करें ..InTransaction। सामान को हमेशा नाम दें कि आप इसे एक साल बाद कैसे पढ़ना चाहेंगे। इसके अलावा, मैं यह सोचने के लिए तर्क दूंगा कि क्या वास्तव में इसे दूसरी निजी विधि की आवश्यकता है: या तो इसकी सामग्री को सीधे तौर पर अनाम executeकार्यान्वयन में डाल दें या यदि यह गड़बड़ हो जाए तो यह कार्यान्वयन को दूसरी सेवा में विभाजित करने का संकेत हो सकता है जिसे आप तब एनोटेट कर सकते हैं @Transactional
Stuck

@Stuck, 2 विधि वास्तव में आवश्यक नहीं है, लेकिन यह मूल प्रश्न जो कैसे एक निजी विधि पर एक वसंत लेनदेन लागू करने के लिए है उत्तर देता है
loonis

हां, मैंने पहले से ही उत्तर को रद्द कर दिया था, लेकिन इसे लागू करने के तरीके के बारे में कुछ संदर्भ और विचार साझा करना चाहता था, क्योंकि मुझे लगता है कि वास्तुकला की दृष्टि से यह स्थिति एक डिजाइन दोष के लिए एक संभावित संकेत है।
Stuck

5

स्प्रिंग डॉक्स बताते हैं कि

प्रॉक्सी मोड में (जो डिफ़ॉल्ट है), प्रॉक्सी के माध्यम से आने वाली केवल बाहरी विधि कॉल इंटरसेप्टेड हैं। इसका मतलब यह है कि स्व-आह्वान, वास्तव में, लक्ष्य ऑब्जेक्ट के भीतर एक विधि जिसे लक्ष्य ऑब्जेक्ट की एक अन्य विधि कहा जाता है, रनटाइम पर एक वास्तविक लेनदेन का नेतृत्व नहीं करेगा, भले ही आह्वान विधि @Transactional के साथ चिह्नित हो।

यदि आप सेल्फ-इनवोकेशन के साथ-साथ लेनदेन के साथ लिपटे होने की अपेक्षा करते हैं, तो AspectJ मोड के उपयोग पर विचार करें (नीचे दी गई तालिका में मोड विशेषता देखें)। इस मामले में, पहली जगह में एक छद्म नहीं होगा; इसके बजाय, किसी भी तरह की विधि पर रनटाइम व्यवहार में @ व्यवहार को चालू करने के लिए लक्ष्य वर्ग को बुना जाएगा (अर्थात इसका बाइट कोड संशोधित किया जाएगा)।

दूसरा तरीका उपयोगकर्ता बीनसेल्फवेयर है


क्या आप एक संदर्भ जोड़ सकते हैं BeanSelfAware? यह वसंत की कक्षा की तरह नहीं दिखता है
भीख देता है

@asgs मान लीजिए, यह आत्म इंजेक्शन के बारे में है (एक सेम प्रदान करता है जो खुद को एक छद्म में लिपटे हुए उदाहरण के साथ)। आप stackoverflow.com/q/3423972/355438 में उदाहरण देख सकते हैं ।
ल्यु

3

जवाब न है। कृपया वसंत संदर्भ देखें :

@Transactionalएनोटेशन एक अंतरफलक, एक वर्ग परिभाषा है, या एक पर, एक अंतरफलक परिभाषा के समक्ष रखा जा सकता है एक तरीका है सार्वजनिक एक वर्ग पर विधि


1

जिस तरह से @ गुब्बारों ने TransactionTemplate का उपयोग करने का सुझाव दिया है, वह इस सहायक घटक (कोटलिन) का उपयोग कर सकता है:

@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()
    }
}

उपयोग:

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

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

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

नहीं जानते कि TransactionTemplateमौजूदा लेनदेन का पुन: उपयोग करें या नहीं लेकिन यह कोड निश्चित रूप से करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.