स्प्रिंग एओपी: जॉइनपॉइंट और पॉइंटकॉट के बीच अंतर क्या है?


88

मैं एस्पर्ट ओरिएंटेड प्रोग्रामिंग कॉन्सेप्ट्स और स्प्रिंग AOP सीख रहा हूं। मैं एक पॉइंटकटैक और एक जॉइंटपॉइंट के बीच के अंतर को समझने में असफल रहा हूँ - दोनों ही मेरे लिए समान प्रतीत होते हैं। एक पॉइंटकट वह जगह है जहाँ आप अपनी सलाह को लागू करते हैं और एक जॉइनपॉइंट भी एक ऐसी जगह है जहाँ हम अपनी सलाह को लागू कर सकते हैं। फिर अंतर क्या है?

एक बिंदु का उदाहरण हो सकता है:

@Pointcut("execution(* * getName()")

एक Joinpoint का एक उदाहरण क्या हो सकता है?

जवाबों:


161

Joinpoint: एक Joinpoint प्रोग्राम के अनुप्रयोग निष्पादन में एक उम्मीदवार बिंदु है जहां एक पहलू को प्लग किया जा सकता है। यह बिंदु एक विधि कहा जा सकता है, एक अपवाद फेंका जा रहा है, या यहां तक ​​कि संशोधित किया जा रहा क्षेत्र भी हो सकता है। ये ऐसे बिंदु हैं जहां आपके व्यवहार के कोड को नए व्यवहार को जोड़ने के लिए आपके आवेदन के सामान्य प्रवाह में डाला जा सकता है।

सलाह: यह एक ऐसी वस्तु है जिसमें एक बिंदु द्वारा निर्दिष्ट जॉइनपॉइंट पर कार्रवाई करने के लिए कार्रवाई का प्रतिनिधित्व करने वाले सिस्टम विस्तृत चिंताओं में एपीआई इनवोकेशन शामिल है।

पॉइंटकट: एक पॉइंटकट जो ज्वाइंट पॉइंट्स पर परिभाषित करता है, संबंधित सलाह को लागू किया जाना चाहिए। AOP फ्रेमवर्क द्वारा समर्थित किसी भी जॉइनपॉइंट पर सलाह लागू की जा सकती है। बेशक, आप अपने सभी पहलुओं को सभी संभावित जॉइनपॉइंट्स पर लागू नहीं करना चाहते हैं। पॉइंटकट आपको यह निर्दिष्ट करने की अनुमति देते हैं कि आप अपनी सलाह कहाँ लागू करना चाहते हैं। अक्सर आप इन बिंदुओं को स्पष्ट वर्ग और विधि नामों का उपयोग करके या नियमित अभिव्यक्तियों के माध्यम से निर्दिष्ट करते हैं जो मिलान वर्ग और विधि नाम पैटर्न को परिभाषित करते हैं। कुछ एओपी ढांचे आपको गतिशील बिंदुओं को बनाने की अनुमति देते हैं जो निर्धारित करते हैं कि रनटाइम निर्णयों के आधार पर सलाह लागू करें, जैसे कि विधि मापदंडों का मूल्य।

निम्नलिखित छवि आपको सलाह, प्वाइंटकुट, जॉइनप्वाइंट को समझने में मदद कर सकती है। यहां छवि विवरण दर्ज करें

स्रोत

रेस्तरां सादृश्य का उपयोग कर स्पष्टीकरण: @ विक्टर द्वारा स्रोत

जब आप किसी रेस्तरां में जाते हैं, तो आप एक मेनू देखते हैं और चुनने के लिए कई विकल्प देखते हैं। आप मेनू में से किसी एक या अधिक आइटम को ऑर्डर कर सकते हैं। लेकिन जब तक आप वास्तव में उन्हें आदेश नहीं देते, तब तक वे सिर्फ "भोजन करने के अवसर" हैं। एक बार जब आप ऑर्डर देते हैं और वेटर इसे आपकी टेबल पर लाता है, तो यह एक भोजन है।

Joinpoint मेनू पर विकल्प हैं और पॉइंटकटेट्स आपके द्वारा चुने गए आइटम हैं।

