Spring MVC ใน Spring 3.0 ง่ายจริงหรือ ??
คำถามนี้ ผมไม่สามารถตอบแทนใครได้ จนกว่าคุณจะได้ลองใช้เองเท่านั้น
หลังจากที่ Spring Framework 3.0 GA ปล่อยออกมาแล้ว ก็มีบทความต่างๆ ออกมา เช่น
- Spring Framework 3.0 goes GA
- Infoq :: What’s New in Spring 3.0
- TSS :: SpringSource takes Spring 3.0 to GA
และบทความ
MVC Simplifications in Spring 3.0 โดยผมอ้างอิงมาจากบทความนี้นะครับ มาดูกันว่า SpringMVC ใน Spring 3 มันง่ายจริงไหม ???
Let’s Go !!!!!
1. ว่าด้วยเรื่องการ configuration มีอะไรที่เปลี่ยนแปลงบ้าง
1.1 mvc:annotation-driven
...
<mvc:annotation-driven />
...
เป็น tag สำหรับทำการ register HandlerMapping และ HandlerAdapter ลงใน request ที่จะส่งไปยัง @Controller และเพิ่มความสามารถอื่นๆ ไปด้วย ถ้ามี libraries ใน classpath เช่น
- สามารถใช้งาน Type ConversionService ในการแปลงชนิดของข้อมูล
- สนับสนุนการใช้งาน @NumberFormat สำหรับการจัดรูปแบบของข้อมูลชนิด Number
- สนับสนุนการใช้งาน @DateTimeFormat สำหรับการจัดรูปแบบของข้อมูลชนิด Date, Calendar และ Joda Time
- สนับสนุนการใช้งาน @Valid สำหรับ validate input ที่เข้ามายัง @Controller ( JSR 303 )
- สนับสนุนการอ่านและเขียน XML ด้วย JAXB
- สนับสนุนการอ่านและเขียน JSON ด้วย Jackson
1.2 mvc:view-controller
...
<mvc:view-controller path="/" view-name="welcome" />
...
กลไกภายในของ SpringMVC นั้นจะทำการ register [ "/" คู่กับ viewname ชื่อว่า welcome ] โดยที่ ParameterizableViewController จะทำการเลือก view มา render ต่อไป โดยจากตัวอย่างนั้น เมื่อมี request “/” เข้ามาจะทำการเลือก view ชื่อว่า welcome มา render กลับไป ซึ่ง view นั้นจะมีนามสกุลเป็น jsp เก็บไว้ใน folder ที่ config ไว้ในส่วน InternalResourceViewResolver [ ซับซ้อนดีจริงๆ ]
1.3 mvc:interceptors
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>
ทำการ register HandlerInterceptors ลงใน Controller ทุกๆ ตัว
จากตัวอย่างเป็นการเปลี่ยน Locale ของระบบ โดยส่ง parameter ชื่อว่า locale เข้ามายัง Controller
2. ว่าด้วยเรื่อง Data Binding และ Validation
มีบทความอธิบายแบบละเอียดที่นี่ Spring 3 Type Conversion and Validation
เป็นส่วนของการ binding และ validation ข้อมูลต่างๆ ที่ส่งเข้ามายัง Controller ซึ่งจะทำงานก่อนที่จะเข้าทำงานใน method ต่างๆ ของ Controller โดยสิ่งที่ทำประกอบไปด้วย
- ฺBinding ของมูลลงใน Model
- Conversion ข้อมูลก่อน binding ลงใน Model
- Validate ข้อมูลก่อน binding ลงใน Model
[ ดูๆ ไปเหมือน Conversion และ Validation phrase ใน JSF เลยนะครับ ]
ตัวอย่างของ method create ใน Controller
@RequestMapping(method=RequestMethod.POST)
public String create(@Valid Account account, BindingResult result) {
if (result.hasErrors()) {
return "account/createForm";
}
this.accounts.put(account.assignId(), account);
return "redirect:/account/" + account.getId();
}
เมื่อมี request เข้ามายัง method create จะทำการ validate ข้อมูลที่ส่งเข้ามา กับ class Account [ จากการกำหนด @Valid ไว้ ] ถ้าเกิดข้อผิดพลาดก็จะส่งออกมายัง method นั้นๆ เลย
มาดูหน้าตาของ class Account ซึ่งเป็น model สำหรับเก็บข้อมูลว่าเป็นอย่างไร
public class Account {
private Long id;
@NotNull
@Size(min=1, max=25)
private String name;
@NotNull
@NumberFormat(style=Style.CURRENCY)
private BigDecimal balance = new BigDecimal("1000");
@NotNull
@NumberFormat(style=Style.PERCENT)
private BigDecimal equityAllocation = new BigDecimal(".60");
@DateTimeFormat(style="S-")
@Future
private Date renewalDate = new Date(new Date().getTime() + 31536000000L);
...
...
...
จะเห็นได้ว่ามีการใช้งาน Annotation มากมาย เพื่อใช้สำหรับการ validate ข้อมุลในแต่ละ fields โดย Annotation ต่างๆ เช่น @NumberFormat, @DateTimeFormat เป็นต้น ซึ่งถูก register มาตั้งแต่ข้อ 1.1 แล้ว
ต่อมา
- ถ้าในขั้นตอนการ validate เกิดข้อผิดพลาดขึ้นมา จะทำการ render หน้า createForm ขึ้นมา
- ถ้าไม่มีข้อผิดพลาด จะทำการบันทึกข้อมูล และ redirect ไปยัง url /account/ ซึ่งจะเข้าทำงานใน method getView ใน Controller ดังนี้
@RequestMapping(value="{id}", method=RequestMethod.GET)
public String getView(@PathVariable Long id, Model model) {
Account account = this.accounts.get(id);
if (account == null) {
throw new ResourceNotFoundException(id);
}
model.addAttribute(account);
return "account/view";
}
- ใน method getView นี้จะรับจาก HTTP GET และมี parameter 1 ตัวชื่อว่า id ชนิดตัวเลข
- ใน method นี้มี feature หนึ่งที่น่าสนใจคือ ถ้าไม่เจอข้อมูลตาม id ที่ส่งเข้ามาแล้วจะโยน exception ออกมา
จากตัวอย่างคือ ResourceNotFoundException มีหน้าตาดังนี้
@ResponseStatus(value=HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
private Long resourceId;
public ResourceNotFoundException(Long resourceId) {
this.resourceId = resourceId;
}
public Long getResourceId() {
return resourceId;
}
}
ถ้าเกิด ResourceNotFoundException ขึ้นมาแล้วจะส่ง HTTP Status= 404 [ Page not found ] ออกไป โดยเราสามารถกำหนด error page ตาม HTTP Status ได้อีกด้วย ซึ่งเป็นความสามารถที่น่าสนใจไม่น้อยทีเดียวครับ
เท่าที่ทดสอบใช้งานมานั้น พบว่า SpringMVC ใน Spring 3.0 นั้นพยายามที่จะลดจำนวนการของ code รวมไปถึงการ configuration ลงไป แต่ก็ได้เพิ่ม Annotation เข้ามาอย่างมากกมายเช่นกัน ซึ่งมีทั้งข้อดีและข้อเสีย ตามแต่ละคนจะมองกันไปครับ แต่ถ้ามองว่ามันช่วยให้ง่ายขึ้น ก็ถือว่ามันมีประโยชน์ แต่ถ้ามองว่ามันยุ่งยากก็กลับไปใช้แบบเดิมก็ไม่แปลกครับ …
ส่วนผมจะลองใช้งานมันต่อไปครับ แล้วจะนำมาอธิบายในบทความต่อๆ ไปครับ ….
ส่วนตัวอย่างนั้น สามารถ checkout ออกมาจาก svn repositories ของ SpringSource ได้จากคำสั่ง
svn co https://src.springframework.org/svn/spring-samples/mvc-basic/trunk/
ปล. ในตัวอย่างนี้มีการใช้งาน UrlRewrite ด้วยนะครับ จะทำให้ url ที่ได้สวยงามยิ่งขึ้นครับ และนำมาอธิบายการใช้งานต่อไปครับ