Showing posts with label Java EE : 3 CDI. Show all posts
Showing posts with label Java EE : 3 CDI. Show all posts

December 29, 2016

Dependency Injection - Part 3

အရင် Dependency Injection - Part 1 မှာတော့ Dependency Injection ဆိုတာ ဘယ်လိုမျိုးလဲ။ ဘာ့ကြောင့် လိုအပ်တာလဲဆိုတာကို အဓိကထားပြီး ဖေါ်ပြခဲ့တယ်။ ပြီးတော့ ဒုတိယပိုင်း Depencency Injection - Part 2 မှာကတော့ CDI ကို သုံးပြီး Dependency Injection ကို ဘယ်လို ရေးသားရမလဲ ဆိုတာကို ဖေါ်ပြခဲ့ပြီးပါပြီ။ Depencency Injection နဲ့ ပတ်သက်ပြီး ဒီတစ်ခေါက်က နောက်ဆုံးအကြိမ်ဖြစ်ပါမယ်။

ဒီတစ်ခေါက်ကတော့ CDI Bean တွေရဲ့ Lifecycle နဲ့ CDI Bean မဟုတ်တဲ့ Object တွေကို Inject လုပ်ချင်ရင် ဘယ်လို လုပ်မလဲဆိုတာကို ဖေါ်ပြသွားပါမယ်။ ဒါပြီးရင်တော့ CDI Bean တွေရဲ့ Scope တွေ အကြောင်းကို ဆက်လက် ဖေါ်ပြသွားပါမယ်။


Lifcycle of CDI Bean

CDI ကို အသုံးပြုရင် Bean တွေကို ကိုယ်တိုင် Object ဆောက်စရာမလိုဘူး။ Inject လုပ်လိုက်တာနဲ့ လိုအပ်တဲ့ Bean ကို Container ကနေ အလိုအလျောက် လာရေက်ပြီး Inject လုပ်ပေးမယ်ဆိုတာတော့ သိခဲ့ပါပြီ။ ဒီတစ်ကြိမ်မှာတော့ လက်တွေ့ Container ကနေ ဘယ်လို အချိန်အခါမျိုးမှာ လာပြီး Inject လုပ်ပေးသလဲဆိုတာကို သဘောပေါက်အောင် CDI Beans တွေရဲ့ Lifecycle ကို လေ့လာသွားပါမယ်။

ပုံမှန်အားဖြင့် POJO Object တွေရဲ့ Lifecycle တွေဟာ ရိုးရှင်းပါတယ်။ Application ထဲမှာ အသုံးပြုလိုတဲ့အခါမှာ new keyword ကို သုံး Constructor ကို ခေါ်ပြီး Create လုပ်ပါမယ်။ နောက်ဆုံး Reference မရှိတော့တဲ့အခါမှာ Garbage Collector ကနေလာပြီး သိမ်းတဲ့အချိန်မှာ Object တစ်ခုရဲ့ သက်တမ်းဟာ ပြီးဆုံးမှာ ဖြစ်ပါတယ်။

များသောအားဖြင့် ကျွန်တော်တို့တွေဟာ Object တစ်ခုရဲ့ Instance Variable တွေကို Initialize လုပ်တဲ့နေရာမှာ Constructor ထဲမှာ ရေးသားလေ့ရှိပါတယ်။ ဒါပေမဲ့ Container ကနေ Manage လုပ်ပေးတဲ့ CDI လို Bean မျိုးကတော့ new keyword နဲ့ Constructor ကို ခေါ်လို့ရမှာ မဟုတ်ပါဘူး။ Constructor ကို ခေါ်တဲ့အခါမှာ Injection Point မှာရှိတဲ့ Variable တွေကို Inject မလုပ်ရသေးတဲ့အတွက်ဖြစ်ပါတယ်။

အဲ့ဒီအစား Container ကနေ Beans တွေရဲ့ Lifecycle ကို Manage လုပ်တဲ့နေရာမှာ ခေါ်သွားတဲ့ Lifecycle Callback Method တွေရှိပါတယ်။ အဲ့ဒါတွေကို အသုံးပြုသွားရမှာ ဖြစ်ပါတယ်။ အရင်ဆုံး CDI Bean တွေရဲ့ Lifecycle ကို ကြည့်ကြည့်ပါမယ်။





Container ကနေ CDI Beans တစ်ခုကို Inject လုပ်ပေးရတော့မယ်ဆိုရင် new keyword နဲ့ Default Constructor ကို ခေါ်ပြီး တည်ဆောက်ပါတယ်။ အဲ့ဒီနောက်မှာ Bean ထဲမှာ ပါတဲ့ @Inject လို့ရေးထားတဲ့ Dependency တွေကို Inject လုပ်ပါမယ်။ အဲ့ဒါပြီးတော့ @PostConstruct Annotation ရေးထားတဲ့ Method ကို ခေါ်ယူမှာ ဖြစ်ပါတယ်။

ဒါ့ကြောင့် Initialize လုပ်တဲ့ Process တွေကို PostConstruct Method ထဲမှာ ရေးသားရမှာ ဖြစ်ပါတယ်။ အဲ့ဒီ​ PostConstruct ကို ခေါ်ပြီးမှသာ CDI Bean ဟာ အလုပ်လုပ်ဖို့ အသင့် အနေအထားကို ရောက်ရှိသွားမှာ ဖြစ်ပါတယ်။

အဲ့ဒီနောက်မှာ Application ထဲက Method Invocation တွေကို အကြိမ်ကြိမ်ပြုလုပ်မယ်။ နောက်ဆုံး Reference မရှိတော့ဘူး။ ဖျက်ဖို့ လိုအပ်လာပြီဆိုရင် @PreDestroy ရေးထားတဲ့ Method ကို ခေါ်ပြီးမှ Memory ပေါ်ကနေ ဖျက်ထုတ်ပစ်မှာ ဖြစ်ပါတယ်။ ဒါ့ကြောင့် Bean ကို Destroy မလုပ်ခင် ရေးသားချင်တဲ့ Process တွေကို @PreDestroy Method ထဲမှာ ရေးသားရမှာ ဖြစ်ပါတယ်။


Injecting No CDI Objects

CDI ကိုသုံးပြီး Inject လုပ်လို့ရတာကတော့ CDI Bean တွေဖြစ်မှသာ ရပါမယ်။ CDI Bean ဆိုတာ ဘယ်လို Bean တွေကို ခေါ်တာလဲ။ CDI Project ထဲမှာရှိပြီး CDI Annotation တွေကို တပ်ဆင်ရေးသားထားတဲ့ Bean တွေကို CDI Bean တွေလို့ခေါ်ပါတယ်။ CDI ရဲ့ deployment descriptor ဖြစ်တဲ့ beans.xml file ကို ရေးသားထားရပါမယ်။ beans.xml ဖိုင်ရဲ့ Bean-Discovery-Mode ကို all လို့ရေးထားရင်တော့ Java Bean တွေအားလုံးကို CDI Bean တွေ အနေနဲ့ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။





တဖန် ထည့်သွင်းထားတဲ့ Lib တွေထဲက Java Beans တွေကို Inject လုပ်ချင်တယ်ဆိုရင်တောင် အဲ့ဒီ Beans တွေဟာ CDI Beans တွေ ဖြစ်မှရမှာ ဖြစ်ပါတယ်။ အဲ့ဒီလိုဖြစ်ဖို့ဆိုတာကလဲ Lib အနေနဲ့ Reference လုပ်နေတဲ့ jar တွေထဲမှာလဲ beans.xml ပါနေမှရမှာ ဖြစ်ပါတယ်။

တကယ်လို့ CDI Bean မဟုတ်တဲ့ Beans နမူနာအားဖြင့် Java SE API ထဲက Type တစ်ခုခုကို Inject လုပ်ဖို့ဆိုရင် ဒီအတိုင်းဆိုရင် ရမှာ မဟုတ်ပါဘူး။ @Produces Annotation ကို သုံးပြီး Produce လုပ်ထားမှသာ Inject လုပ်နိုင်မှာ ဖြစ်ပါတယ်။
public class MoakSMSSender implemets Sender {
 
 @Inject
 private String name;

 @Override
 public void send(String address, String message) {
  String dummy = "%s : %s : %s";
  System.out.println(String.format(name, address, message));
 }
}

အထက်ပါ နမူနာထဲမှာလို String name ကို Inject လုပ်ပြီး သုံးလိုတယ်ဆိုရင် အဲ့ဒီအတွက် @Produces လုပ်ထားဖို့လိုအပ်ပါတယ်။ MoakSMSSender ထဲမှာ String name ဆိုတာကို Inject လုပ်ထားတဲ့အတွက် Container ကနေ Inject လုပ်ဖို့ လိုက်ရှာပါမယ်။ Resources Class ထဲမှာ String Object တစ်ခုကို Produce လုပ်ထားတာကို တွေ့ရင် အဲ့ဒီနေရာမှာ Inject လုပ်ပးပါမယ်။
public class Resource {
 
 @Produce
 private String name = "Moak ";
}

String တွေလို Object တွေသာ မကပဲ primitive type တွေကိုလဲ Produce လုပ်ထားရင် Inject လုပ်ပြီး သုံးလို့ရမှာ ဖြစ်ပါတယ်။

တဖန် EntityManager လို Resource တွေကို Produces လုပ်ထားရင် လိုအပ်တဲ့နေရာမှာ Inject လုပ်ပြီး သုံးရုံပါပဲ။ ဒါပေမဲ့ ဒီလို Object တွေဟာ Resource တွေကို သုံးနေတာဖြစ်တဲ့အတွက် အသုံးပြုပြီးရင် close လုပ်ပေးဖို့ လိုအပ်ပါတယ်။ Java EE Server တွေဆိုရင်တော့ Server ကနေ အလို အလျောက်ပိတ်ပေးမှာ ဖြစ်တဲ့အတွက် ပြဿနာမရှိပါဘူး။ ဒါပေမဲ့ Java SE ပတ်ဝန်းကျင်တို့လို Web Container သာပါတဲ့ Tomcat Server ပေါ်မှာဆိုရင် Resource တွေကို အသုံးပြုပြီးရင် Application ထဲကနေ ပြန်ပိတ်ပေးဖို့ လိုအပ်ပါတယ်။

