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

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

: XML กับ Default Constructor และ Constructor

กลับมาทบกวน applicationContext.xml กันอีกครั้งก่อนจะเพิ่ม config ให้กับ constructor ของคลาส CustomerServiceImpl

คลาส CustomerServiceImpl เราได้เขียน setCustomerRepository เป็นเหตุให้ต้องเขียน setter config ที่ไฟล์ .xml เป็น
property name="customerRepository" จริงไหมล่ะ

ดูด้านล่างนี้ประกอบ (ตัดเอาบางส่วนมา และก็อย่าไปงงกับ property ref ล่ะ)

<bean name="customerService" class="com.sample.service.CustomerServiceImpl">
     <property name="customerRepository" ref="customerRepository" ></property>
</bean>

จากนั้นลองกลับไปดูโค้ดจาวาที่ไฟล์ Application.java เจาะจงท่อนที่เขียนว่า

CustomerService service = applicationContext.getBean("customerService", CustomerService.class);

โค้ดข้างบนนี้ทำอะไร? มันสั่งให้ไฟล์ .xml สร้างออบเจ็กต์คลาส CustomerServiceImpl ผ่าน default constructor ครับ (default constructor หน้าตาเป็นไงคงรู้กันนะ ซึ่งเราไม่จำเป็นต้องเขียนเอง)

คำถามคือ default constructor ไม่มีพารามิเตอร์ส่งค่าให้ออบเจ็กต์ แล้วอีกท่อนที่เขียนว่า service.findAll().get(0).getFirstName() ทำงานได้อย่างไรครับ?

ใครตาไวก็จะตอบได้เลยว่ามาจาก setter config ที่เขียนบอกไว้ในไฟล์ .xml นั่นไง ถูกต้องนะครับ! หากไม่เชื่อลองลบท่อนด้านล่างนี้ออกไปสิ

<property name="customerRepository" ref="customerRepository" ></property>

ผลคือเมื่อมันพยายามเรียก findAll() ค่าที่ได้ก็จะเป็น null ทันที

สรุปได้ว่าเมื่อเราสั่งให้ไฟล์ .xml สร้างออบเจ็กต์คลาส CustomerServiceImpl ผ่าน default constructor หลังจากนั้นมันก็จะไปเรียก setter ของคลาส CustomerServiceImpl ให้ทำงานด้วย เหตุนี้มันจึงสร้างออบเจ็กต์คลาส HibernateCustomerRepositoryImpl ให้เลยโดยอัตโนมัติครับ (แท้จริงก็ตาม config ที่เขียนไว้นั่นแหละ)

ทีนี้มีบางคนเกิดอยากรู้ว่า ถ้าไม่ต้องการให้ setter ทำงานล่ะ จะมีวิธีอื่นอีกไหมในการเขียน config ในไฟล์ .xml ?

คำตอบคือ มีครับ หนึ่งในนั้นคือการเขียน constructor config ที่รับค่าออบเจ็กต์ของคลาส HibernateCustomerRepositoryImpl เข้าไปแทน ดังนี้ (ลบ setter config ออกไปได้เลย หรือจะใส่ comment ไว้ก็ได้ ในที่นี้ผมลบไปเลย)

<bean name="customerService" class="com.sample.service.CustomerServiceImpl">
     <constructor-arg index="0" ref="customerRepository"></constructor-arg>
</bean>

เอาล่ะ constructor-arg index="0" คือตำแหน่งแรกของ args ตัวที่หนึ่ง
และ ref="customerRepository" ก็มีไว้อ้างไปยังคลาส HibernateCustomerRepositoryImpl ที่เขียน config ไว้ไง

เสร็จแล้ว save จะพบกับ error ทันตา นั่นเพราะคลาส CustomerServiceImpl ไม่มี constructor ดังที่เขียนไว้ใน constructor config (ส่วน default constructor ตายไปแล้วเรียบร้อย เพราะเราพยายามเขียน constructor ขึ้นมาเอง)

เราก็ต้องไปเพิ่ม constructor ลักษณะดังกล่าวให้กับคลาส CustomerServiceImpl ครับ

public class CustomerServiceImpl implements CustomerService {
...

     public CustomerServiceImpl(CustomerRepository customerRepository) {
          this.customerRepository = customerRepository;
     }

...
}

ได้อย่างนี้แล้ว setter ในคลาสนี้ก็จะไร้ความหมายไปเลยนะ ใครอยากลองก็ comment มันออกไป แล้ว run ดูผลลัพธ์ ก็จะได้ตามเดิมครับ

ข้อสังเกตที่ผมได้รับคือ การสร้างออบเจ็กต์ด้วย xml ในกรณีตัวอย่างนี้ constructor ที่ผ่านค่าได้โดยตรงสำคัญกว่า default constructor

part ต่อไปเราจะเริ่ม injection เหล่าคลาสโดยการใช้ autowire กันนะครับ
และนี้คือโค้ดทั้งหมดของ part นี้ (ง่วงนอนแล้ว ฝันดีเพื่อนๆ)
http://www.mediafire.com/download/e75264xnp9kjbsc/spring_sample_xml_2.zip

ไม่มีความคิดเห็น:

แสดงความคิดเห็น