एक Joinpoint आपके लिए एक पहलू लागू करने के लिए कोड के भीतर एक अवसर है ... बस एक अवसर। एक बार जब आप उस अवसर को ले लेते हैं और एक या एक से अधिक Joinpoint का चयन करते हैं और उनके लिए एक पहलू लागू करते हैं, तो आपको एक पॉइंटकट मिल गया है।

स्रोत विकी :

एक में शामिल होने के मुद्दे पर एक कार्यक्रम है जहाँ नियंत्रण प्रवाह के माध्यम से पहुंच सकते हैं के नियंत्रण के प्रवाह में एक बिंदु है दो अलग अलग रास्तों (IMO: यही कारण है कि कॉल संयुक्त है)।

सलाह कार्यों के एक वर्ग का वर्णन करती है जो अन्य कार्यों को संशोधित करता है

एक बिंदु एक सम्मिलित बिंदुओं का एक समूह है।


3
इसे सही उत्तर के रूप में चिह्नित किया जाना चाहिए। बस कुछ और जानकारी जोड़ने के लिए, क्रागी वॉल्स के जवाब को देखें ... कोडरनचैट / टीटी / 485525 / Spring / Difference-Joint-Point-Point-Cut
विक्टर

2
इस बिंदु पर: एक पॉइंटकट में यह निर्धारित होता है कि जॉइनपॉइंट्स की सलाह को क्या लागू किया जाना चाहिए +1
नमन पर्व

बस पुष्टि के लिए, more Joinpoints and apply an aspect to them, you've got a Pointcut. उनके लिए पहलू या उन्हें सलाह?
आसिफ मुश्ताक

@Premraj तो, आपके अनुरूप सलाह के अनुसार भोजन का आदेश दिया जाएगा। क्या मैं सही हू?
विश्वास अत्रे

JoinMoints और बिंदुओं के बीच भ्रम को दूर करने के लिए रेस्तरां सादृश्य ने मदद की, धन्यवाद!
एसएम

30

एक बिंदु और बिंदु के बीच अंतर को समझने के लिए, बुनाई नियमों को निर्दिष्ट करने और उन नियमों को संतुष्ट करने वाली स्थितियों में शामिल होने के बिंदुओं के रूप में बिंदुओं के बारे में सोचें।

नीचे उदाहरण में,

  @Pointcut("execution(* * getName()")  

पॉइंटकट ने नियमों को परिभाषित करते हुए कहा, किसी भी पैकेज में किसी भी वर्ग में मौजूद getName () पद्धति पर सलाह लागू की जानी चाहिए और जॉइंटप्वाइंट सभी getName () विधि की एक सूची होगी जो कक्षाओं में मौजूद है ताकि इन तरीकों पर सलाह लागू की जा सके।

(वसंत के मामले में, नियम केवल प्रबंधित बीन्स पर लागू किया जाएगा और सलाह केवल सार्वजनिक तरीकों पर लागू की जा सकती है)।


1
"पॉइंटकट ने नियमों को परिभाषित करते हुए कहा, किसी भी पैकेज में किसी भी कक्षा में मौजूद getName () पद्धति पर सलाह लागू की जानी चाहिए और जॉइनपॉइंट सभी getName () वर्गों में मौजूद विधि की एक सूची होगी ताकि इन तरीकों पर सलाह लागू की जा सके।" मुझे क्षमा करें लेकिन यह अधिक भ्रमित करने वाला है। क्या आप कृपया मुझे एक वास्तविक दुनिया में दिन-प्रतिदिन के परिदृश्य के लिए एक सादृश्य दे सकते हैं?
सौरभ पाटिल

28

JoinPoints: ये मूल रूप से वास्तविक व्यापार तर्क में स्थान हैं जहाँ आप कुछ विविध कार्यक्षमता सम्मिलित करना चाहते हैं जो कि आवश्यक है लेकिन वास्तविक व्यावसायिक तर्क का हिस्सा नहीं है। JoinPints ​​के कुछ उदाहरण हैं: विधि कॉल, विधि सामान्य रूप से वापस आना, विधि एक अपवाद फेंकना, किसी वस्तु को त्वरित करना, किसी वस्तु का संदर्भ देना, आदि ...