တကယ်လို့ Produce လုပ်ထားတဲ့ Resource Object တွေကို မသုံးတော့ရင် ပြန်ပိတ်ပေးဖို့အတွက် @Dispose Annotation ကို သုံးပြီး ရေးသားနိုင်ပါတယ်။
public class Resources {
 
 @PersistenceContext(unitName="payrollDB")
 private EntityManager em;

 @Produces
 public EntityManager getEm() {
  return this.em;
 }

 public void close(@Dispose EntityManager em) {
  em.close();
 }
}

အထက်ပါ နမူနာထဲမှာတော့ EntityManager Object ကို Produce လုပ်ထားပြီး နောက်ဆုံး မသုံးတော့လို့ရှိရင် Close လုပ်ပေးနိုင်အောင် close method ရဲ့ Argument မှာ @Dispose ဆိုပြီး EntityManager ကို ပေးထားပါတယ်။ CDI Container မှ နောက်ဆုံး Produce လုပ်ထားတဲ့ EntityManager ကို Reference မရှိတော့ဘူးဆိုရင် close method ကို ခေါ်မှာ ဖြစ်တဲ့အတွက် EntityManager ကို close လုပ်ပေးမှာ ဖြစ်ပါတယ်။

ဆက်ပါဦးမယ်
မင်းလွင်


December 17, 2016

Dependency Injection - Part 2

ပြီးခဲ့တဲ့ Blog Post မှာတော့ Dependency Injection ဆိုတာ ဘယ်လိုဟာမျိုးလဲ။ ပြီးတော့ CDI ကို သုံးပြီး Injection ကို ရေးမလဲဆိုတာကို ဖေါ်ပြခဲ့ပါတယ်။ တဆက်ထဲ စဉ်းစားသွားစေလိုတာကတော့ Injection Point မှာ သုံးလို့ရတဲ့ Class အမျိုးအစားတွေကိုပါ။ Injection Poing မှာ Concrete Class တွေကိုလဲ အသုံးပြုနိုင်သလို၊ Abstract Class ကော Interface တွေကိုပါ အသုံးပြုနိုင်ပါတယ်။ Concrete Class တွေဆိုရင် ပြဿနာမရှိပေမဲ့ Abstract Class တွေ Interface တွေကို သုံးထားရင် အဲ့ဒီနေရာမှာ ဘယ် Class ကို လာ Inject လုပ်မလဲ စိုးရိမ်စရာရှိပါတယ်။



အထက်ပါ Class Diagram ထဲမှာလို ExamRegistor ထဲက Injection Point မှာ Model ဆိုတဲ့ Type ကို အသုံးပြုထားပါတယ်။ Model ဆိုတာက Interface တစ်ခုဖြစ်ပြီး ExamModel Class က Implement လုပ်ထားတာဖြစ်ပါတယ်။ ဒါ့ကြောင့် ExamModel Class ကို Model Interface ရဲ့ Type အနေနဲ့ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

အထက်ပါအတိုင်း Model Interface ကို Implement လုပ်ထားတဲ့ Class ဟာ တစ်ခုထဲရှိနေမယ်ဆိုရင် Container ကနေ ExamModel Class ကနေ Objectကို ဆောက်ပြီး Model ရဲ့ နေရာမှာ လာပြီး Inject လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။

Injection Point မှာ Inject လုပ်လို့ရတဲ့ Class ဟာ တစ်မျိုးထဲ ဖြစ်နေလို့သာ Container ကနေ အလိုအလျောက် Inject လုပ်ပေးနိုင်တာဖြစ်ပါတယ်။ ထိုကဲ့သို့ Inject လုပ်နိုင်ခြင်းကို Default Injection လို့ခေါ်ပါတယ်။


Injection Point မှာ Inject လုပ်လို့ရတဲ့ Bean တစ်ခုထက်မကရှိနေခဲ့ရင်




အထက်ပါပုံအတိုင်း Injection Point ဖြစ်တဲ့ Model နေရာမှာ Implement လုပ်ထားတဲ့ Class တွေ တစ်ခုထက်မကရှိလာပြီဆိုရင် ဒီအတိုင်းဆို Container ကနေ ဘယ် Class ကနေ Object ဆောက်ပြီး Inject လုပ်ရမယ်ဆိုတာကို ဆုံးဖြတ်လို့မရပဲ ဖြစ်နေပါလိမ့်မယ်။

ထိုအခါမျိုးဆိုရင် Application ထဲကနေ ဘယ် Class ကို သုံးမယ်ဆိုတာ သိသာအောင် Qualifier တွေကို ရေးသားပေးဖို့ လိုအပ်ပါတယ်။


@Default & @Any Qualifier


အထက်ပါ အနေအထာ:မျို:မှာ Injection Point မှာ ဘယ် Bean ကို Inject လုပ်ရမယ်ဆိုတာ သိသာအောင် ရေ:ထာ:ဖို့ လိုအပ်ပါတယ်။ Inject လုပ်လို့ရတဲ့ Bean တွေက ၂ ခုထဲသာဆိုရင်တော့ Built In ပါဝင်တဲ့ @Default နဲ့ @Any Annotation တို့ကို အသုံ:ပြုနိုင်ပါတယ်။

ExamModel Class ကို Default အနေနဲ့ သုံ:ချင်ရင် Class Definition ရဲ့ရှေ့မှာ @Default Annotation ကို ရေ:သာ:ပါမယ်။ ပြီ:မှ FileModel မှာတော့ @Any ကို ရေ:သာ:ပါမယ်။

ExamModel.java
@Default
public class ExamModel implements Model {
    // business methods
}

FileModel.java
@Any
public class FileModel implements Model {
    // business methods
}
Injection Point နေရာမှာတော့ အသုံ:ပြုလိုတဲ့ Type အလိုက် @Default နဲ့ @Any တို့ကို ရေ:သာ:ရုံပါပဲ။ နမူနာထဲမှာတော့ Default ကို သုံ:စေလိုတဲ့အတွက် @Default လို့ရေ:သာ:ထာ:ပါတယ်။

ExamRegistration.java
public class ExamRegistration {
    
    @Inject
    @Default
    private Model model;

    // business methods
}
Default နဲ့ Any ကို သုံ:မယ်ဆိုရင် Inject လုပ်လို့ရတဲ့ Bean က ၂ ခုထဲဆိုရင် အစဉ်ပြေပေမဲ့ ၂ ခုထက်မကရှိရင် ဘယ် Any ကို သုံ:မလဲဆိုတာ မသိတော့ပဲ နေပါမယ်။ အဲ့ဒီလိုအခါမျို:မှာ အသုံ:ပြုနိုင်တာက @Named Qualifier နဲ့ မိမိကိုယ်တိုင် ရေ:သာ:အသုံ:ပြုနိုင်တဲ့ Custom Qualifier တို့ပဲ ဖြစ်ပါတယ်။


@Named Qualifier

Injection Point မှာ Inject လုပ်လို့ရတဲ့ Type တွေ တစ်ခုထက်မက ရှိနေရင် နောက်တနည်းရေးလို့ရတာက @Named Annotation ပဲ ဖြစ်ပါတယ်။ Inject လုပ်မည့် Class တွေမှာ @Named Annotation ကို ရေးထားပြီး Injection Point မှာ အသုံးလိုတဲ့ Name ကို ရေးလိုက်ရုံပါပဲ။

ExamModel.java
@Named
public class ExamModel implements Model {
    // business methods
}

FileModel.java
@Named
public class FileModel implements Model {
    // business methods
}

@Named Annotation ဟာ Bean တွေကို နာမည်ပေးခွဲခြားနိုင်တဲ့ Annotation ဖြစ်ပါတယ်။ Default အတိုင်းဆိုပါက class name ရဲ့ အစစာလုံးကို lower case နဲ့ စပြီး ပေးမှာ ဖြစ်ပါတယ်။ အထက်ပါနမူနာ အရ FileModel ဆိုရင် name ဟာ fileModel ဖြစ်ပါလိမ့်မယ်။ တကယ်လို့ Default Name အတိုင်း မသုံးလိုဘူးဆိုရင် @Named Annotation ရဲ့ value attribute တန်ဖိုးကို ပြောင်းပြီး အသုံးပြုနိုင်ပါတယ်။ @Named(“file”) ဆိုရင် အမည်ဟာ file ဖြစ်သွားပါမယ်။

Injection Point နေရာမှာတော့ @Named နဲ့ @Inject ကို သုံးပြီး Inject လုပ်နိုင်မှာ ဖြစ်ပါတယ်။ Injection Point မှာရှိတဲ့ Instance Name ဟာ Inject လုပ်မည့် name နဲ့ အတူတူဆိုရင်တော့ ဒီအတိုင်းကို Inject လုပ်နိုင်မှာ ဖြစ်တယ်။

ExamRegister.java
public class ExamRegistration {
    
    @Named
    @Inject
    private Model examModel;

    // business methods
}

တကယ်လို့ Instance Name နဲ့ Inject လုပ်မည့် Bean ရဲ့ name ဟာ မတူဘူးဆိုရင်တော့ Injection Point မှာ ရေးထားတဲ့ @Name ရဲ့ value မှာ အသုံးပြုလိုတဲ့ Bean ရဲ့ name ကို ရေးသားပေးရမှာ ဖြစ်ပါတယ်။

ExamModel.java
@Named("exams")
public class ExamModel implements Model {
    // business methods
}

ExamRegister.java
public class ExamRegistration {
    
    @Named("exams")
    @Inject
    private Model model;

    // business methods
}

@Named Annotation ကို အသုံးပြုပြီး Bean တွေကို ခွဲခြားသတ်မှတ်နိုင်တာမှန်ပေမဲ့ name တွေကို String တွေနဲ့ သတ်မှတ်နေရတဲ့အတွက် ရေးထားတာမှားသွားတာတို့ ဖြစ်နိုင်ပါတယ်။ Type Safe ဖြစ်တယ်လို့ မဆိုနိုင်ပါဘူး။

Type Safe ဖြစ်စေချင်ရင်တော့ Custom Qualifier တွေကိုရေးသားအသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။


