বসন্তে একটি স্কোপড প্রক্সি কী?


21

যেমনটি আমরা জানি স্প্রিং কার্যকারিতা যুক্ত করতে প্রক্সি ব্যবহার করে ( @Transactionalএবং @Scheduledউদাহরণস্বরূপ)। দুটি বিকল্প রয়েছে - একটি জেডিকে ডায়নামিক প্রক্সি ব্যবহার করে (ক্লাসটি নন-খালি ইন্টারফেস প্রয়োগ করতে পারে), বা সিজিএলআইবি কোড জেনারেটর ব্যবহার করে একটি শিশু শ্রেণি তৈরি করা। আমি সর্বদা ভেবেছিলাম যে প্রক্সিমোড আমাকে জেডিকে ডায়নামিক প্রক্সি এবং সিজিএলআইবির মধ্যে নির্বাচন করতে দেয়।

তবে আমি একটি উদাহরণ তৈরি করতে সক্ষম হয়েছিল যা দেখায় যে আমার অনুমানটি ভুল:

মামলা 1:

একক:

@Service
public class MyBeanA {
    @Autowired
    private MyBeanB myBeanB;

    public void foo() {
        System.out.println(myBeanB.getCounter());
    }

    public MyBeanB getMyBeanB() {
        return myBeanB;
    }
}

প্রোটোটাইপ:

@Service
@Scope(value = "prototype")
public class MyBeanB {
    private static final AtomicLong COUNTER = new AtomicLong(0);

    private Long index;

    public MyBeanB() {
        index = COUNTER.getAndIncrement();
        System.out.println("constructor invocation:" + index);
    }

    @Transactional // just to force Spring to create a proxy
    public long getCounter() {
        return index;
    }
}

প্রধান:

MyBeanA beanA = context.getBean(MyBeanA.class);
beanA.foo();
beanA.foo();
MyBeanB myBeanB = beanA.getMyBeanB();
System.out.println("counter: " + myBeanB.getCounter() + ", class=" + myBeanB.getClass());

আউটপুট:

constructor invocation:0
0
0
counter: 0, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$2f3d648e

এখানে আমরা দুটি জিনিস দেখতে পারি:

  1. MyBeanBশুধুমাত্র একবার ইনস্ট্যান্ট করা হয়েছিল ।
  2. এর @Transactionalকার্যকারিতা যুক্ত করতে MyBeanB, বসন্ত সিজিএলবি ব্যবহার করেছে।

কেস 2:

আমাকে MyBeanBসংজ্ঞাটি সংশোধন করতে দিন :