पॉइंटकट्स: पॉइंटकर्ट रेग्युलर एक्सप्रेशंस की तरह कुछ होते हैं जिनका इस्तेमाल जॉइंटप्वाइंट को पहचानने के लिए किया जाता है। पोंटकट्स को "पॉइंटकट एक्सप्रेशन लैंग्वेज" का उपयोग करके व्यक्त किया जाता है। पॉइंट्स निष्पादन प्रवाह के बिंदु हैं जहां क्रॉस-कटिंग चिंता को लागू करने की आवश्यकता है। Joinpoint और Pointcut के बीच अंतर है; ज्वाइंट पॉइंट्स अधिक सामान्य होते हैं और किसी भी नियंत्रण प्रवाह का प्रतिनिधित्व करते हैं जहां हम 'क्रॉस-कटिंग चिंता का परिचय दे सकते हैं जबकि पॉइंटकट ऐसे जॉइंटप्वाइंट्स की पहचान करते हैं जहां' हम क्रॉस-कटिंग चिंता का परिचय देना चाहते हैं।


1
Joinpoint - सलाह कोड लागू / चलाने के लिए संभावित स्थान। पॉइंटकट - सलाह को क्रियान्वित करने के लिए वास्तविक चुने गए ज्वाइंट पॉइंट्स।
user104309

24

किसी के लिए आम आदमी की व्याख्या जो अवधारणाओं के लिए नया है AOP। यह संपूर्ण नहीं है, लेकिन अवधारणाओं को समझने में मदद करनी चाहिए। यदि आप पहले से ही मूल शब्दजाल से परिचित हैं, तो आप अभी पढ़ना बंद कर सकते हैं।

मान लें कि आपके पास एक सामान्य वर्ग का कर्मचारी है और आप हर बार कुछ करना चाहते हैं, तो ये तरीके कहलाते हैं।

class Employee{
    public String getName(int id){....}
    private int getID(String name){...}
}

इन विधियों को JoinPoints कहा जाता है । हमें इन विधियों को पहचानने के लिए एक तरीका चाहिए ताकि रूपरेखा उन सभी वर्गों के बीच पा सके। इसलिए हम इन विधियों के हस्ताक्षर से मेल खाने के लिए एक नियमित अभिव्यक्ति लिखेंगे। जबकि इसमें और भी बहुत कुछ है जैसा कि आप नीचे देखेंगे, लेकिन शिथिल यह नियमित अभिव्यक्ति है जो पॉइंटकट को परिभाषित करती है । जैसे

* * mypackage.Employee.get*(*)

पहला * संशोधक सार्वजनिक / निजी / संरक्षित / डिफ़ॉल्ट के लिए है। दूसरा * वापसी विधि के प्रकार के लिए है।

लेकिन फिर आपको दो और बातें भी बताने की जरूरत है:

  1. जब कोई कार्रवाई की जानी चाहिए - जैसे विधि निष्पादन से पहले या अपवाद के बाद
  2. जब यह मेल खाता है तो क्या करना चाहिए (शायद सिर्फ एक संदेश प्रिंट करें)

इन दोनों के संयोजन को सलाह कहा जाता है ।

जैसा कि आप कल्पना कर सकते हैं, आपको # 2 करने में सक्षम होने के लिए एक फ़ंक्शन लिखना होगा। तो यह है कि यह मूल बातें कैसे दिख सकता है।

नोट: स्पष्टता के लिए, के बजाय REGEX शब्द का उपयोग करें * * mypackage.Employee.get*(*)। वास्तव में पूर्ण अभिव्यक्ति परिभाषा में जाती है।

@Before("execution(REGEX)")
public void doBeforeLogging() {....}   <-- executed before the matching-method is called

@After("execution(REGEX)")
public void doAfterLogging() {....}  <-- executed after the matching-method is called

एक बार जब आप इनका उपयोग करना शुरू कर देते हैं, तो आप कई @ बाद / @ पहले / @ आस-पास सलाह निर्दिष्ट कर सकते हैं। बार-बार नियमित अभिव्यक्ति अंततः बातें भ्रामक और बनाए रखने के लिए मुश्किल हो जाता है खत्म हो जाएगा। तो हम क्या करते हैं, हम बस अभिव्यक्ति को एक नाम देते हैं और इसे हर जगह पहलू वर्ग में उपयोग करते हैं।