Custom Qualifiers

Java EE specifications တွေ အတော်များများဟာ Annotation တွေကို အသုံးပြုပြီး သူတို့ရဲ့ Service တွေကို အလွယ်တကူရေးသားနိုင်အောင် ဆောင်ရွက်ထားပါတယ်။ JPA မှာ ဆိုရင် Bean တစ်ခုကို Entity Object တွေ အဖြစ်အသုံးပြုနိုင်အောင် @Entity တို့ EntityManager ကို Inject လုပ်နိုင်အောင် @PersistenceContext တို့ EntityManagerFactory အတွက်ဆိုရင် @PersistenceUnit တို့ကို ပြင်ဆင်ထားပါတယ်။ EJB မှာဆိုရင်လဲ @Stateless, @Statefull, @Singleton တို့ @EJB တို့ကို အလွယ်တကူ အသုံးပြုနိုင်အောင် ပြင်ဆင်ထားလေ့ရှိပါတယ်။

CDI နဲ့ Beans Validation တွေမှာလဲ မိမိတို့ အသုံးပြုနိုင်အောင် ပြင်ဆင်ထားတာတွေရှိပြီး လိုအပ်လာရင် မိမိကိုယ်ပိုင် Annotation တွေကိုလဲ ရေးသား အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

Qualifier ဆိုတာကတော့ မိမိကိုယ်တိုင်ရေးသားရမည့် Annotation တစ်မျိုးဖြစ်ပါတယ်။ ဘယ်လိုနေရာမှာသုံးလဲ ဆိုတော့ @Named တွေလိုပဲ Injection Point မှာ Inject လုပ်လို့ရတဲ့ Bean တွေ တစ်ခုထက်မက ရှိနေတဲ့အခါမှာ ခွဲခြားပေးဖို့ အသုံးပြုပါတယ်။ ရှေ့မှာ ဖေါ်ပြခဲ့သလိုပဲ @Named ကို သုံးရင် Bean တွေကို String တွေနဲ့ သတ်မှတ် နေတဲ့အတွက် စာလုံးပေါင်းမှားတာတို့ဖြစ်နိုင်ပါတယ်။ ဒါပေမဲ့ Qualifier ဟာ Annotation ဖြစ်တဲ့ အတွက် Type Safe ဖြစ်စွာနဲ့ ခွဲခြားပေးနိုင်ပါမယ်။

Qualifier ကို အသုံးပြုမယ်ဆိုရင်တော့ အရင်ဆုံး အသုံးပြုမည့် Qualifier Annotation ကို ရေးသားရပါမယ်။ ပြီးမှ Inject လုပ်မည့် Class မှာကော Injection Point မှာပါ Qualifier Annotation ကို ရေးသားပေးရမှာပါ။
@Qualifier ဆိုတာ Annotation ရေးတဲ့နေရာမှာ အသုံးပြုရတဲ့ Meta Annotation တစ်ခုပါ။ Qualifier တစ်ခုရေးမယ်ဆိုရင် @Qualifier ကို တပ်ဆင် ရေးသားထားရပါမယ်။ ဒါမှ မိမိရေးသားထားတဲ့ Annotation ဟာ Qualifier တစ်ခုဖြစ်ကြောင်း Container ကို ပြောပြနိုင်မှာ ဖြစ်ပါတယ်။

File.java
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
public @inerface File {
 
}

@Target နဲ့ @Retention တို့ကတော့ Annotation ရေးပြီဆိုရင် မဖြစ်မနေရေးသားရမည့် Meta Annotation တွေ ဖြစ်ပါတယ်။ @Target ဆိုတာကတော့ ရေးထားတဲ့ Annotation ကို ဘယ်နေရာမှာ ရေးလို့ရလဲ ဆိုတာကို သတ်မှတ်ပေးတာပါ။ @Retention ကတော့ ရေးထားတဲ့ Annotation ကို ဘယ်မှာ သုံးမလဲ ဆိုတာကို ဖေါ်ပြပေးမှာပါ။ Qualifier တွေကို Runtime မှာ Container ကနေ အသုံးပြုမှာ ဖြစ်တဲ့အတွက် RUNTIME လို့ ရေးပေးထားရခြင်းဖြစ်ပါတယ်။

FileModel.java
@File
public class FileModel implements Model {
    // business methods
}

ပြီးရင်တော့ ရေးသားထားတဲ့ @File Annotation ကို Qualifier အနေနဲ့ Inject လုပ်မည့် Bean ကို ရေးသားပေးရပါမယ်။ အလားတူပဲ Injection Point နေရာမှာလဲ @File Annotation ကို ရေးသားထားမယ်ဆိုင် Container ကနေ FileModel Bean ကို Injection Point နေရာမှာ Inject လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။

FileRegister.java
public class ExamRegistration {
    
    @File
    @Inject
    private Model model;

    // business methods
}

Qualifier Annotation တွေကို တစ်ခုထက်မက ရေးသား အသုံးပြုနိုင်ပါတယ်။

XmlFileModel.java
@File
@XMLFile
public class XmlFileModel implements Model {
    // business methods
}

FileRegister.java
public class ExamRegistration {
    
    @File
    @XMLFile
    @Inject
    private Model model;

    // business methods
}

အထက်ပါ အတိုင်း Bean တစ်ခုမှာ Qualifier တွေကို တစ်ခုထက်မက ရေးသားနိုင်ပါတယ်။ နမူနာထဲမှာတော့ XmlFileModel Bean ရဲ့ ရှေ့မှာ @File နဲ့ @XMLFile ဆိုပြီး Qualifier နှစ်ခုကို ရေးသားထားပါတယ်။ အဲ့ဒီ Bean ကို Inject လုပ်ချင်ရင်တော့ Injection Point မှာလဲ @File နဲ့ @XMLFile တို့ကို @Inject နဲ့ တွဲပြီး ရေးသားရမှာ ဖြစ်ပါတယ်။


Qualifier with Parameters

တကယ်လို့ Injection Point မှာ Inject လုပ်နိုင်တဲ့ Bean တွေ ၁၀ခုလောက်ရှိနေပြီဆိုရင် ဘယ်လိုလုပ်မလဲ။ Bean တစ်ခုအတွက် Qualifier တစ်ခုရေးနေရရင် မလွယ်ဘူး။ ပိုပြီး ရှုပ်ထွေးသွားနိုင်ပါတယ်။ အဲ့ဒီလို အခါမျိုးဆိုရင်တော့ Qualifier ကို တစ်ခုထဲရေးပြီး Qualifier ထဲကနေ လုပ်အပ်သလို Parameter တွေကို ယူပြီး သုံးသွားနိုင်ပါတယ်။

Qualifier Annotation တွေကို Enum နဲ့ တွဲပြီးအသုံးပြုမယ်ဆိုရင် Type Safe မဖြစ်မှာ စိတ်ပူစရာမလိုပါဘူး။
ModelType.java
@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
public @inerface ModelType {

    public enum Type {File, XMLFile, RemoteFile, SessionCache}

    Type value();
}
အထက်ပါအတိုင်း ModelType Qualifier ထဲမှာ Type ဆိုတဲ့ enum ကို ရေးသားထားပါမယ်။ ပြီးရင် Type ကို Attribute အနေနဲ့ ပြန်ယူနိုင်မှာ ဖြစ်ပါတယ်။ method name ကို value လို့ပေးထားတဲ့အတွက် Annotation ကို ရေးသားတဲ့နေရာမှာ attribute name ကို မရေးပဲနေမယ်ဆိုလဲ ရပါမယ်။

အသုံးပြုလိုတဲ့ အမျိုးစားရှိသလောက် enum ထဲ ထည့်ရေးထားမယ်ဆိုရင် Qualifier တစ်ခုထဲနဲ အားလုံးမှာ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။
XmlFileModel.java
@ModelType(XMLFile)
public class XmlFileModel implements Model {
    // business methods
}
ExamRegister.java
public class ExamRegistration {
    
    @Inject
    @ModelType(XMLFile)
    private Model model;

    // business methods
}
အထက်ပါအတိုင်း XmlFileModel ကို @ModelType ကို သုံးပြီး Parameter အနေနဲ့ XMLFile enum တန်ဖိုးကို သတ်မှတ်ရေးသားပြီး၊ Injection Point မှာလဲ အလားတူရေးသားပြီး Inject လုပ်နိုင်မှာ ဖြစ်ပါတယ်။


@Alternatives

Java EE ဆိုတာက Enterprise Application ကို ရေးသားဖို့အတွက်ပါ။ Enterprise Application ဆိုတာ အတော်လေးကို ကြီးမားလေ့ရှိပါတယ်။ Application တစ်ခုကို နှစ်နဲ့ချီ လူဦးရေ ရာနဲ့ချီပြီး ရေးသားရတဲ့ Application တွေလဲ ရှိပါတယ်။ နည်းပညာတစ်ခုထဲမဟုတ်ပဲ Business Domain တွေရဲ့ Knowledge တွေလဲ အများကြီး လိုအပ်ပါတယ်။ ရေးသားလိုတဲ့ Business က ဘယ်လိုရှိတယ် ဆိုတာကို နားမလည်ပဲ ရေးလို့ရမှာ မဟုတ်ပါဘူး။

ဆိုလိုတာက တစ်ယောက်ထဲရေးရတဲ့ Application မျိုးမဟုတ်ပါဘူး။ အဲ့ဒီလို လူပေါင်းများစွာ Team တွေနဲ့ အလုပ်လုပ်တဲ့အခါမှာ ကိုယ်ရေးနေတဲ့ Module ကို အခြား Team တွေကလဲ အသုံးပြုနေသလို ကိုယ်ကလဲ အခြား Team တွေ ရေးထားတဲ့ Module တွေကို သုံးနေမှာပါပဲ။ ပြီးတော့ Development လုပ်တဲ့နေရာမှာ Team အားလုံးက ရေးနေကြမှာပဲ။

အဲ့ဒီလို အနေအထားမျိုးမှာ Module တစ်ခုမပြီးသေးလို့ အခြား Module တွေကို ရေးလို့မရဘူးဆိုရင် အတော်လေးကို ပြဿနာတက်မှာပဲ။ ပုံမှန်အားဖြင့်တော့ Moak Class တွေကို အစားထိုးပြီး ရေးကြ ပြီးရင် Test လုပ်ကြ လုပ်နေလေ့ရှိပါတယ်။

