จริงมีเนื้อหาสำหรับ Spring MVC มาแล้วสามตอนด้วยกันคือ SpringMVC ภาค 1, SpringMVC ภาค 2 และ SpringMVC ภาค3 หรือสามารถเข้าไปหาหาจาก Tag SpringMVC ได้ครับ
แต่วันนี้มาดูของใหม่ใน Spring MVC 3 กันหน่อยว่าสวยงามกว่าเดิมขนาดไหนก่อนอื่นให้ไป Check Out โค้ดตัวอย่าง mvc-basic และให้เพิ่ม jetty plugin เข้าไปเพื่อความสะดวกในการทดสอบเพราะไม่อยาก build เป็น war
เมื่อเรียบร้อยแล้วเรามาดูกันทีละไฟล์ไปว่ามีอะไรน่าสนใจบ้างไฟล์แรกที่เราจะดูคือ web.xml ส่วนแรกคือ UrlRewriteFilter โอวจริงมันเป็นของที่เพิ่มเข้ามาเพื่อทำให้ URL สวยงามตามมาตรฐาน REST (ตัวโปรเจคอยู่ที่ urlrewritefilter ได้รับอิทธิพลจาก mod_rewrite)
<!-- Enables clean URLs with JSP views e.g. /welcome instead of /app/welcome -->
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
ส่วนต่อมาคือ app-config.xml ส่วนนี้ไม่มีอะไรน่าตื่นเต้นสิ่งที่เราใส่ไว้คือการเปิดใช้ Component Scanning ที่จะช่วยเราหา bean แบบอัตโนมัติและพ่วงท้ายด้วยการ import ตัว spring mvc config เข้ามาด้วย
<!-- Configures Spring MVC -->
<import resource="mvc-config.xml" />
และสำหรับ mvc-config.xml เองมีเรื่องน่าสนใจคือเราจะใช้สิ่งที่เรียกว่า annotation-driven tag
<!-- Configures the @Controller programming model -->
<mvc:annotation-driven />
หน้าที่ของ tag จะจัดการใส่ HandlerMapping และ HandlerAdapter เข้าไปใน @Controllers นอกจากนี้มันยังทำการกำหนดค่า default ต่างๆที่เรามีไลบารี่อยู่ใน Class Path ของเราเช่น
รองรับการทำ formatting Number fields ด้วย @NumberFormat
รองรับการทำ formatting Date, Calendar, และ Joda Time fields ด้วย @DateTimeFormat, ถ้า Joda Time อยู่ใน classpath
รองรับการทำ validating @Controller ด้วย @Valid,ถ้า JSR-303 Provider อยู่ใน classpath
รองรับกาอ่านและเขียน XML, ถ้า JAXB อยู่ใน classpath
รองรับการอ่านและเขียน JSON,ถ้า Jackson อยู่ใน classpath
ส่วนต่องมาที่น่าสนใจคือ
<!-- Forwards requests to the "/" resource to the "welcome" view -->
<mvc:view-controller path="/" view-name="welcome"/>
เบื้องหลังการทำงานเราจะเห็นว่า mvc:view-controller ทำการใส่ ParameterizableViewController ที่จะทำหน้าที่เลือก view สำหรับการ render ยกตัวอย่างเช่นเมื่อมีการเรียก “/” จะมีการเปลี่ยนค่าเป็น welcome.jsp ที่อยู่ใน /WEB-INF/views แทน
ส่วนต่อไปใน mvc-config จะแสดง
<!-- Configures Handler Interceptors -->
<mvc:interceptors>
<!-- Changes the locale when a 'locale' request parameter is sent; e.g. /?locale=de -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>
mvc:interceptors ช่วยให้เราใส่ HandlerInterceptors เข้าไปใน Controller ได้ซึ่งมันจะช่วยอำนวยความสะดวกให้เราได้มากขึ้นเพราะก่อนหน้านี้เราต้อง register interceptor ลงไปใน HandlerMapping เป็นตัวๆไปแต่ปัจจุบันเราสามารถระบุผ่าน URL ได้เลย
Data Binding Simplification
ส่วนที่น่าสนใจที่สุดสำหรับผมคือเรื่องการ Binding เราสามารถทำ Binding ได้ง่ายเหมือนเดิมรวมถึงสามารถใช้ Validation Annotation ได้ด้วย
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);
...
}
ในส่วนของ Controller เองก็สามารถใช้ Annotation ได้มากขึ้นและไม่ต้องมีกระบวนการภายใน Controller เองเยอะเหมือนสมัยก่อน ยากตัวอย่างเช่น Validation ที่ Model Level สามาถถูกเรียกใช้ด้วยการใช้ @Validate ตัวอย่างด้านล่างเป็นการรับ POST request ที่ Spring จะทำการ Bind ตัว Request กลับมาเป็น Account account ให้เราเองเลย
@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();
}
ส่วนการรับ GET request ก็ทำได้หลายแบบเช่นจังหวะการ initiate Form กับ Object ที่จะใช้รับค่าก็ทำได้ดังนี้
@RequestMapping(method = RequestMethod.GET)
public String getCreateForm(Model model) {
model.addAttribute(new Account());
return "account/createForm";
}
และถ้าเราต้องการดูรายละเอียดของ Account ที่เราต้องการด้วยการรับ accountId เข้ามาก็สามารถทำได้
@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";
}
ตัวหย่างนี้เป็นการใช้ springMVC3 แบบบ้านที่สุดตอนต่อไปคือเราจะมาลองดูกันว่าถ้าเราจะทำ RESTful WS ด้วย SpringMVC 3 เราจะสามารถทำได้ไหม