@Pointcut("execution(REGEX)") <-- Note the introduction of Pointcut keyword
public void allGetterLogging(){} <-- This is usually empty

@Before("allGetterLogging")
public void doBeforeLogging() {....}

@After("allGetterLogging")
public void doAfterLogging() {....}

BTW, आप इस पूरे तर्क को एक कक्षा में लपेटना चाहेंगे, जिसे Aspect कहा जाता है और आप एक वर्ग लिखेंगे:

@Aspect
public class MyAwesomeAspect{....}

इन सभी चीजों को काम करने के लिए, आपको @ AOP कीवर्ड पर पढ़ने, समझने और कार्रवाई करने के लिए कक्षाओं को पार्स करने के लिए स्प्रिंग को बताना होगा। ऐसा करने का एक तरीका निम्नलिखित है जो स्प्रिंग कॉन्फिग xml फ़ाइल में निर्दिष्ट है:

<aop:aspectj-autoproxy>


1
मैं AOP के लिए नया हूं और इस स्पष्टीकरण ने मुझे सलाह / बिंदु / JoinPoints के बीच संबंधों को स्पष्ट रूप से समझने में मदद की।
जतिन शशू

11

SQL जैसी डेटा क्वेरी भाषा के लिए AspectJ जैसी AOP भाषा की तुलना में, आप कई पंक्तियों के साथ डेटाबेस तालिका के रूप में ज्वाइंट पॉइंट्स (यानी आपके कोड के सभी स्थान जहाँ आप पहलू कोड बुनाई कर सकते हैं) के बारे में सोच सकते हैं। एक बिंदु एक SELECT stamement की तरह है, जो पंक्तियों / जॉइनपॉइंट्स के उपयोगकर्ता-निर्धारित सबसेट को चुन सकता है। उन चयनित स्थानों में आपके द्वारा बुना गया वास्तविक कोड सलाह कहलाता है।


9

परिभाषाएं

प्रलेखन के अनुसार:

बिंदु से जुड़ें: किसी कार्यक्रम के निष्पादन के दौरान एक बिंदु, जैसे कि किसी विधि का निष्पादन या अपवाद को संभालना।

आप एक कार्यक्रम के निष्पादन में घटनाओं के रूप में संयुक्त बिंदुओं पर विचार कर सकते हैं । यदि आप स्प्रिंग AOP का उपयोग कर रहे हैं, तो यह विधियों के आह्वान तक सीमित है। AspectJ अधिक लचीलापन प्रदान करता है।

जब भी आप किसी रेस्तरां में जाते हैं (मैं आपको नहीं जानता, आप कर सकते हैं! लेकिन, मैं निश्चित रूप से नहीं करता) लेकिन आप सभी घटनाओं को कभी भी मेनू में सभी भोजन नहीं खाते हैं। तो आप उन्हें संभालने के लिए घटनाओं का चयन करें और उनके साथ क्या करें। यहाँ पॉइंटकट्स जाता है । प्रलेखन के अनुसार,

पॉइंटकट : एक विधेय जो मैचों में शामिल होता है

फिर आप जोड़ते हैं कि पॉइंटकट के साथ क्या करना है , वहां सलाह मिलती है । प्रलेखन के अनुसार,

सलाह एक बिंदु अभिव्यक्ति के साथ जुड़ा हुआ है और बिंदु से मेल खाते हुए किसी भी बिंदु पर चलता है।

कोड

package com.amanu.example;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * @author Amanuel Nega on 10/25/16.
 */
class ExampleBussinessClass {

    public Object doYourBusiness() {
        return new Object();
    }

}

@Aspect
class SomeAspect {

    @Pointcut("execution(* com.amanu.example.ExampleBussinessClass.doYourBusiness())")
    public void somePointCut() {
    }//Empty body suffices

    @After("somePointCut()")
    public void afterSomePointCut() {
        //Do what you want to do after the joint point is executed
    }

    @Before("execution(* *(*))")
    public void beforeSomePointCut() {
        //Do what you want to do before the joint point is executed
    }

}