ဒီလိုနေရာမျိုးမှာ အသုံးဝင်တာကတော့ CDI ရဲ့ @Alternatives Qualifier Annotation ပဲ​ဖြစ်ပါတယ်။ @Alternatives ဟာ CDI ရဲ့ Built In Annotation တစ်ခုပါပဲ။ အဲ့ဒီအတွက် အသုံးပြုလိုတဲ့ Moak Clas မှာ ဒီအတိုင်း ရေးသားလို့ရပါတယ်။ @Alternatives ကို အသုံးပြုမယ်ဆိုရင် တစ်ခုသတိထားရမှာက CDI ရဲ့ Deployment Descriptor ဖြစ်တဲ့ beans.xml ထဲမှာ သွားရောက်ရေးသားထားဖို့လိုတဲ့ အချက်ပါပဲ။

လက်တွေ့နမူနာ တစ်ခုကို ရေးသားကြည့်ပါမယ်။ ကျွန်တော်တို့က PayRoleService ဆိုတဲ့ Class ကို ရေးနေပါတယ်။ အဲ့ဒီအထဲမှာ Holiday တွေကို သိအောင် CalendarService ကို ယူသုံးနေတယ်။ ဒါပေမဲ့ အခြား Team တစ်ခုကနေ CalendarService ကို ရေးနေတာ မပြီးသေးဘူး။ အဲ့ဒီလို အနေအထားမျိုးမှာ ကျွန်တော်တို့က CalendarServiceMoak ဆိုတဲ့ Class ကို ရေးပြီး ကိုယ်ရေးနေတဲ့ Module ကို ရေးနေ စမ်းနေလို့ရပါတယ်။
CalendarService.java
@Local
public interface CalendarService {
    List<Date> getHoliday(Date startOfMonth);
}
ဒါကတော့ ကျွန်တော်တို့အသုံးပြုလိုတဲ့ Interface ပါ။ အဲ့ဒီ Implementation ကို အခြား Team တစ်ခုက ရေးနေတာ မပြီးသေးပါဘူး။ ကျွန်တော်တို့ ရေးရမည့် Module ကတော့ အောက်က အတိုင်းပါ။ Inject တွေ သုံးပြီး ရေးလို့ရတာ မှန်ပေမဲ့ Implementation Class မပြီးမချင်း Test လုပ်လို့မရဘူးဖြစ်နေပါမယ်။
PayRolService.java
@Stateless
public class PayRolService implements PayRolServiceLocal {
    
    @Inject
    private CalendarService calendarService;

    // business logic methods
}
အဲ့ဒီလို အနေအထားမျိုးမှာ ကျွန်တော်တို့တွေ Alternatives ကို သုံးပြီး Test လုပ်နေလို့ရပါတယ်။ Alternative ကို သုံးဖို့ဆိုရင် Alternatives အနေနဲ့ အသုံးပြုလိုတဲ့ Class မှာ Alternatives Annotation ကို ရေးသားရပါမယ်။
CalendarServiceMoak.java
@Alternatives
@Stateless
public class CalendarServiceMoak implements CalendarService {
    
    public List<Date> getHolidays(Date startOfMonth) {

        List<Date> result = new ArrayList<>();

        // adding test data

        return result;
    }
}
ပြီးရင် beans.xml မှာ Alternative အနေနဲ့ အသုံးပြုမည့် Class ကို သွားရေးထားရပါမယ်။
beans.xml
<?xml version="1.0"?>
<beans bean-discovery-mode="all" version="1.1"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
  http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">

 <alternatives>
  <class>com.jdc.payrol.alter.CalendarServiceMoak</class>
 </alternatives>

</beans>
အထက်ပါ​အတိုင်း ရေးသားထားမယ်ဆိုရင် PayRolService ကို အသုံးပြုတိုင်းမှာ Alternative အနေနဲ့ ရေးသားထားတဲ့ CalendarServiceMoak ကို အသုံးပြုသွားမှာ ဖြစ်ပါတယ်။


Conclusion


ဒီ Post ထဲမှာ Injection Point တစ်ခုမှာ Inject လုပ်နိုင်တဲ့ Beans တွေ တစ်ခုထက်မကရှိခဲ့ရင် Qualifier ကို သုံးပြီး ဘယ်လို ရေးသားရမယ်ဆိုတာကို အဓိကထားဖေါ်ပြခဲ့ပါတယ်။ Qualifier တွေထဲမှာမှ Built In ပါဝင်တဲ့ @Default, @Any နဲ့ @Named Qualifier တွေ အပြင် Customer Qualifier တွေကို ဘယ်လိုရေးရမယ်ဆိုတာကိုလဲ ဖေါ်ပြခဲ့ပါတယ်။ တဖန် @Alternatives ရဲ့အသုံးပြုပုံကိုလဲ ဖေါ်ပြထားပါတယ်။

ဆက်လက်ပြီး CDI Bean တွေရဲ့ Lifecycle နဲ့ Callbacks တွေရဲ့ အသုံးပြုပုံတို့ CDI Bean မဟုတ်တဲ့ Beans တွေကို Inject လုပ်ချင်ရင် ဘယ်လိုရေးရမယ်ဆိုတာကို နောက် Blog ဖြင့် ဖေါ်ပြပါဦးမယ်။

ဆက်ပါဦးမယ်
မင်းလွင်

December 16, 2016

Dependency Injection - Part 1

Dependency Injection(DI) ဟာ CDI ရဲ့ အဓိက Feature တစ်ခုဖြစ်ပါတယ်။ OO Design Pattern တစ်ခုဖြစ်တဲ့ Dependency Injection Pattern ကို အခြေခံထားပါတယ်။ DI Pattern ဟာ Dependency တွေ အတွက် Hard Coding နဲ့ ရေးနေရတာကို ဖြောက်ဖျက်ပေးနိုင်ပါတယ်။ အဲ့ဒါကြောင့် Module တွေကြားမှာရှိတဲ့ Coupling ကို လျော့ချပေးနိုင်ပါတယ်။ အကျိုးဆက်အနေနဲ့ Maintainability ကို တိုးမြင့်စေနိုင်ပါတယ်။


What is Dependency?


ဥပမာအားဖြင့် MessageService Class က MailSender ဆိုတဲ့ Class ကို အသုံးပြုပြီး Message တွေကို Send လုပ်နေကြတယ်ဆိုပါစို့။ MessageSerivce အတွက် MailSender ဟာ မရှိမဖြစ်လိုအပ်နေပါတယ်။ ဘာလို့လဲဆိုတော့ MailSender မရှိပဲ Message ကို Send လုပ်လို့ရမှာ မဟုတ်လို့ပါ။
public class MailSender {
    
    public void send(String address, String message) {
        // sending message
    }
}


public class MessageService {
    
    private MailSender sender = new MailSender();

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

ဒီအတိုင်းရေးထားတယ်ဆိုရင် လတ်တလောတော့ အစဉ်ပြေနေပါမယ်။ ဒါပေမဲ့ အကြောင်းတစ်မျိုးမျိုးကြောင့် MessageService ကို SMS Message ကိုပါ Send လုပ်ဖို့လိုအပ်လာပြီဆိုကြပါစို့။

ဒီနေရာမှာ ပြဿနာ ၂ ခုရှိနေပါတယ်။ MessageService ဟာ High Level Class ဖြစ်ပြီး MailSender ဟာ Low Level Class ဖြစ်ပါတယ်။ ပထမတစ်ခုက High Level Class ဖြစ်တဲ့ MessageService ဟာ Low Level Class ဖြစ်တဲ့ MailSender ကို တိုက်ရိုက်အသုံးပြုနေတဲ့အတွက် MailSender ကလွဲရင် တစ်ခြား Object တွေနဲ့သုံးလို့ရမှာ မဟုတ်တော့ဘူး။

အဲ့ဒီအစား ပိုပြီး Abstraction ကျတဲ့ Abstract Class ဒါမှမဟုတ် Interface ကို အသုံးပြုမယ်ဆိုရင် တစ်ခြား အုပ်စုတူ Class တွေကို အစားထိုးသုံးမယ်ဆိုရင် အသုံးပြုနိုင်မှာ ဖြစ်တယ်။ ဒါ့ကြောင့် ကုဒ်တွေကို ဒီလို ပြောင်းကြည့်ရအောင်။ Sender ဆိုတဲ့ Interface တစ်ခုကို ဆောက်မယ်။ ပြီးတော့ send(address, message) method ကို ရေးထားလိုက်မယ်။ ပြီးမှ MailSender ကနေ Sender ကို Implement လုပ်လိုက်မယ်။
public interface Sender {
    void send(String address, String message);
}

public class MailSender implements Sender{
    
    @Override
    public void send(String address, String message) {
        // sending message
    }
}


public class MessageService {
    
    private Sender sender = new MailSender();

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

ဒါပေမဲ့ ဒီလိုရေးပြန်ရင်လဲ ပြဿနာ နောက်တစ်ခုကျန်နေပါသေးတယ်။ Sender Object ကို new MailSender() ဆိုပြီး Hard Coding နဲ့ Object ကို ဆောက်နေတဲ့နေရာဖြစ်ပါတယ်။ Dependency Object က Abstraction ဖြစ်တဲ့ Sender Interface ဆိုပေမဲ့ Object ဆောက်တာကို Class ထဲမှာ ရေးထားတဲ့အတွက် MailServcie မှာ SMS Send လုပ်ချင်ရင် MessageService ကို ပြုပြင်မှ ဖြစ်ပါမယ်။ ဒါ့ကြောင့် Class ထဲမှာ Dependency တွေကို Hard Coding နဲ့ Object ဆောက်နေတာဟာ Coupling ကို ပိုမိုမြင့်မားစေပါတယ်။

ကဲ ဒါဖြင့် Class ထဲမှာ Object မဆောက်ပဲ အဲ့ဒီ Dependency တွေကို ရရှိအောင် ဘယ်လိုလုပ်မလဲ။ နည်းလမ်း ၂ မျိုးရှိနိုင်ပါတယ်။ Constructor Argument ကနေတဆင့် Dependency Object ကို ရယူပြီး Dependency Object တွေမှာ အစားထိုးရင် ရနိုင်ပါတယ်။
public class MessageService {
    
