วันอาทิตย์ที่ 13 เมษายน พ.ศ. 2557

คึกอยากจะทบทวน Spring Framework part 7

: Injection เหล่าคลาสโดยการใช้ Autowire

ถาม : ขอคำตอบง่ายๆทำไมต้อง Injection?
ตอบ : เพราะไม่อยาก new object เอง ถึงเวลา (want) ก็ฉีด (injection) object นั้นให้ไปใช้เลย

ถาม : ขอคำตอบง่ายๆทำไมต้อง Autowire?
ตอบ : เพราะไม่อยากเขียน config ที่ไฟล์ XML เยอะๆไง (มันเยอะมันยุ่ง มันงง)

ถาม : แล้ว Injection เกี่ยวกับ Autowire อย่างไร?
ตอบ : มันเป็น concept บวกกับวิธีการที่ว่า How to? อธิบายง่ายๆได้ว่า โดยปกติแล้ว object ที่จะถูกใช้งานก็ต้อง new มันขึ้นมาก่อนใช่ไหม? ใช่ นอกจากนี้บรรดา object อาจต้องการทรัพยากร (arguments) หนึ่งสองสามสี่ห้า ไม่รู้แหละ (ใครเคยโค้ดโปรแกรมใหญ่ๆจะรู้ว่ามันทรมารใจมากที่เห็น parameter ยาวเป็นหางงู) จึงต้องหาวิธีการสักอย่างที่จะนำทรัพยากรดังกล่าวใส่ลงไปใน object แบบโค้ดสะอาดที่สุด
- โค้ดในไฟล์ .xml น้อยลงก็ต้องใช้ Autowire
- เตรียม object พร้อมใช้ ก็แค่รอฉีด (injection) มันเข้าไป (ไม่ต้อง new เอง)

ถาม : ชักอยากเห็นการใช้ Injection และ Autowire
ตอบ : เอา project จาก part 6 มาแก้และเพิ่มเติมโค้ดดังนี้สิ

>> autowire="constructor"

เปิดไฟล์ applicationContext.xml ลบหรือ comment แท็ก <property ... ></property> แล้วเพิ่ม autowire เข้าไปแทน (ลดโค้ด โค้ดน้อยลง โค้ดก็สะอาดขึ้น) ดังด้านล่าง

<bean name="customerService" class="com.sample.service.CustomerServiceImpl" autowire="constructor" >
</bean>


ทดสอบการ run ก็จะได้ผลลัพธ์ตามเดิม เรียกวิธีการนี้ว่า 'Constructor Injection' โดยจะมองไปที่ไทป์และจำนวนของไทป์ที่จะส่งให้ constructor นั้นๆเป็นสำคัญ

ทดลองเปลี่ยน autowire เป็นแบบ byName และ byType แล้ว run สลับกับการ comment หรือ uncomment ในไฟล์ CustomerServiceImpl.java พลางสังเกตผลลัพธ์ ด้วยเงื่อนไขต่างๆต่อไปนี้

>> autowire="byName"

ทำงานกับชื่อของ setter method ต่างๆ ดังนั้น setCustomerRepository จึงถูกเรียกให้ทำงาน โดยจะตัด set ออกเหลือเพียง customerRepository แล้วเทียบกับแท็ก bean ในไฟล์ .xml ที่ระบุชื่อเดียวกันนี้ นั่นก็คือ

<bean name="customerRepository" class="com.sample.repository.HibernateCustomerRepositoryImpl" />

หมายความว่าอย่างไร? หมายความว่า ถ้าเราเขียน setter method ในไฟล์ CustomerServiceImpl.java เป็น setCustomerRepository1 ก็ต้องเขียน bean name ข้างต้นเป็น customerRepository1 ด้วย นี่จึงเรียกว่าการ 'Injection by Name'

*** หมายเหตุ หากว่ารันเจอ error แบบนี้หมายความว่าอย่างไร?

- แสดงว่าคลาส CustomerServiceImpl ขาด default constructor ตามที่มันบอกงั้นเหรอ? ไม่เชิงเสียทีเดียว เพราะว่าเราได้ไปสร้าง constructor ขึ้นมาเองและบังคับให้มันผ่านออบเจ็กต์ของคลาส CustomerRepository ซึ่งไม่ใช่ constructor แบบที่มันต้องการ, มันต้องการ constructor เพิ่มอีกหนึ่งตัว (หากว่าเราไม่ลบ constructor เดิมทิ้งก่อน) ที่มีหน้าตาเหมือน default constructor เป๊ะๆ ดังรูปนี้ครับ


>> autowire="byType"

ทำงานกับไทป์ของ setter method ต่างๆ ขอเพียงไทป์ตรงกัน setter method นั้นๆก็จะถูกเรียกให้ทำงานทันที

หมายความว่าอย่างไร? หมายความว่า สมมติว่าถ้ามี
setCustomerRepository1,
setCustomerRepository2,
setCustomerRepository3 และ setFoo (ชื่อไม่เกี่ยวกับชาวบ้านเลย)
ที่รับไทป์แบบเดียวกันทั้งหมด มันก็จะไล่ทำ setter ข้างต้นนี้หมดเบยไงก๊าบ นี่จึงเรียกว่าการ 'Injection by Type'

*** ตามที่ได้บอกไป ทั้ง byName และ byType จะต้องมี default constructor แบบเขียนเองด้วยเสมอ (ยกเว้นเราไม่เขียน constructor เลยสักตัว แต่ปล่อยให้จาวาสร้างให้ ก็จะมี default constructor ให้ใช้โดยอัตโนมัติ)


part นี้ไม่มีไฟล์ให้นะครับ คืนนี้ฝันดีครับ

>> กลับมาตอบข้อสงสัยของเพื่อนๆที่ว่า มันเรียงลำดับการทำงานของ setter methods อย่างไรกับ autowire="byType"
คำตอบนี้ตามภาพเลยครับ

ขอบคุณเพื่อน Phruds Kaewmuang ที่ช่วยให้คำตอบครับ :)

4 ความคิดเห็น:

  1. คือมันไล่ทำงานตาม Method ที่เราเขียนเรียงไว้ใน file จากบนลงล่างด้วยใช่ไหมคับ

    ตอบลบ
    คำตอบ
    1. สำหรับ autowire="byType" มันจะไล่ทำงานเรียงตาม method ที่เราเขียนไว้ ใช่ครับ แต่จะเรียงอย่างไรนั้นผมก็ไม่ได้สังเกต ทว่าหัวใจของการ byType ก็คือไทป์ ดังนั้นให้พึงทราบว่ามันจะทำงานครบทุก method ที่เข้าเงื่อนไขอย่างแน่นอนครับ

      ลบ
  2. ความคิดเห็นนี้ถูกผู้เขียนลบ

    ตอบลบ
  3. เหมือนจะไล่ตามชื่อครับ
    เปลี่ยนชื่อ setter ตัวสุดท้ายเป็น setBxxxxxx แล้วรัน ดูผลการเรียง
    เปลี่ยนชื่อ setter ตัวสุดท้ายเป็น setDxxxxxx แล้วรัน ดูผลการเรียง

    ตอบลบ