طريقة منتج CDI العامة لا تعمل كما هو متوقع
سؤال
لدي طريقة منتج CDI - اعتمادًا على بعض الظروف غير ذات صلة بهذا المثال - ينشئ كائنات من أنواع مختلفة:
public class TestProducer {
@Produces @TestQualifier
public Object create(InjectionPoint ip) {
if(something) {
return "a String";
} else {
return Integer.valueOf(42);
}
}
ولكن عند استخدام هذا المنتج ، أحصل دائمًا على خطأ في حالة المتابعة:
@Named("test")
public class TestComponent {
...
@Inject public void setA(@TestQualifier String stringValue) {
...
@Inject public void setB(@TestQualifier Integer integerValue) {
إنه يعمل فقط عندما يكون للطريقة إنشاء المنتج النوع المتوقع في توقيع الطريقة:
public class TestProducer {
@Produces @SpringBean
public String create(InjectionPoint ip) {
الآن تم حقن السلسلة بشكل صحيح ، لكن ليس لدي أي وسيلة لتوليد عدد صحيح من طريقة المنتج. ولكن هذا هو بالضبط ما أريد تجنبه ، لأن المنتج نفسه يجب أن يكون عامًا تمامًا.
هل أفعل شيئًا خاطئًا أم لا توجد طريقة لتحقيق السلوك الذي أريده؟
المحلول
توضح جميع وثائق CDI أن CDI يفعل الأنواع حقن التبعية - وهي خاصية عاجلة لـ CDI. IMHO ، ما تحاول القيام به هو مجرد ما يحاول CDI تجنبه. تريد الحاوية لإلقاء Object
لكل نوع و CDI لا يعمل بهذه الطريقة.
نقاط الحقن stringValue
و integerValue
لا يمكن سوى تلقي الفول الذي لديه java.lang.String
و java.lang.Integer
في قائمة أنواع الفول على التوالى. java.lang.Object
لا يرضي هذا المعيار.
لدي اقتراحان. أولاً ، نظرًا لأن لديك نقطتين أو أكثر من الأنواع المختلفة ، قم بإنشاء طريقتين أو أكثر من المنتجين لتلك الأنواع:
public class TestProducer {
@Produces @TestQualifier
public String createString(InjectionPoint ip) {
if(something) {
return "a String";
} else {
// Some other value
}
}
@Produces @TestQualifier
public int createInt(InjectionPoint ip) {
if(something) {
return 42;
} else {
// Some other value
}
}
// ...
إنه يعمل إذا كان something
الشرط هو مجرد التحقق من نوع نقطة الحقن (ما أراهن هو الحال).
ومع ذلك ، إذا كان something
الشرط يقرر النوع باستخدام معايير أخرى غير نوع نقطة الحقن ، سأشير إلى القيام بـ "المهمة القذرة" بنفسك: حقن القيمة التي تم إرجاعها في Object
-نقطة حقن مملوءة ويقوم المدلى بها يدويًا:
@Named("test")
public class TestComponent {
...
@Inject public void setA(@TestQualifier Object value) {
String stringValue = (String) value;
...
@Inject public void setB(@TestQualifier Object value) {
int intValue = (Integer) value;
النقطة الرئيسية هي أنه ، على عكس بعض أطر عمل DI الأخرى ، لا يعمل CDI مع نظام نوع Java - على العكس من ذلك ، فإنه يستخدمه بشدة. لا تحاول مكافحة ذلك ولكن استخدم هذا الجانب من CDI لصالحك :)
نصائح أخرى
منتج ل Object
غريب على أي حال. لست متأكدًا مما إذا كان هذا ممنوعًا من خلال المواصفات ، أو أنه خطأ ، لكنني أعتقد أنه يمكنك جعل بعض الحلول الذكية:
public class ValueHolder<T> {
private T value;
public T getValue() {
return value;
}
}
ثم حقن أ ValueHolder<String>
و ValueHolder<Integer>
من المحتمل إنشاء كائنات عامة مع CDI تنتج مثل ذلك:
// the wrapper class
public class Wrapper<T> {
public final T bean;
public Wrapper(T bean){
this.bean = bean;
}
}
// the producer inside some class
@Produces
public <T> Wrapper<T> create(InjectionPoint p){
// with parameter 'p', it is possible retrieve the class type of <T>, at runtime
}
// the bean example 1
public class BeanA {
public void doFoo(){
// ...
}
}
// the bean example 2
public class BeanB {
public void doBar(){
// ...
}
}
// the class that uses the produced beans
public class SomeBean{
//// There on producer method, do you can retrieve the Class object of BeanA and BeanB, from type parameters of Wrapper.
@Inject
private Wrapper<BeanA> containerA;
@Inject
private Wrapper<BeanB> containerB;
public void doSomeThing(){
containerA.doFoo();
containerB.doBar();
}
}
يعمل على اللحام 2.2.0. أعتقد أن هذا يعمل على بعض الإصدارات السابقة أيضًا.
ستبحث أساليب التهيئة عن حبة مُدارة مع سلسلة أنواع API و integer ، ولكن طريقة المنتج الخاصة بك تحتوي فقط على نوع API (في حالة طريقة المنتج ، نوع الإرجاع).
وبالتالي ، يمكنك فقط استخدام الكائن في الحقول التي تم حقن طريقة التهيئة الخاصة بك ثم تمييز بين الأنواع من جسم المتلقي ، أو ببساطة لفها وطريقة المنتج في نوع فعلي يمكن الأدوية الجيلية)