    private Sender sender;

    public MessageService(Sender sender) {
     this.sender = sender;
    }

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

နောက်တနည်းကတော့ setter method တွေနဲ့ Dependency တွေကို Set လုပ်တဲ့နည်းပါ။
public class MessageService {
    
    private Sender sender;

    public void setSender(Sender sender) {
     this.sender = sender;
    }

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

အထက်ပါ အတိုင်း Constructor Argument ဒါမှ မဟုတ် setter တွေနဲ့ Dependency တွေကို Set လုပ်ပေးမယ်ဆိုရင် MessageService ဟာ သူ့ရဲ့ Dependency ဖြစ်တဲ့ Sender နဲ့ Coupling တွေကို ကင်းဝေးစေနိုင်ပါတယ်။ ဒါ့ကြောင့် MessageService နဲ့ Mail ကို ပို့ချင်ရင် MailSender Object ကို set လုပ်ပေးလိုက်ရုံပါပဲ။ တဖန် SMS ကို ပို့ချင်ရင် Sender Interface ကို Implement လုပ်ပြီး SMSSender Class ကို ဆောက်ပြီး အသုံးပြုရုံပါပဲ။

MessageService ကို သူ့ရဲ့ Dependency ကို မှီခိုမှု့မရှိအောင် လုပ်လိုက်နိုင်တဲ့အတွက် အဲ့ဒီ Object ကို နေရာအမျိုးမျိုးမှာ အသုံးချသွားနိုင်ပါတယ်။

ဒါပေမဲ့ MessageService မှာ Dependency တွေကို ဆောက်စရာမလိုအပ်တော့ပေမဲ့ MessageService ကို သုံးတဲ့ နေရာမှာ Object တွေကိုတော့ ဆောက်နေရဦးမှာပါပဲ။ တစ်နေနေရာမှာ Coupling ကတော့ ကျန်နေဦးမှာ ဖြစ်ပါတယ်


Dependency Injection With CDI

CDI ကို အသုံးပြုမယ်ဆိုရင် မိမိရဲ့ Application တွေထဲမှာရှိတဲ့ Class တွေရဲ့ Dependency တွေနေရာမှာ @Inject လို့ရေးသားပြီး Runtime မှာ Container ကနေ လိုအပ်တဲ့ Object တွေကို တည်ဆောက်ပြီး Inject လုပ်ပေးနိုင်အောင် ဆောင်ရွက်ထားပါတယ်။ ဒါ့ကြောင့် မိမိရဲ့ Application ထဲမှာ ဘယ်နေရာမှာမှ Object ဆောက်စရာ လိုအပ်တော့မှာ မဟုတ်ပါဘူး။ အရှေ့ကနမူနာနေရာမှာ ကို CDI နဲ့ ရေးကြည့်ပါမယ်။
public class MessageService {
 
    @Inject    
    private Sender sender;

    public void send(String address, String message) {
        sender.send(address, message);
    }
}

အများကြီး ရှင်းရှင်းလင်းလင်းရေးနိုင်တာကို တွေ့ရပါမယ်။ MessageService ထဲမှာ Sender Object နေရာမှာ @Inject Annotation ကို တပ်ပြီးရေးထားရုံနဲ့ သက်ဆိုင်ရာ Object ကို Container ကနေ အလိုအလျောက် လာပြီး Inject လုပ်ပေးနိုင်ပါလိမ့်မယ်။


Where can Inject?

ဘယ်နေရာတွေမှာ @Inject Annotation တွေကို ရေးသားလို့ရလဲ။ အထက်ပါ အတိုင်း Instance Variable တွေရဲ့ရှေ့မှာရေးသားနိုင်သလို Conctructor တွေရဲ့ ရှေ့မှာကော၊ setter method တွေရဲ့ရှေ့မှာလဲ ရေးသားနိုင်ပါတယ်။
public class MessageService {
 
    private Sender sender;

    @Inject    
    public void setSender(Sender sender) {
     this.sender = sender;
    }

    public void send(String address, String message) {
        sender.send(address, message);
    }
}
အထက်ပါ နမူနာကတော့ Setter Method မှာ @Inject Annotation ကို ရေးသားထားတာဖြစ်ပါတယ်။
public class MessageService {
 
    private Sender sender;

    @Inject    
    public MessageService(Sender sender) {
     this.sender = sender;
    }

    public void send(String address, String message) {
        sender.send(address, message);
    }
}
အထက်ပါကုဒ်ထဲမှာတော့ Argument ကို ယူတဲ့ Constructor ရှေ့မှာ @Inject ကို အသုံးပြုထားတာဖြစ်ပါတယ်။ ပြန်ကြည့်မယ်ဆိုရင် Depencency Object တွေကို set လုပ်လို့ရတဲ့နေရာတွေကြီးပါပဲ။


ဘယ်လို Class တွေကို Inject လုပ်ပေးနိုင်တာလဲ

ဒီနေရာမှာ စကားလုံးသုံးပုံကြောင့် အထင်မှားနိုင်ပါတယ်။ Inject လုပ်တဲ့နေရာမှာ ပါဝင် ပတ်သက်နေတာက နှစ်ခုရှိနိုင်ပါတယ်။ Injection Point မှာရေးထားတဲ့ Type နဲ့ အဲ့ဒီ​နေရာမှာလာပြီး Inject လုပ်မည့် Class ပါ။ ဒီနေရာမှာက ကျွန်တော်ပြောချင်တာက Inject လုပ်မည့် Class အကြောင်းဖြစ်ပါတယ်။

Injection Point မှာ Inject လုပ်မည့် Class တွေဟာ အောက်ပါ အရည် အချင်းနဲ့ ပြည့်စုံရပါမယ်။

