ลองมาดูกันว่าคนที่ไม่ชอบ XML จะสามารถเลิกใช้มันได้ไหมและเมื่อไหร่ คำตอบคือตอนนี้ SpringJavaConfig ออก M3
แล้วนั่นหมายความว่าความฝันของคนเหล่านี้ใกล้เป็นจริงมากขึ้นเรื่อยๆ แต่ก่อนอื่นลองมาดูกันก่อนว่า Java Config จะช่วย้ชีวิตเราง่ายขึ้นอย่างไรได้บ้าง
เราลองมาเขียน Bean สักสองตัวในแบบปัจจุบัน
<beans>
<bean id="orderService" class="com.acme.OrderService"/>
<constructor-arg ref="orderRepository"/>
</bean>
<bean id="orderRepository" class="com.acme.OrderRepository"/>
<constructor-arg ref="dataSource"/>
</bean>
</beans>
จาก config ไฟล์นี้เราสามารถเรียกใช้ bean ของเราได้ด้วยการเขียน code ตามนี้
ApplicationContext ctx = new ClassPathXmlApplicationContext("application-config.xml");
OrderService orderService = (OrderService) ctx.getBean("orderService");
ซึ่งในกรณีที่เราต้องการเปลี่ยนไปใช้ Java Config เราสามารถเปลี่ยนจากการอธิบาย bean ไว้ใน XML ไปเป็นการเพิ่ม Annotation ลงไปใน Java Class ของเราได้เลยดังนี้
@Configuration
public class ApplicationConfig {
public @Bean OrderService orderService() {
return new OrderService(orderRepository());
}
public @Bean OrderRepository orderRepository() {
return new OrderRepository(dataSource());
}
public @Bean DataSource dataSource() {
// instantiate and return an new DataSource …
}
}
ดังนั้นวิธีการใช้งานก็จะเปลี่ยนไปเล็กน้อย
JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ApplicationConfig.class); OrderService orderService = ctx.getBean(OrderService.class);
AnnotationApplicationContext ถูกเลิกใช้
ข้างบนคือข่าวดี และแน่นอนก็ต้องมีข่าวร้ายเพราะ AnnotationApplicationContext กำลังจะถูกเลิกใช้งานสถานะตอนนี้เป็น Deplicate และจะถูกตัดทิ้งไปเมื่อ release 1.0.0rc1ในเมื่อ
AnnotationApplicationContext ก็สามารถทำ Annotation-Driven Injection ได้อยู่แล้วคำตอบก็คือ JavaConfig ทำในสิ่งที่แตกต่างกว่า Annotaion-Driven Injection
แต่ก็จะมีคำถามจตามมาคือแล้วมันต่างกันยังไง เรามาดูเป็นข้อๆกันเลย
Type-Safe Improvements
ข้อแรกก็กรี๊ดคือไม่ต้องทำ Casting ไม่ต้อง Lookup ผ่านชื่อที่เป็น String อีกต่อไปดังนี้
JavaConfigApplicationContext context = new JavaConfigApplicationContext(AppConfig.class); OrderService orderService = context.getBean(OrderService.class);
แต่ปัญหาคือในกรณีที่มี Bean มากกว่าหนึ่งตัวใช้คลาส OrderService เหมือนกันเราจะทำยังไงมีทางออกสามทาง ไปอ่านเองก่อนนะครับ ที่นี่
สามารถ JavaConfig ใน Web-Tier ได้
เนื่องจากข้อจำกัดหลักของ Spring คือมันจะโหลด Application Context ผ่าน XML เท่านั้นดังนั้ในกรณีที่เราต้องเอาไปใช้ใน Web เราต้องบอก web.xml ว่า เห้ยไม่มีแล้วเค้าให้โหลดแบบใหม่นะ
โดยเราสามารถทำได้ดังนี้
<web-app>
<!– Configure ContextLoaderListener to use JavaConfigWebApplicationContext
instead of the default XmlWebApplicationContext –>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.config.java.context.JavaConfigWebApplicationContext</param-value>
</context-param>
<!– Configuration locations must consist of one or more comma- or space-delimited
fully-qualified @Configuration classes –>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>example.RootApplicationConfig</param-value>
</context-param>
<!– Bootstrap the root application context as usual using ContextLoaderListener –>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!– Declare a Spring MVC DispatcherServlet as usual –>
<servlet>
<servlet-name>dispatcher-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!– Configure DispatcherServlet to use JavaConfigWebApplicationContext
instead of the default XmlWebApplicationContext –>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.config.java.context.JavaConfigWebApplicationContext</param-value>
</init-param>
<!– Again, config locations must consist of one or more comma- or space-delimited
and fully-qualified @Configuration classes –>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>example.web.WebBeansConfig</param-value>
</init-param>
</servlet>
</web-app>
ข้อต่อไปเพิ่มเติมความสามารถเรื่อง @import เข้าไปอีก
อันนี้ต้องดูตัวอย่าง
@Configuration
public class FooConfig {
public @Bean Foo foo() { … }
public @Bean Bar bar() { … }
}
@Import(FooConfig.class)
@Configuration
public class ApplicationConfig {
public @Bean ServiceA serviceA() { … }
}
JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ApplicationConfig.class);
// foo, bar, and serviceA beans will all be available
ctx.getBean(ServiceA.class); // works
ctx.getBean(Foo.class); // works too
External Resource
แต่ก่อนเราต้องใช้ properties ไฟล์ในการเก็บค่าคงที่ที่ใช้งานนอกระบบต่างๆเช่น username, password, url ในการติดต่อกับ Database แต่สำหรับ JavaConfig เราสามารถปวดกระโหลกมาขึ้นด้วยการยัดมันลงไปในคลาสได้เลยดังนี้
@Configuration
@ResourceBundles("classpath:/com/acme/datasource")
public abstract class ApplicationConfig {
public @Bean OrderService orderService() {
return new OrderServiceImpl(orderRepository());
}
public @Bean OrderRepository orderRepository() {
return new JdbcOrderRepository(dataSource());
}
public @Bean DataSource dataSource() {
return new DriverManagerDataSource(url(), username(), password());
}
abstract @ExternalValue("datasource.url") String url();
abstract @ExternalValue("datasource.username") String username();
abstract @ExternalValue("datasource.password") String password();
}
เป็นไงครับปวดหัวกว่าเดิมหรือดีกว่าเดิม ==’