संहिता की व्याख्या

  • ExampleBusinessClass जब प्रॉक्सी-एड, हमारा लक्ष्य है!
  • doYourBusiness()एक संभावित संयुक्त बिंदु है
  • SomeAspect हमारा पहलू यह है कि इस तरह के कई चिंताओं को पार करता है ExampleBusinessClass
  • somePointCut()एक बिंदु कटौती की परिभाषा है जो हमारे संयुक्त बिंदु से मेल खाती है
  • afterSomePointCut()एक सलाह है जो संयुक्त बिंदु से मेल खाने वाले हमारे somePointCut पॉइंट कट के बाद निष्पादित की जाएगीdoYourBusiness()
  • beforeSomePointCut()एक सलाह भी है जो सभी publicविधि निष्पादन से मेल खाती है । इसके विपरीत afterSomePointCut, यह एक इनलाइन प्वाइंट कट घोषणा का उपयोग करता है

यदि आप मुझ पर विश्वास नहीं करते हैं तो आप दस्तावेज देख सकते हैं । आशा है कि ये आपकी मदद करेगा


1
सरल व्याख्या। केवल तीन उद्धृत ग्रंथ समझने के लिए पर्याप्त हैं। धन्यवाद।
TRNENE

6

दोनों पहलू-उन्मुख प्रोग्रामिंग के "जहां" से संबंधित हैं।

एक जुड़ने का स्थान एक व्यक्तिगत स्थान है जहां आप AOP के साथ कोड निष्पादित कर सकते हैं। उदाहरण के लिए "जब एक विधि एक अपवाद फेंकता है"।

एक बिंदु में शामिल होने के बिंदुओं का एक संग्रह है। जैसे "जब क्लास फू में एक विधि एक अपवाद फेंकता है"।


4

JoinPoint : Joinpoint आपके प्रोग्राम एक्ज़ीक्यूशन में पॉइंट्स हैं जहाँ एक्ज़ीक्यूशन कैचिंग, कॉलिंग फ्लो बदल गया।

PointCut : PointCut मूल रूप से वे Joinpoint हैं जहाँ आप अपनी सलाह (या कॉल पहलू) लगा सकते हैं।

तो मूल रूप से पॉइंटकॉट JoinPoints का सबसेट है


3

वसंत में AOP के पास {सलाहकार, सलाह, प्वाइंटकट, ज्वाइन पॉइंट} है

जैसा कि आप जानते हैं कि एनओपी का मुख्य उद्देश्य आवेदन कोड से क्रॉस-कटिंग चिंता तर्क (एस्पेक्ट) को डिकूप करना है, इसे वसंत में लागू करने के लिए हम उपयोग करते हैं (सलाह / सलाहकार)

पॉइंटकट का उपयोग फ़िल्टर करने के लिए किया जाता है, जहाँ हम इस सलाह को ठीक से लागू करना चाहते हैं, जैसे "सभी विधियाँ इन्सर्ट से शुरू होती हैं" इसलिए अन्य विधियों को बाहर रखा जाएगा, इसीलिए हम पॉइंटकट इंटरफ़ेस {ClassFilter और MethodMatcher} में हैं

इसलिए सलाह क्रॉस-कटिंग लॉजिक कार्यान्वयन है और एडवाइजर सलाह प्लस पॉइंट है, यदि आप केवल सलाह वसंत का उपयोग करते हैं तो यह सलाहकार के लिए मैप करेगा और पॉइंटकट ट्रू बना देगा जिसका अर्थ है कि कुछ भी ब्लॉक न करें। इसीलिए जब आप केवल सलाह का उपयोग करते हैं तो इसे लक्ष्य वर्ग के सभी तरीकों पर लागू किया जाता है क्योंकि आपने उन्हें फ़िल्टर नहीं किया था।

लेकिन Joinpoint प्रोग्राम में एक स्थान है, आप इसके बारे में सोच सकते हैं जब आप क्लास ऑब्जेक्ट को एक्सेस करते हैं तो आप रिफ्लेक्ट कर सकते हैं और फिर आप मेथड ऑब्जेक्ट प्राप्त कर सकते हैं, फिर आप इस क्लास में किसी भी तरीके को इनवॉइस कर सकते हैं, और यदि आप चाहें तो कंपाइलर काम करता है। यह आप Joinpoint की कल्पना कर सकते हैं।