  • Non Static Inner Class တစ်ခု မဟုတ်ရပါဘူး။
  • Concrete Class ဒါမှမဟုတ် @Decorator Annotation ကို တပ်ဆင် ရေးသားထားရပါမယ်။
  • No Argument Default Constructor တစ်ခုကို မဖြစ်မနေပိုင်ဆိုင်ရပါမယ်။
  • တကယ်လို့ Constructor မှာ Argument ကို ယူနေခဲ့ရင် အဲ့ဒီ Constructor ရှေ့မှာ @Inject ကို ရေးသားထားရပါမယ်။

အထက်ပါ အရည်အချင်းနဲ့ ပြည့်စုံမှသာ Container ကနေ Inject လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။

ဆက်ပါဦးမယ်
မင်းလွင်

December 15, 2016

To Be Enable CDI in Your Project

CDI တွေကို ဘယ်လို Project တွေမှာ အသုံးပြုနိုင်သလဲ။ ပြီးတော့ မိမိရေးသားနေတဲ့ Java Project တွေထဲမှာ CDI ကို အသုံးပြုနိုင်အောင် ဘယ်လိုလုပ်မလဲ။ ဒါတွေကို လေ့လာသွားကြပါမယ်။

အရင်ဆုံး CDI ကို ဘယ်လို Project တွေမှာ အသုံးပြုနိုင်သလဲ။ ဒါကို မစဉ်စားခင် ဘယ်လို Project မျိုးတွေရှိမလဲ ဆိုတာကို စဉ်းစားပါမယ်။ Java ရဲ့ Edition တွေအရကြည့်မယ်ဆိုင် Java SE ဆိုတဲ့ Standard Edition ရှိမယ်။ ပြီးတော့ Java EE ဆိုတဲ့ Enterprise Edition တွေရှိမယ်။ နောက် Java ME ဆိုတဲ့ Micro Edition တွေရှိပါမယ်။ Java ME ကတော့ ကျွန်တော်တို့ မြန်မာနိုင်ငံမှာ အလွယ်တကူရေးလေ့မရှိတဲ့ အတွက် ဖယ်ထားလိုက်ရအောင်။


Java SE Environments


Java SE ပတ်ဝန်းကျင်မှာတော့ အရေးများတာက Desktop Application တွေရှိတယ်ဗျာ။ အဲဒီ Desktop Application တွေမှာ CDI ကို သုံးလို့ရလားဆိုတော့ ရပါတယ်။ ဒါပေမဲ့ CDI Container လို့ခေါ်တဲ့ Library လိုအပ်ပါလိမ့်မယ်။ CDI Container အနေနဲ့ အသုံးများတာကတော့ JBoss Community က ရေးသားတဲ့ WELD ကို အသုံးပြုနိုင်ပါတယ်။ WELD တစ်ခုထဲနဲ့ ဆိုရင် CDI ရဲ့ Feature တွေ အကုန်ကို အသုံးပြုနိုင်မှာ မဟုတ်ပါဘူး။
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.jdc</groupId>
  <artifactId>cdi1</artifactId>
  <version>1.0</version>
  <properties>
      <maven.compiler.version>1.8</maven.compiler.version>
      <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  <dependencies>
      <dependency>
          <groupId>org.jboss.weld.se</groupId>
          <artifactId>weld-se</artifactId>
          <version>2.2.15.Final</version>
      </dependency>
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
      </dependency>
  </dependencies>
</project>

ဆိုလိုတာက Transaction Handling တို့လို Feature တွေကို သုံးလို့ရမှာ မဟုတ်ဘူး။

Maven Dependency ကတော့ အောက်ပါ အတိုင်းဖြစ်ပါတယ်။
CDI Container အနေနဲ့ weld-se ကို ဖြည့်စွက်ထားဖို့လိုအပ်ပါတယ်။ ဒါမှသာ Java SE ပတ်ဝန်းကျင်မှာ CDI ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ တဖန် Java SE Project တွေဟာ Default အတိုင်း ဆိုရင် CDI ရဲ့ Feature တွေကို ရမှာ မဟုတ်ဘူး။ အဲ့ဒီအတွက် resources/META-INF/ အောက်မှာ CDI ရဲ့ Deployment Descriptor File ဖြစ်တဲ့ beans.xml file ကို ရေးသားထားရပါမယ်။ Eclipse IDE ရဲ့ create > New > beans.xml ကို သုံးပြီး အလွယ်တကူ Create လုပ်နိုင်မှာ ဖြစ်ပါတယ်။

beans.xml မပါပဲနဲ့တော့ Java SE Project တစ်ခုကို CDI Project အဖြစ် အသိမှတ်ပြုမှာ မဟုတ်ဘူး။




ကျွန်တော်တို့ ဒီတစ်ခေါက်ကတော့ CDI အကြောင်းကို ရှင်းသွားမှာ မဟုတ်ပါဘူး။ ပတ်ဝန်းကျင် တိုင်းအတွက် CDI ကို အသုံးပြုနိုင်အောင် ဘယ်လို လုပ်ရမယ်ဆိုတာကိုပဲ အဓိထားပြီး လေ့လာသွားပါမယ်။ အထက်ပါ အတိုင်း Maven Dependency ကို ထည့်ပြီးနောက် beans.xml ကို ထည့်လိုက်တာနဲ့ ကျွန်တော်တို့ Java SE Project လေးဟာ CDI ကို သုံးလို့ရတဲ့ Project တစ်ခုဖြစ်လာပါပြီ။

DI အကြောင်းလေ့လာတဲ့အခါမှပဲ Java SE ပတ်ဝန်းကျင်ပေါ်မှာ CDI ကို သုံးပြီးလေ့လာသွားပါမယ်။


Java EE Environments


Java EE ပတ်ဝန်းကျင်နဲ့ ပတ်သက်ပြီး Container အကြောင်းကို အရင် သဘောပေါက်ဖို့လိုတယ်။ Java EE ရဲ့ Application Server တွေမှာ Web Container နဲ့ EJB Container ဆိုပြီးရှိပါတယ်။ Java EE Full Version ကို ရေးချင်ရင် ဒါမှမဟုတ် EJB Lite ကို သုံးချင်ရင် EJB Container ပါတဲ့ Server မျိုးနဲ့မှ ရေးလို့ရတယ်။ ဥပမာ Glassfish တို့လို JBoss Application Server တို့လို IBM ရဲ့ Web Sphere Server တွေဟာ EJB Container ပါတဲ့ Application Server တွေဖြစ်တယ်။ EJB Container ပါတဲ့ Server တွေမှာ CDI ကို သုံးချင်ရင် ဒီအတိုင်းရေးလို့ရပါတယ်။ အထွေအထူး CDI Container တွေ ရေးစရာ မလိုအပ်ပါဘူး။

Web Container သာပါတဲ့ Tomcat တို့လို Server တွေကတော့ CDI ကို ဒီအတိုင်း သုံးလို့မရပါဘူး။ Java SE ပတ်ဝန်းကျင်မှာလိုပဲ CDI Container ကို Library အနေနဲ့ ထည့်ထားမှသာ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ ဒါပေမဲ့ Apache Foundation က ရေးသားထားတဲ့ CDI Extension Library တစ်ခု ဖြစ်တဲ့ Deltaspike နဲ့ တွဲသုံးမယ်ဆိုရင်တော့ Java EE ပတ်ဝန်းကျင် မဟုတ်တဲ့ နေရာတွေမှာလဲ Java EE လို CDI ရဲ့ Feature တွေကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။


Web Application on Application Server


Application Server တွေပေါ်မှာ Web Application ကို ရေးသားမယ်ဆိုရင် war ပုံစံနဲ့ ရေးသားရမှာ ဖြစ်ပါတယ်။ Java EE 6 ကနေစပြီး Application Server တွေက CDI ကို Support လုပ်ပါတယ်။ ဒါ့ကြောင့် Application Server တွေပေါ်က Web Application တွေမှာ CDI ကို ရေးသားချင်ရင် အထွေအထူး Maven Dependency တွေကို ရေးသားစရာ မလိုအပ်ပါဘူး။

တဖန် Java EE 7 ကနေစပြီး CDI ရဲ့ Deployment Descriptor ဟာ Optional ဖြစ်လာပြီး CDI ရဲ့ Default Feature အတိုင်းသုံးနေမယ်ဆိုလို့ကတော့ beans.xml တောင် မလိုအပ်ပါဘူး။ ဒါပေမဲ့ Default Feature တွေကို ပြုပြင်ချင်တယ်ဆိုမှ beans.xml file ကို WEB-INF အောက်မှာ ရေးသားပြီး Customise လုပ်ရမှာ ဖြစ်ပါတယ်။




အထက်ပါအတိုင်း Eclipse ရဲ့ Beans XML Editor ကို သုံးပြီး မိမိ လိုအပ်သလို CDI ရဲ့ Feature တွေကို Customise လုပ်နိုင်မှာ ဖြစ်ပါတယ်။

CDI ရဲ့ Scope တွေကို လေ့လာသွားတဲ့အခါမှာ အထက်ပါ ရေးသားပုံကို အသုံးပြုသွားမှာ ဖြစ်ပါတယ်။


Enterprise Applications


Enterprise Application တစ်ခုမှာ Module တွေ တစ်ခုမက ပါဝင်နေနိုင်ပါတယ်။ ပြီးတော့လည်း Module တွေကလဲ တစ်ခုနဲ့ တစ်ခု Dependent လုပ်နေနိုင်ပါတယ်။ ဥပမာအားဖြင့် Enterprise Application တစ်ခုမှာ Business Logic တွေကို Wrap လုပ်ပေးနိုင်တဲ့ EJB Module တွေလဲ​တစ်ခုမကပါနိုင်ပါတယ်။ လုပ်ငန်းအလိုက် Web Module တွေကိုလဲ ခွဲခြားထားနိုင်ပါတယ်။ တဖန် JMS တို့ Web Service တို့အတွက် Module တွေကို ရေးသားထားနိုင်ပါတယ်။ အဲ့ဒီ Module တွေကို စုစည်းပြီး EAR အဖြစ် Archive လုပ်ရမှာ ဖြစ်ပါတယ်။




အထက်ပါ Project Structure ကတော့ Java EE ရဲ့ Full Feature ကို အသုံးပြုနိုင်တဲ့ Enterprise Application တစ်ခုကို Maven နဲ့ တည်ဆောက်ထားတာဖြစ်ပါတယ်။

ဦးစွာ ejb1 အမည်နဲ့ Maven POM Project တစ်ခုကို တည်ဆောက်ပါတယ်။ ပြီးတော့မှ EJB Module အနေနဲ့ ejb1-ejb ဆိုတဲ့ Project ကို ejb ပုံစံနဲ့တည်ဆောက်ပါတယ်။ ထို့နောက် ejb1-war အမည်နဲ့ war ပုံစံနဲ့ တည်ဆောက်ပါမယ်။

အဲ့ဒီ Module တိုင်းမှာ CDI ကို Default အတိုင်း သုံးမယ်ဆိုရင်တော့ ဘာမှ အထွေအထူး ရေးသားစရာမလိုအပ်ပါဘူး။ customize လုပ်လိုမှသာ beans.xml ကို ဖြည့်စွက်ရမှာ ဖြစ်ပါတယ်။ CDI ကို သုံးမည့် project ဟာ jar ဖြစ်ရင် META-INF အောက်မှာ ရေးသားရပြီး war project ဆိုရင်တော့ WEB-INF အောက်မှာ beans.xml ကို ရေးသားရပါမယ်။

ejb1-war ထဲကနေ ejb1-ejb ကို Reference လုပ်နေမှာ ဖြစ်ပါတယ်။ ejb1-ejb project ထဲမှာ Business Logic တွေကို Encapsulate လုပ်ထားတဲ့ EJB တွေနဲ့ Persistence Layer အတွက် JPA Entity တွေကို ရေးသားထားမှာ ဖြစ်ပါတယ်။ အဲ့ဒီ EJB တွေကို ejb1-war ထဲကနေ ယူသုံးနေမှာ ဖြစ်ပါတယ်။ ejb1-ejb ထဲက Beans တွေမှာ CDI ကို သုံးပြီး Inject လုပ်ချင်တယ်ဆိုရင်တော့ မဖြစ်မနေ ejb1-ejb project ထဲမှာ beans.xml ကို ရေးသားထားရပါမယ်။

Project ထဲမှာ beans.xml ပါမှသာ အခြား Project ကနေကြည့်ရင် CDI Beans အနေနဲ့ တွေ့ရှိမှာ ဖြစ်ပါတယ်။ ဒါ့ကြောင့် အခြား Project ထဲကနေ Reference လုပ်မည့် Project တွေဆိုရင် beans.xml ကို ရေးသားထားရမှာ ဖြစ်ပါတယ်။

ဒီလို ပုံစံတွေကိုတော့ ကျွန်တော်တို့ EJB ကို လေ့လာတဲ့ နေရာမှာ ရေးသားအသုံးပြုရမှာ ဖြစ်ပါတယ်။


Web Container's Applications


Tomcat လို Web Container တွေပေါ်မှာရေးသားတဲ့ Dynamic Web Application တွေမှာ CDI ကို သုံးချင်တယ်ဆိုရင် ဒီအတိုင်းဆိုရင် သုံးလို့ရမှာ မဟုတ်ပါဘူး။ CDI container ကို Library အဖြစ်ဖြည့်စွက်ပြီးမှသာ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ WELD Container ကိုသုံးပြီး Tomcat လို Web Container တွေမှာလဲ CDI ရဲ့ Feature တွေကို အသုံးပြုနိုင်မှာ ဖြစ်တယ်။ တဖန် Deltaspike လို CDI Extension လို Library တွေနဲ့ တွဲသုံးရင် Java EE Application တွေလိုပဲ Transaction တွေကို Handle လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။

Tomcat 7 အထက်ကို သုံးမယ်ဆိုရင် CDI ကို Servlet, Filter တွေနဲ့ Listener တွေမှာ Injection ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

လုပ်ရမှာကတော့ Maven Dependency ထဲမှာ Weld Servlet ကို Container အနေနဲ့ ဖြည့်စွက်ပါမယ်။
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.jdc</groupId>
    <artifactId>web-cdi</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.weld.servlet</groupId>
            <artifactId>weld-servlet</artifactId>
            <version>2.2.15.Final</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
    </build>
</project>

ပြီးရင် WEB-INF အောက်မှာ CDI ရဲ့ Deployment Descriptor ဖြစ်တဲ့ beans.xml ကို ရေးသားထားရုံပါပဲ။ ဒါဆိုရင် Tomcat ရဲ့ Dynamic Web Application တွေမှာ CDI ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

Project Structure ကတော့ အောက်ပါ အတိုင်းဖြစ်ပါတယ်။




ပြီးရင် စမ်းသပ်ဖို့အတွက် CDI Beans တစ်ခုကို @RequestScoped သုံးပြီးရေးသားပါမယ်။
package com.jdc.cdi;

import javax.enterprise.context.RequestScoped;

@RequestScoped
public class HelloBean {