@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {

এই ক্ষেত্রে আউটপুট হয়:

constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$b06d71f2

এখানে আমরা দুটি জিনিস দেখতে পারি:

  1. MyBeanB3 বার ইনস্ট্যান্ট করা হয়েছিল
  2. এর @Transactionalকার্যকারিতা যুক্ত করতে MyBeanB, বসন্ত সিজিএলবি ব্যবহার করেছে।

আপনি কি বলতে পারেন যে কি চলছে? প্রক্সি মোডটি আসলে কীভাবে কাজ করে?

পুনশ্চ

আমি ডকুমেন্টেশন পড়েছি:

/**
 * Specifies whether a component should be configured as a scoped proxy
 * and if so, whether the proxy should be interface-based or subclass-based.
 * <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
 * that no scoped proxy should be created unless a different default
 * has been configured at the component-scan instruction level.
 * <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
 * @see ScopedProxyMode
 */

তবে এটি আমার পক্ষে পরিষ্কার নয়।

হালনাগাদ

কেস 3:

আমি আরও একটি মামলা তদন্ত করেছি, যার মধ্যে থেকে আমি ইন্টারফেসটি বের করেছি MyBeanB:

public interface MyBeanBInterface {
    long getCounter();
}



@Service
public class MyBeanA {
    @Autowired
    private MyBeanBInterface myBeanB;


@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
public class MyBeanB implements MyBeanBInterface {

এবং এই ক্ষেত্রে আউটপুট হয়:

constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class com.sun.proxy.$Proxy92

এখানে আমরা দুটি জিনিস দেখতে পারি:

  1. MyBeanB3 বার ইনস্ট্যান্ট করা হয়েছিল
  2. এর @Transactionalকার্যকারিতা যুক্ত করতে MyBeanB, স্প্রিং একটি জেডিকে ডায়নামিক প্রক্সি ব্যবহার করেছিল।

আমাদের আপনার লেনদেনের কনফিগারেশন প্রদর্শন করুন।
সোটিরিওস ডেলিমনোলিস

@ সোটিরিওস ডেলিমনোলিস আমার কোনও বিশেষ কনফিগারেশন নেই
gstackoverflow

আমি স্কোপড মটরশুটি বা স্প্রিং বা জেইইতে থাকা অন্য যে কোনও ধরনের এন্টারপ্রাইজ ফ্রেমওয়ার্ক যাদুতে জেনে নেই। @ সোতিরিওসডেলিমনোলিস সেই স্টাফ সম্পর্কে একটি দুর্দান্ত উত্তর লিখেছিলেন, আমি কেবল জেডিকে বনাম সিজিআইএলবি প্রক্সির বিষয়ে মন্তব্য করতে চাই: 1 এবং 2 ক্ষেত্রে আপনার MyBeanBক্লাসটি কোনও ইন্টারফেস প্রসারিত করে না, তাই অবাক হওয়ার কিছু নেই যে আপনার কনসোল লগটি সিজিএলআইবি প্রক্সি উদাহরণগুলি দেখায়। ক্ষেত্রে 3 আপনি একটি ইন্টারফেস প্রবর্তন এবং প্রয়োগ করে, ফলস্বরূপ আপনি একটি জেডিকে প্রক্সি পাবেন। এমনকি আপনি আপনার প্রবর্তনীয় পাঠ্যে এটি বর্ণনা করে।
kriegaex

সুতরাং নন-ইন্টারফেস প্রকারের জন্য আপনার সত্যিকারের কোনও পছন্দ নেই, সেগুলি সিজিএলআইবি প্রক্সি হতে হবে কারণ জেডিকে প্রক্সিগুলি কেবল ইন্টারফেসের ধরণের জন্য কাজ করে। আপনি স্প্রিং এওপি ব্যবহার করার সময় ইন্টারফেস ধরণের জন্য এমনকি সিজিআইএলবি প্রক্সি প্রয়োগ করতে পারেন। এটি যথাক্রমে <aop:config proxy-target-class="true">বা মাধ্যমে কনফিগার করা @EnableAspectJAutoProxy(proxyTargetClass = true)হয়েছে।
ক্রিগেক্স

@kriegaex আপনি কি বলতে চান যে Aspectj প্রক্সি প্রজন্মের জন্য CGlib ব্যবহার করে?
gstackoverflow

উত্তর:


10

@Transactionalআচরণের জন্য উত্পন্ন প্রক্সিটি স্কোপড প্রক্সিগুলির চেয়ে আলাদা উদ্দেশ্যে কাজ করে।

@Transactionalপ্রক্সি এক যে সেশন ম্যানেজমেন্ট আচরণ যোগ করার জন্য নির্দিষ্ট শিম গোপন নেই। সমস্ত পদ্ধতির অনুরোধগুলি আসল শিমের কাছে অর্পণ করার আগে এবং পরে লেনদেন পরিচালনা করে।

আপনি যদি এটি চিত্রিত করেন তবে এটির মতো দেখতে হবে

main -> getCounter -> (cglib-proxy -> MyBeanB)

আমাদের উদ্দেশ্যগুলির জন্য, আপনি মূলত এর আচরণটিকে উপেক্ষা করতে পারেন ( @Transactionalসিগলিব প্রক্সি না থাকলে আপনি সরিয়ে ফেলুন এবং আপনার একই আচরণটি দেখতে হবে)।

@Scopeপ্রক্সি ভিন্নভাবে আচরণ করে। নথিটিতে বলা হয়েছে:

[...] আপনাকে এমন একটি প্রক্সি অবজেক্ট ইনজেক্ট করতে হবে যা স্কোপড অবজেক্টের মতো একই পাবলিক ইন্টারফেসকে প্রকাশ করে তবে এটি প্রাসঙ্গিক সুযোগ থেকে প্রকৃত টার্গেট অবজেক্ট (যেমন একটি HTTP অনুরোধ) পুনরুদ্ধার করতে পারে এবং রিয়েল অবজেক্টের সাথে ডেলিগেট পদ্ধতি কলগুলি ডিলিট করতে পারে ।

স্প্রিং সত্যিই যা করছে তা প্রক্সি প্রতিনিধিত্বকারী এক ধরণের কারখানার জন্য সিঙ্গলটন শিম সংজ্ঞা তৈরি করছে। তবে সম্পর্কিত প্রক্সি অবজেক্টটি প্রতিটি অনুরোধের জন্য আসল বিন সম্পর্কিত প্রসঙ্গটি অনুসন্ধান করে।

আপনি যদি এটি চিত্রিত করেন তবে এটির মতো দেখতে হবে

main -> getCounter -> (cglib-scoped-proxy -> context/bean-factory -> new MyBeanB)

যেহেতু MyBeanBএকটি প্রোটোটাইপ বিন, প্রসঙ্গটি সর্বদা একটি নতুন উদাহরণ ফিরে আসবে।

এই উত্তরের উদ্দেশ্যে, ধরে নিন যে আপনি MyBeanBসরাসরি সাথে এটি পুনরুদ্ধার করেছেন

MyBeanB beanB = context.getBean(MyBeanB.class);

@Autowiredইনজেকশন লক্ষ্য পূরণের জন্য স্প্রিং মূলত যা করে ।


আপনার প্রথম উদাহরণে,

@Service
@Scope(value = "prototype")
public class MyBeanB { 

আপনি একটি প্রোটোটাইপ শিম সংজ্ঞা ঘোষণা করুন (টীকাগুলির মাধ্যমে)। @Scopeএকটি proxyModeউপাদান আছে যা

কোনও উপাদানকে স্কোপড প্রক্সি হিসাবে কনফিগার করা উচিত কিনা তা নির্দিষ্ট করে এবং যদি তা হয় তবে প্রক্সিটি ইন্টারফেস ভিত্তিক বা সাবক্লাস-ভিত্তিক হওয়া উচিত।

ডিফল্ট ScopedProxyMode.DEFAULT, যা সাধারণত ইঙ্গিত দেয় যে কোনও উপাদান পূর্ববর্তী স্ক্যান নির্দেশ স্তরে অন্য কোনও ডিফল্ট কনফিগার করা না থাকলে স্কপড প্রক্সি তৈরি করা উচিত নয়

সুতরাং স্প্রিং ফলাফল শিমের জন্য একটি স্কোপড প্রক্সি তৈরি করছে না। আপনি সেই শিমটি পুনরুদ্ধার করুন

MyBeanB beanB = context.getBean(MyBeanB.class);

আপনার কাছে এখন MyBeanBস্প্রিং দ্বারা নির্মিত একটি নতুন অবজেক্টের রেফারেন্স রয়েছে । এটি অন্য যে কোনও জাভা অবজেক্টের মতো, পদ্ধতির অনুরোধগুলি সরাসরি উল্লেখ করা দৃষ্টান্তে যাবে।

আপনি যদি getBean(MyBeanB.class)আবার ব্যবহার করেন তবে স্প্রিং একটি নতুন উদাহরণ ফিরে আসবে, যেহেতু শিম সংজ্ঞাটি প্রোটোটাইপ শিমের জন্য । আপনি এটি করছেন না, সুতরাং আপনার সমস্ত পদ্ধতির অনুরোধগুলি একই অবজেক্টে যায়।


আপনার দ্বিতীয় উদাহরণে,

@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {

আপনি cglib মাধ্যমে প্রয়োগ করা হয় এমন একটি স্কোপড প্রক্সি ঘোষণা করেন। স্প্রিং সহ এই জাতীয় শিমের অনুরোধ করার সময়

MyBeanB beanB = context.getBean(MyBeanB.class);

স্প্রিং জানে যে MyBeanBএটি একটি স্কোপড প্রক্সি এবং সেইজন্য এমন একটি প্রক্সি অবজেক্ট দেয় যা এপিআইকে MyBeanB(যেমন তার সমস্ত পাবলিক পদ্ধতি প্রয়োগ করে) সন্তুষ্ট করে যা অভ্যন্তরীণভাবে MyBeanBপ্রতিটি পদ্ধতির অনুরোধের জন্য কীভাবে একটি আসল বিনের পুনরুদ্ধার করতে পারে তা জানে ।

চালানোর চেষ্টা করুন

System.out.println("singleton?: " + (context.getBean(MyBeanB.class) == context.getBean(MyBeanB.class)));

এটি trueস্প্রিং একটি সিঙ্গলটন প্রক্সি অবজেক্ট (প্রোটোটাইপ বিন নয়) ফিরিয়ে দিচ্ছে এমন ইঙ্গিত দিয়ে ফিরে আসবে।

প্রক্সি বাস্তবায়নের অভ্যন্তরে একটি পদ্ধতির অনুরোধে স্প্রিং একটি বিশেষ getBeanসংস্করণ ব্যবহার করবে যা প্রক্সি সংজ্ঞা এবং আসল MyBeanBবিন সংজ্ঞাটির মধ্যে পার্থক্য করতে জানে knows এটি একটি নতুন MyBeanBউদাহরণ ফিরে আসবে (যেহেতু এটি একটি প্রোটোটাইপ) এবং স্প্রিং প্রতিচ্ছবি (ক্লাসিক Method.invoke) এর মাধ্যমে পদ্ধতিটির অনুরোধটি তার কাছে অর্পণ করবে ।


আপনার তৃতীয় উদাহরণটি মূলত আপনার দ্বিতীয়টির মতো।


তবে সিউন্ডের ক্ষেত্রে আমার কাছে 2 টি প্রক্সি রয়েছে: স্কোপড_প্রক্সি যা ট্রানজিশনাল_প্রক্সিটি মোড় করে যা প্রাকৃতিক মাইবিয়ানবি_বিনকে মোড়ায় ? স্কোপড_প্রক্সি
গস্ট্যাকওভারফ্লো

স্কোপড_প্রক্সির জন্য সিজিএলআইবি প্রক্সি এবং লেনদেন_প্রক্সির জন্য জেডিকে_ডিনামিক_প্রক্সি থাকা কি সম্ভব?
gstackoverflow 19

1
@ জিস্ট্যাকওভারফ্লো যখন আপনি করেন context.getBean(MyBeanB.class), আপনি প্রক্সি প্রকৃতপক্ষে পাচ্ছেন না, আপনি আসল শিম পাচ্ছেন। @Autowiredপ্রক্সি পায় (বাস্তবে আপনি MyBeanBইন্টারফেসের ধরণের পরিবর্তে ইনজেকশন দিলে ব্যর্থ হবেন )। আমি জানি না কেন স্প্রিং আপনাকে getBean(MyBeanB.class)ইন্টারফেস সহ করতে দেয় ।
সোটিরিওস ডেলিমনলিস

1
@gstackoverflow কথা ভুলে যান @Transactional@Autowired MyBeanBInterfaceপ্রকোপ সহ এবং স্কোপড, স্প্রিং প্রক্সি অবজেক্টটি ইনজেক্ট করবে। আপনি কেবল যদি এটি করেন getBean(MyBeanB.class)তবে স্প্রিং প্রক্সিটি ফিরিয়ে দেবে না, এটি লক্ষ্য বিনটি ফেরত দেবে।
সোতিরিওস ডেলিমনলিস

1
এটি লক্ষণীয় যে এটি বসন্তের মধ্যে মটরশুটি সম্পর্কিত একটি প্রতিনিধি প্যাটার্ন বাস্তবায়ন
স্টিফান
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.