Joinpoint फील्ड, कंस्ट्रक्टर या मेथड के साथ हो सकता है लेकिन Spring में हमारे पास केवल विधियों के साथ joinpoint होती है, इसीलिए स्प्रिंग में हमारे पास Joinpoint के प्रकार (पहले, बाद, फेंकता, प्रकार) होते हैं, ये सभी क्लास में स्थानों को संदर्भित करते हैं।

जैसा कि मैंने उल्लेख किया है कि आपके पास बिना किसी पॉइंटकट (नो फिल्टर) के साथ सलाह हो सकती है तो इसे सभी विधियों पर लागू किया जाएगा या आपके पास सलाहकार हो सकते हैं जो [सलाह + पॉइंटकट] है जो विशिष्ट तरीकों पर लागू होगा लेकिन आपके पास सलाह के बिना नहीं हो सकता जॉइंट पॉइंट जैसे पॉइंटकट, आपको इसे निर्दिष्ट करना होगा, और इसीलिए वसंत में सलाह के प्रकार बिल्कुल उसी प्रकार के होते हैं जैसे जॉइनपॉइंट जब आप एक सलाह चुनते हैं तो आप जो ज्वाइन प्वाइंट चुनते हैं।

लपेटने के लिए, सलाह लक्ष्य वर्ग के लिए आपके पहलू के लिए कार्यान्वयन तर्क है, इस सलाह में शामिल होने से पहले मंगलाचरण की तरह होना चाहिए, आह्वान के बाद, या फेंकने के बाद, फिर आप फ़िल्टर कर सकते हैं, जहाँ आप बिंदु का उपयोग करके इसे लागू करना चाहते हैं। विधियों या नो पॉइंटकट (कोई फ़िल्टर नहीं) को फ़िल्टर करें, इसलिए इसे कक्षा के सभी तरीकों पर लागू किया जाएगा।


3

एक बिंदु को पहलू - वर्ग कार्यान्वयन पर परिभाषित किया गया है। सलाह के भीतर बिंदु कट मूल रूप से पॉइंटकट अभिव्यक्ति को संदर्भित करता है।

उदाहरण के लिए,

@Before("execution(* app.purchase2.service.impl.*(..))")
public void includeAddOns(RolesAllowed roles) {
..
}

उपरोक्त साधनों का अर्थ है, "इनकाउन्डऑन्स" विधि को आह्वान करने से पहले (@Before सलाह के कारण) किसी भी तरीके (पैकेज के भीतर कक्षाओं में "app.purchase2.service.impl" कहा जाता है)

पूरे एनोटेशन को पॉइंटकट कहा जाता है @Before("execution(* app.purchase2.service.impl.*(..))")

संयुक्त बिंदु वास्तविक विधि मंगलाचरण है, जो पैकेज में विधि में शामिल हो गया "app.purchase2.service.impl" पहलू वर्ग में विधि को शामिल करें "ddAddOns () "।

आप org.aspectj.lang.JoinPointक्लास के साथ जुड़ने के बिंदु तक पहुँच सकते हैं ।


अच्छा उत्तर! अंत में मुझे अंतर समझ में आया!
डांटे १

2

मैं mgroves से सहमत हूं .. एक बिंदु कटौती को कई संयुक्त बिंदुओं के संग्रह के रूप में माना जा सकता है। संयुक्त बिंदु उस विशेष स्थान को निर्दिष्ट करते हैं जहां सलाह को लागू किया जा सकता है, जहां बिंदु के रूप में सभी संयुक्त बिंदुओं की सूची दर्शाती है।


0

JoinPoint: यह आवेदन में एक बिंदु (विधि) निर्दिष्ट करता है जहां सलाह को निष्पादित किया जाएगा।

पॉइंटकट: यह जॉइनपॉइंट्स का संयोजन है, और यह निर्दिष्ट करता है कि जिस पर JoinPoint सलाह को क्रियान्वित किया जाएगा।


-5

ज्वाइन पॉइंट वह जगह है जहाँ हम वास्तव में सलाह देते हैं

लेकिन बिंदु में कटौती बिंदु बिंदुओं का संग्रह है। इसका मतलब है कि हम क्रॉस-कटिंग लॉजिक को कितने तरीके से रखते हैं, इसे पॉइंट कट कहा जाता है

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