    public String getMessage() {
        return "Hello CDI";
    }
}

ပြီးပါက အထက်ပါ HelloBean ကို Servlet ထဲမှာ Inject လုပ်ပြီး သုံးကြည့်ပါမယ်။
package com.jdc.cdi;

import java.io.IOException;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/hello")
public class HelloCDIServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    @Inject
    private HelloBean hello;
       
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().append(hello.getMessage());
    }
}

အထက်ပါ အတိုင်း Servlet ထဲမှာ CDI ကို သုံးပြီး Inject လုပ်လာနိုင်ပါမယ်။ Servlet ပေါ်မှာ CDI ကို သုံးလာနိုင်တာ ဝမ်းသာစရာ သိပ်ပြီး မကောင်းပါဘူး။ ဒါပေမဲ့ CDI Feature နဲ့ Deltaspike ကို သုံးပြီး Struts လို Framework တွေနဲ့ တွဲပြီး သုံးနိုင်မယ်ဆိုရင် Java EE ပတ်ဝန်းကျင် မဟုတ်ပေမဲ့ Java EE Feature တွေကို သုံးလာနိုင်တာဟာ စိတ်ဝင်စားစရာကောင်းပါတယ်။

ဘာလို့လဲဆိုတော့ Cloud ပတ်ဝန်းကျင်တွေမှာ Java EE Server ကို သုံးနိုင်တဲ့ Cloud တွေကို Comercial အနေနဲ့ သုံးမယ်ဆိုရင် ဈေးကြီးပါတယ်။ ဒါပေမဲ့ Tomcat လောက်ဆိုရင် သိပ်ပြီး ဈေးမကြီးတော့ပါဘူး။ ဒါ့ကြောင့် Tomcat လို Server မျိုးနဲ့ Java EE Feature တွေသုံးနိုင်တာဟာ ဝမ်းသာစရာ အချက်တစ်ခုဖြစ်ပါတယ်။


Conclusion


ကျွန်တော်တို့ ဒီတစ်ခေါက် ဘယ်လို Project တွေမှာ CDI ကို သုံးနိုင်အောင် ဘယ်လိုလုပ်မလဲ ဆိုတာကို အဓိကထားလေ့လာခဲ့ပါတယ်။ CDI ကိုဘယ်လို ရေးမလဲ​ဆိုတာ မဟုတ်သေးပါဘူး။ မိမိရေးသားနေတဲ့ Project က ဘယ်ပုံစံ အဲ့ဒီမှာ CDI ကို သုံးချင်ရင် ဘာတွေလုပ်ရမယ်ဆိုတာကို လေ့လာနိုင်မယ်လို့ ထင်ပါတယ်။

နောက် အခန်းတွေကဆက်ပြီး Dependency Injection, Scopes of CDI Beans, Interceptors, Decorators, Events အစရှိတဲ့ CDI Feature တွေကို ဆက်လက်ပြီး လေ့လာသွားပါမယ်။

ဆက်ပါဦးမယ်။
မင်းလွင်

December 14, 2016

About CDI

CDI ဟာ Java EE 6 ကျမှ Java EE Specification ထဲကို စတင်ပါဝင်လာတဲ့ Java EE ရဲ့ Standard API တစ်ခုဖြစ်ပါတယ်။ CDI ဟာ စတင်ပါဝင်ခဲ့တာ သိပ်ပြီးမကြာသေးပေမဲ့ JSF, JPA, EJB, Bean Validation တို့လို နေရာတော်တော်များများမှာ ပါဝင်ပတ်သက်နေတဲ့အတွက် မဖြစ်မနေသိရှိထားရန်လိုအပ်တဲ့ API တစ်ခုလဲ ဖြစ်ပါတယ်။

CDI ဆိုတာ Context And Dependency Injection ဆိုတဲ့ အမည်ရဲ့ အတိုကောက်အမည် တစ်ခုဖြစ်ပြီး၊ Context ဆိုတဲ့ Service နဲ့ Dependency Injection ဆိုတဲ့ Service တို့ကို အဓိက ပံ့ပိုးပေးထားတဲ့ API လဲ ဖြစ်ပါတယ်။

Context ဆိုတာက Object တွေရဲ့ Lifecycle ကို Container မှ Manage လုပ်ပေးနိုင်ပြီး၊ ထို Object တွေဟာလဲ Well Define ဖြစ်တဲ့ Scope တွေမှာ အသုံးပြုနိုင်အောင် ဆောင်ရွက်ပေးနိုင်တယ်။ တဖန် ထို Object တွေရဲ့ Lifecycle တစ်လျောက်လုံးမှာ အသုံးပြုနေတဲ့ Object တွေကိုလဲ သူနဲ့အတူတကွ အလုပ်လုပ်နိုင်အောင် ဆောင်ရွက်ပေးနိုင်တဲ့ Service ဖြစ်ပါတယ်။

Context Service ကြောင့် Java EE မှာ အသုံးပြုနေတဲ့ Component တွေကို ဘယ် Client က အသုံးပြုနေတယ်ဆိုတာကို ခွဲခြား သတ်မှတ်နိုင်ပြီး မသက်ဆိုင်တဲ့ Communicate မလုပ်အောင် စီမံပေးနိုင်မှာ ဖြစ်ပါတယ်။

Dependency Injection ဆိုတာကတော့ ထင်ရှားတဲ့ Design Pattern တစ်ခုဖြစ်ပြီး၊ Java EE ပတ်ဝန်းကျင်မှာ အသုံးပြုလိုတဲ့ Dependency Object တွေကို Application ထဲတွင် Instantiate လုပ်ရန်မလိုအပ်ပဲ၊ လိုအပ်တဲ့အချိန်မှာ Container မှ အလိုအလျောက် Inject လုပ်ပေးနိုင်တဲ့ Service ဖြစ်ပါတယ်။ Dependency Injection ကြောင့် Dependent Component တွေထဲမှာ Dependency Object တွေကို တည်ဆောက်စရာမလိုအပ်တော့တဲ့အတွက် Component တွေအကြားမှာရှိတဲ့ Coupling ကို လျော့ချစေနိုင်ပြီး Component တွေကို လွတ်လွလပ်လပ် အသုံးပြုနိုင်အောင် ဆောင်ရွက်ပေးနိုင်ပါတယ်။ ထို့ကြောင့် Reusable ဖြစ်တဲ့ Component တွေကို ရေးသားနိုင်မှာ ဖြစ်ပါတယ်။


Inversion of Controls (IoC)


CDI ကို လေ့လာတဲ့နေရာမှာ IoC ဆိုတဲ့ Keyword ကိုလဲ မကြာမကြာ တွေ့ရှိရမှာ ဖြစ်ပါတယ်။ IoC ဆိုတာဟာ Inversion of Controls ဆိုတဲ့ စကားလုံးရဲ့အတိုကောက်ပါ။ Control တွေကို ပြောင်းပြန်လုပ်ပစ်ခြင်းလို့ အဓိပ္ပါယ်ရရှိပါတယ်။

Application တွေကို ရေးသားတဲ့အခါမှာ ပုံမှန်အားဖြင့်တော့ High Level Module (Business Class) တွေထဲမှာ Low Level Module (Technical Service API) တွေကို Management လုပ်ပြီး ရေးသားလေ့ရှိပါတယ်။ ဥပမာအားဖြင့် Database Connection တွေကို Business Class တွေက လိုအပ်လာရင် ကိုယ်တိုင် တည်ဆောက်ရပြီး အသုံးမလိုတော့တဲ့အခါမှာ ပြန်ပြီး Close လုပ်နေရပါတယ်။

Control ကို ပြောင်းပြန်လုပ်တယ်ဆိုတာက Low Level Module တွေရဲ့ Management ကို Business Module တွေက (Application ထဲကနေ) လုပ်စရာမလိုပဲ Container (Application Server) ကနေ Management လုပ်ပေးတာကို ဆိုလိုခြင်းဖြစ်ပါတယ်။ Connection တွေကို Business Module တွေထဲမှာ Control လုပ်စရာမလိုအပ်တော့တဲ့အတွက် Business Module တွေကို Environment Specific ဖြစ်တဲ့ Low Level Module တွေနဲ့ ပတ်သက်မှု့ကို လျော့ချပေးစေပါတယ်။ အဲ့ဒီအတွက် Business Module တွေကို လွတ်လွတ်လပ်လပ် ရေးသားအသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။ နမူနာအားဖြင့် မိမိရဲ့ Application ကို Development State မှာတုန်းက H2DB ကို အသုံးပြုရေးသားထားပြီး Release လုပ်တော့မှ Oracle DB ကို ပြောင်းတာတို့ လုပ်ချင်တယ်ဆိုရင်တောင် Server ပေါ်က Datasource Setup ကို ပြင်လိုက်ရုံပါပဲ။ Business Module တွေကိုပြုပြင်စရာလိုအပ်မှာ မဟုတ်ပါဘူး။

CDI ဟာ IoC ဆိုတဲ့ Concept ကို အခြေခံပြီး ရေးသားထားတဲ့ API တစ်ခုဖြစ်ပါတယ်။


Loose Coupling & Strong Typing


CDI ရဲ့ Specification ကို ရေးသားခဲ့စဉ်က ရေးသားခဲ့သူများက Loose Coupling & Strong Typing ဆိုတဲ့ အချက်ကို အဓိကထားပြီး ရေးသားခဲ့ကြပါတယ်။

Loose Coupling ဆိုတာက Component တွေကြားမှာရှိတဲ့ Coupling (ချုပ်နှောင်မှု့) ကို လျော့ချပေးခြင်းကို ဆိုလိုတာပါ။ Loose Coupling ဖြစ်တော့ ဘာဖြစ်မှာလဲ။ Component တွေက ချူပ်နှောင်မှု့ကင်းတဲ့အတွက် အဲ့ဒီ Component တွေကို ကြိုက်တဲ့နေရာမှာ လွတ်လပ်စွာ အသုံးပြုနိုင်မှာ ဖြစ်ပြီး Reusable ဖြစ်တဲ့ Component တွေကို ရေးသားနိုင်မှာ ဖြစ်ပါတယ်။

Coupling ကို ဘယ်လိုဘာသာပြန်မလဲလို့ စဉ်းစားတုန်းက သံယောဇဉ်လို့ဘာသာပြန်ခဲ့ဘူးပါတယ်။ သံယောဇဉ်များလေလေ လွတ်လပ်မှု့ဆုံးရှုံးလေဖြစ်ပါမယ်။ အဲ့ဒီအတွက် ဟိုဟာလုပ်ချင်လဲ မလုပ်နိုင် ဒီဟာ လုပ်ချင်လဲ မလုပ်နိုင်ဖြစ်ပါမယ်။

အဲ့ဒီလိုပဲ Coupling များတဲ့ Component တွေဟာ လွတ်လပ်မှု့ရှိမှာမဟုတ်ပါဘူး။ အဲ့ဒီ Component ကို သုံးချင်ရင် အဲ့ဒီ Component တွေနဲ့ ပတ်သက်နေတဲ့ Component မရှိပဲ သုံးလို့မရဘူး တို့ဖြစ်လာပါမယ်။ ဟိုနေရာလဲ သုံးမရ ဒီနေရာလဲ သုံးမရဖြစ်လာပြီး သုံးရခက်တဲ့ Component ဖြစ်လာမှာ ဖြစ်ပါတယ်။

CDI ရဲ့ ဘယ် Feature တွေက Loose Coupling ကို တိုးတက်အောင်လုပ်ပေးနိုင်တာလဲ။

Dependency Injection(DI) ဆိုတာ CDI ရဲ့ အဓိက Feature တစ်ခုဖြစ်ပါတယ်။ DI ကြောင့် Java EE ပတ်ဝန်းကျင်မှာ ရေးသားနေတဲ့ Object တွေကို သူတို့ရဲ့ Dependency တွေ (သူတို့အသုံးပြုနေတဲ့ Objectတွေ) နဲ့ Decouple လုပ်ပေးနိုင်ပါတယ်။

တဖန် Contextual Lifecycle Management ဆိုတဲ့ Feature ကြောင့် Object တွေကို Well Define ဖြစ်တဲ့ Scope တွေ အတွင်းမှာ အသုံးပြုနိုင်အောင် ဆောင်ရွက်ပေးနိုင်ပြီး Lifecycle ကို Manage လုပ်ရတဲ့ Code တွေနဲ့ Decouple ဖြစ်အောင် ဆောင်ရွက်ပေးနိုင်ပါတယ်။ ထို့အပြင် CDI ရဲ့ Scope တွေကို Java EE ရဲ့ Container တွေဖြစ်ကြတဲ့ Web Container ရော EJB Container မှာပါ အသုံးပြုနိုင်ပါတယ်။ ဒါ့ကြောင့် CDI Scope တွေကို Presentation Layer မှာကော Business Layer မှာကော Persistance Layer မှာပါ အသုံးပြုနိုင်ပါတယ်။ ရလဒ်အနေနဲ့ Coponent တွေကို Manageလုပ်တဲ့ Container က မတူပေမဲ့ သူတို့တွေအားလုံးကို CDI ကို အသုံးပြုပြီး Layer တစ်ခုထဲမှာ အလုပ်လုပ်နေသလို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

Interceptor ဆိုတာက Business Logic Code တွေနဲ့ နည်းပညာအရလိုအပ်တဲ့ Technological Concern တွေကို Decouple လုပ်ပေးနိုင်ပါတယ်။ ဥပမာအားဖြင့် Database ထဲကို Record တစ်ကြောင်း Insert လုပ်ချင်တယ်။ ဒါပေမဲ့ ရေးသားမယ်ဆိုင် Insert မလုပ်ခင် Trasaction ကို Begin လုပ်ပေးရမယ်။ ပြီးရင် Commit လုပ်မယ်။ Error ဖြစ်ရင် Roll Back လုပ်မယ်။ ဒါတွေကို နေရာတိုင်းမှာ ရေးသားနေရမှာပါ။ အဲ့ဒီလို Technical အရလိုအပ်တဲ့ Code တွေကို Interceptor ထဲမှာ ရေးသားထားပြီး Business Method တိုင်းမှာ လုပ်ဆောင်စေနိုင်ရန် ရေးသားနိုင်ပါတယ်။ ဒါ့ကြောင့် Business Method တွေထဲမှာ လိုအပ်တဲ့ Code တွေသာ ရေးသားရန်လိုအပ်တဲ့အတွက် Code တွေရဲ့ Readablity ကိုလဲ တိုးတက်စေနိုင်ပြီး Maintainablity ကိုလဲ မြင့်မားစေနိုင်ပါတယ်။ တဖန် Developer တွေဟာ မိမိရဲ့ Business Logic ကိုသာ အာရုံစိုက်စရာ လိုအပ်တော့တဲ့အတွက် အမှား တွေကိုလဲ လျော့ချစေနိုင်ပါတယ်။ Interceptor ဆိုတာက Aspect Oriented Programming (AOP) နဲ့ တူညီတဲ့ ရလဒ်တွေကို ဆောင်ရွက်ပေးနိုင်ပါတယ်။

Decorator ဆိုတာက ထင်ရှားတဲ့ Design Pattern တစ်ခုဖြစ်ပြီး CDI မှာလဲ​ အသုံးပြုနိုင်အောင် ပြင်ဆင်ထားပါတယ်။ Decorator ဆိုတာက မူရင်း Service ရဲ့ရလဒ်ကို အမျိုးမျိုး Decorate လုပ်ရင်း မတူညီတဲ့ ရလဒ်တွေကို ရရှိစေနိုင်တဲ့ Design Pattern တစ်မျိုးဖြစ်ပါတယ်။ မူလ Service ရဲ့ ရလဒ်နဲ့ မတူညီတဲ့ ရလဒ် အသစ်တစ်မျိုးရရှိလိုပါက Decorate လုပ်ချင်းအားဖြင့် မတူညီတဲ့ Service အသစ်တွေကို ဖန်တီးပေးနိုင်မှာ ဖြစ်ပါတယ်။ ကျွန်တော်တို့ပတ်ဝန်းကျင်မှာလဲ Decorator Pattern ကို သုံးနေတာကို နေ့စဉ်တွေ့မြင်နိုင်ပါတယ်။ နမူနာအားဖြင့် လဘက်ရည်ဆိုင်တစ်ဆိုင်ဆိုကြပါဆို့။ အကျရည်ရှိတယ် ဆိုရင် ချို့ကျတို့ ချိုစိမ့်တို့ ချိုပေါ့ကျတို့ နို့ဆီ သကြားတို့ကို ပုံစံအမျိုးမျိုးနဲ့ Decorate လုပ်ရင်း Service အမျိုးမျိုး ကို ဆောင်ရွက်ပေးနိုင်နေခြင်းဖြစ်ပါတယ်။

Event ဆိုတာကလဲ CDI ရဲ့ ထူးခြားတဲ့ Feature တစ်ခုရှိပါတယ်။ Event ဆိုတာကတော့ Observable Design Pattern ကို အခြေခံထားတဲ့ Feature ဖြစ်ပါတယ်။ Java EE Application ထဲမှာရှိတဲ့ ကြိုက်တဲ့ Component က Event Producer ဖြစ်နိုင်ပြီး၊ ကြိုက်တဲ့ Component က Event Consumer ဖြစ်နိုင်ပါတယ်။ Event Producer က Event တစ်ခုကို Fire လုပ်လိုက်တာနဲ့ သတ်မှတ်ထားတဲ့ Event Consumer တွေက အလိုအလျောက် ထပြီး အလုပ်လုပ်ပေးမှာ ဖြစ်ပါတယ်။ CDI ရဲ့ Event Producer နဲ့ Event Consumer တွေဟာ Compile Time မှာတောင် အချင်းချင်း သိနေစရာမလိုအပ်ပါဘူး။

CDI ဟာ Dependency Inject တွေကို ဆောင်ရွက်တဲ့ နေရာမှာ Type Safe ဖြစ်အောင် String တွေနဲ့ရေးသားရတဲ့ နည်းအစား Qualifier ဆိုတဲ့ Annotation ကို အသုံးပြုနိုင်အောင် ဆောင်ရွက်ထားပါတယ်။ ဒါ့ကြောင့် အမည်တူပေးပြီး Type တွေကို မှားပြီး Inject မလုပ်ဖြစ်အောင် ဆောင်ရွက်ပေးနိုင်ပါတယ်။


Brief History of CDI






What new in CDI 1.1


  1. Managed Bean တွေရဲ့ ပြင်ပမှ CDI class တွေမှတဆင့် CDI facilities တွေကို ဆက်သွယ် အသုံးပြုလာနိုင်ပါတယ်။
  2. Interceptors, decorators, and alternatives တွေရဲ့ Priority ကို @Priority ကို အသုံးပြုပြီး ခွဲခြားသတ်မှတ်နိုင်ပါတယ်။
  3. Type တွေ၊ ဒါမှမဟုတ် package တွေမှာ @Vetoed annotation ကို ရေးသားပြီး CDI မှနေ Bean တစ်ခု အဖြစ်မစဉ်းစားအောင် တားစီးနိုင်ပါတယ်။
  4. @New qualifier ကို ရပ်နားပြီး၊ @Dependent scoped beans ကိုအသစ်Inject လုပ်နိုင်ပါတယ်။
  5. @WithAnnotations အားအသုံးပြုပြီး အသုံးပြုနိုင်တဲ့ Type တွေကို Filter လုပ်နိုင်လာပါတယ်။

ဆက်ပါဦးမည်။
မင်းလွင်