Posts Tagged ‘Spring Java Config’

มาดูการสร้าง Bean ของ Spring JavaConfig กัน

July 28th, 2009

เริ่มแรกดูการคอนฟิก Spring Context กันก่อน จากเดิมใช้ XML เปลี่ยนเป็น Java + Annotation

@Configuration
class MyConfig {
 public @Bean MyBean myBean() {
  new MyBean();
 }
}

เพื่อให้ง่ายต่อความเข้าใจจะใช้ XML ร่วมกับ ConfigurationPostProcessor เพื่อทำ Bootstrapping JavaConfig

<beans>
    <!-- first, define your individual @Configuration classes as beans -->
    <bean class="myapp.MyConfig"/>

    <!-- be sure to include the JavaConfig bean post-processor -->
    <bean class="org.springframework.config.java.process.ConfigurationPostProcessor"/>
</beans>

จะเห็นได้ว่ามีการกำหนด MyConfig เป็น Spring Bean ร่วมกับใช้ ConfigurationPostProcessor ซึ่งเป็น BeanFactoryPostProcessor ชนิดหนึ่ง

การสร้าง Spring Bean แบบปกติตัวที่ทำหน้าที่สร้าง Instance Bean คือ InstantiationStrategy

Spring JavaConfig ก็ใช้หลักการเดียวกัน แต่จะใช้การสร้าง bean ผ่าน Factory Method นั่นคือ MyConfig.myBean() จะทำหน้าที่เป็น Factory Method นั่นเอง หากแปลงเป็น XML จะได้ประมาณนี้

 <bean id="myconfig" class="myapp.MyConfig" />
 <bean id="myBean" factory-bean="myconfig" factory-method="myBean" />

spring java config M3

January 9th, 2009

ลองมาดูกันว่าคนที่ไม่ชอบ 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();
}

เป็นไงครับปวดหัวกว่าเดิมหรือดีกว่าเดิม ==’