Posts Tagged ‘spring maven’

Introducing Spring Batch แบบ ผิวๆ บางๆ (ภาคต่อ)

February 4th, 2009

ความเดิมตอนที่แล้ว :
กล่าวถึงการ cofig เบื้องต้นของ spring-batch แล้วก็ xml ตัวพ่ออย่าง pom มาตอนนี้ ไม่พูดพร่ำทพเพลงครับ เด่วลืม เขียนต่อกันไปเลยดีกว่า

เราก็มาดูกันต่อเลย ก็คือ config ตัวพระเอกของเราคือตัว job กันหล่ะครับ โดยเราก็ ไป regis bean

<bean id="tm" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>

<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
        <property name="transactionManager" ref="tm"/>
</bean>

<bean id="usersLoadStep" class="org.springframework.batch.core.step.item.SimpleStepFactoryBean">
        <property name="transactionManager" ref="tm"/>
        <property name="jobRepository"      ref="jobRepository"/>
        <property name="itemReader"         ref="reader"/>
        <property name="itemWriter"         ref="writer"/>
</bean>

<bean id="usersLoadJob" class="org.springframework.batch.core.job.SimpleJob">
        <property name="jobRepository" ref="jobRepository"/>
        <property name="steps">
            <list>
                <ref bean="usersLoadStep"/>
            </list>
        </property>
</bean>

<bean id="writer" class="com.spring66.tutorial.batch.DatabaseWriter"/>

ว่ากันที่ตัวแรก tm ว้าววว ไม่น่าแปลกใจ เพราะ job ก็คือ งานงานนึงที่สำคัญ จึงมี TransactionManager มาครอบไม่น่าสงสัย
ตัวที่สอง ก็ jobRepository ตัวนี้เล่าแล้วยาวมาก ขอให้ตามไปอ่านเอานะครับ จริงๆ แล้วก็ ผมเองก็ไม่ได้เข้าใจแบบสามารถอธิบายได้ดีนัก
เหมือน design pattern ที่นิยมชมชอบ FactoryBean หรือ step ที่เราพูดถึงตอนแรก usersLoadJob นี่หล่ะพระเอกของเรา
โดยจุดที่น่าสนใจคือ list parameter

 <property name="steps">
            <list>
                <ref bean="usersLoadStep"/>
            </list>
 </property>

นั่นเอง นั่นคือจุดที่บอกว่าหนึ่งงานอาจจะมีหลาย step ก็ได้ นั่นเอง ตรงนี้หล่ะจร้า
ส่วนตัวสุดท้าย

เอ้อันนี้ผมพูดไปหรือยังน้อ เอาเป็นว่ามันคือคลาสที่เราสร้างมา นั่นเองหรือ process ที่เราจะสร้างและให้ทำงานตามที่ใจเราปราถนา นั่นเอง
โดยหน้าตาผมก็เอาแบบง่ายๆไม่มีเทพแถมด้วยถึงแบบนี้ครับ

package com.spring66.tutorial.batch;

import org.springframework.batch.item.ClearFailedException;
import org.springframework.batch.item.FlushFailedException;
import org.springframework.batch.item.ItemWriter;

/**
 *
 * @author Phongsak
 */
public class DatabaseWriter implements ItemWriter {

    public void write(Object item) throws Exception {
        System.out.println(" "+ item.getClass().toString());
        System.out.println("insert to database...");
    }

    public void flush() throws FlushFailedException {
    }

    public void clear() throws ClearFailedException {
    }

}

อ่ะ ใช้ sout debug แบบเมพๆ เนี่ยหล่ะครับ ดูแล้วก็ น่าสนใจถ้า เราทำการ อะแด๊พ แอ๊พพาย หน่อยก็เอาไปต่อกับ dao ของคุณ roofimon ได้เลย โอ้วช่างสมใจ
อ่ะเท่านี้หล่ะทีนี้ก็หาทางทำให้มันทำงานกันดีกว่า คำเท่ๆ พูดให้ชินทำให้ เคยมือ ก็คือ เขียน test ครับ

package com.spring66.tutorial;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.job.SimpleJob;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;

public class BaseSpringTestCase extends AbstractDependencyInjectionSpringContextTests {

    protected final Log log = LogFactory.getLog(getClass());

    @Override
    protected String[] getConfigLocations() {
        setAutowireMode(AUTOWIRE_BY_NAME);
        return new String[]{
                    //"classpath:/applicationContext.xml",
                    "classpath*:/applicationContext.xml" // for modular projects
                //"classpath:**/applicationContext*.xml" // for web projects
                };
    }

    public void testLoadUsers() {

        Job job = (SimpleJob) super.applicationContext.getBean("usersLoadJob");
        JobParameters jobParameters = new JobParameters();
        JobRepository jobRepository = (JobRepository) super.applicationContext.getBean("jobRepository");

        try {
            JobExecution je = jobRepository.createJobExecution(job, jobParameters);
            job.execute(je);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}

ถึงตรงนี้ผมคงไม่ขอขยายความนะครับ ลองแก้ ลองเขียนกันไปเลยดีกว่า แล้วแบ่งปันประสบการกันบ้างนะครับ มันยังมีอีกหลายวิธีวันหลัง จะมาต่อนะครับ
เพราะอีกเดี๋ยวผมคงต้องเอาไป implements ในงานเหมือนกัน ตอนนี้แค่เอามาอ่านเฉยๆ เลยออกมาไม่ค่อย เคลียเท่าไหร่

Introducing Spring Batch แบบ ผิวๆ บางๆ

January 30th, 2009

เรื่องของเรื่อง ก็มาจาก ที่ว่า คุณ roofimon อยากได้ โจทย์มาทำ dojo ครับเลย อยากได้ sample data ใน database ซัก แสน record เลยใช้อำนาจมืด(เอ้ย!)ไหว้วานให้ผมทำ
ก็เลย มานั่งนึกว่าเอ้อ จะเขียนแบบธรรมดาๆ ก็ ดูว่า จะเสียชื่ออาจารย์อย่างคุณ roofimon ก็เลย คิดแบบเมพๆ หน่อยเขียน spring batch ดีกว่า ก็เลย เริ่มกันไปเลย เริ่มกันที่ intro กันก่อนแล้วกัน

ตาม entry เรื่อง Spring Product ของคุณ roofimon เรื่อง spring และลูกๆ spring batch ก็คือหนึ่งในลูกๆ ของ spring ตัวแม่ ในความเห็นผมเอง มองว่า ประโยชน์ของการใช้งาน
spring batch นั่นมีส่วนที่คล้ายกับ spring aop ตัวเก่ง คือ มุ่งหมายจะทำใำห้ระบบทำ logic อันใดอันหนึ่ง โดย design แยกออกมาจาก main business logic โดย spring aop
นั้นมุ่งหมายให้ทำ โดยสามารถกำหนด ว่าจะให้ทำ หลัง, ก่อน หรือ ทั้งก่อนหลัง การกระทำสิ่งใดอีกสิ่งหนึ่ง(main business logic) ทุกครั้งเสมอเฮ้อ เขียนแล้วงง เอง แต่ spring batch ก็ คือ สิ่งที่อยากให้ทำ ในเวลาหรือว่าจังหว่ะที่กำหนด
ที่เจอกันบ่อย ๆ เช่น ทุกวันศุกย์เย็น ต้อง import data from xml อะไรเทือกนั้นเป็นต้น
เกือบจะหมดแรงข้าวต้มละ ยังไมไ่ด้ขึ้นเลย อ่ะจะเข้าละ เด่วจะยืดเยื้อไปกันใหญ่ ผมคิดว่าการที่เราจะเรียนรู้และเข้าใจสิ่งใหม่ที่เราไม่เคยใช้ ง่ายที่สุดก็คือการทำตามตัวอย่าง งั้นก็อย่างที่บอก เรามาลองทำอะไรเล่นๆ ตามธรรมเนียมกัน ไหนๆก็ไหนๆ ก็เลย เขียนต่อ จาก spring66-app แล้วกันเพราะคาดว่าหลายๆ ท่านก็คงทำตามมาตลอด จัดไปครับ แบบผิวๆ บางๆ

ตัวละครของ Spring Batch
เรามาเริ่มกันที่ การแนะนำตัวละครหลักเลยดีกว่ครับ ซึ่งก็คือ ผู้มีบทบาทในการ เริ่มต้นใช้งานมันนั่นเอง มีดังนี้นะครับ ขอแนะนำแบบ ผิวๆ บางๆ นะครับ ขอคนช่วยเสริมเติมแต่งส่วนที่ผมเข้าใจผิดนะครับ
Job ก็คืองานหรือว่า batch นึงที่เราจะสร้างขึ้นครับ โดยในแต่ละงานอาจจะ ประกอบไปด้วยหนึ่งหรือหลายขั้นตอน(Step)ครับ Step ก็ คือขั้นตอนที่อยู่ใน Job นั่นหล่ะครับโดย Job ก็จะมี Instance คือ JobInstance ที่สามารถรับพาามิเตอร์ได้คือ JobParameter โดยในแต่ละการแสดงบทบาทของ JobInstance จะ ถูกเรียกว่า JobExecution เรา ลองนึกภาพเล่นๆ ว่า สมมุติเราจะทำการ นำเข้าข้อมูล users จำนวน นึงแล้วต้องการ ที่จะทำการ truncate ข้อมูลเก่าทิ้งทั้งหมด (อย่าถามนะครับว่าทำ ทำไม บอกแล้วว่าสมมุติ) งานที่เราจะทำก็จะมี สองขั้นตอนคือ หนึ่งทำการ truncate ก่อนแล้วแค่ทำการ insert ข้อมูลใหม่ลงไป พอเห็นภาพมั้ยครับ แต่ spring66 เราจะทำ toturial ทั้งทีใหญ่มหาศาลครับ จะมีงานเดียวได้อย่างไร เราจึงลืมไปไมไ่ด้เลยที่จะแนะนำ สิ่งที่ พอ spring batch เค้าต้องการอย่างแรงคือ JobRepository เป็นตัวเก็บ งานที่เราจะทำั้งหมดไว้ นั่นเองตรงนี้ไม่ค่อยเคลีย ในการอธิบายครับต้องรบกวนท่านอื่นเสริม แต่อ่ะ พอๆ เด่วจะตัวละครเยอะไปกันใหญ่ เอาหล่ะทีนี้ จากกระทู้ก่อนๆ เราก็มีการจัดเตรียมอุปกรณ์การแสดงเพียบ เราจึงไม่ต้องไปทำใหม่ทั้งหมด ขอไปเอาของเค้ามาใช้หน่อยแล้วกันนะครับ

เริ่มกันที่ xml ตัว พ่อครับ pom.xml นั่นเอง ถ้าผ่านมาทั้งหมดก็ น่าจะประมาณแบบนี้เนอะ


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.spring66.tutorial</groupId>
	<artifactId>spring66-app</artifactId>
	<packaging>jar</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>spring66-app</name>
	<url>http://maven.apache.org</url>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-resources-plugin</artifactId>
				<version>2.2</version>
				<configuration>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring</artifactId>
			<version>2.5.6</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>2.5.6</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.6</version>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>
</project>

เราก็ทำการเพิ่มสิ่งที่เราจะใช้เข้าไปโลดครับ


<dependency>

	<groupId>org.springframework.batch</groupId>

	<artifactId>spring-batch-core</artifactId>

	<version>1.1.3.RELEASE-A</version>

</dependency>

<dependency>

	<groupId>org.springframework.batch</groupId>

	<artifactId>spring-batch-infrastructure
	</artifactId>

	<version>1.1.3.RELEASE-A</version>

</dependency>

เรื่องของ dependency เนี่ย รบกวนต้องตามอ่านกันเอานะครับ เยอะเหลือหลายครับ blog แรกผมครับพลังน้อย

เหมือน อย่างเคยๆ เจ้าพ่อสปริงเค้าก็เขียนอะไรๆ มาให้ใช้เยอะแยะไปหมด เริ่มกันที่ เราจะทำการสร้าง csv file เก็บ รายชื่อของ usrId,usrName,usrPassword นะครับโดยใช้  , เป็นตัวขั้นหน้าตา ก็ประมาณนี้ users.csv

userId,userName,userPassword
id1,Poorprogrammer001,password01
id2,Poorprogrammer002,password02
id3,Poorprogrammer003,password03
id4,Poorprogrammer004,password04
id5,Poorprogrammer005,password05
id6,Poorprogrammer006,password06

ด้วย บุญเก่าทำให้เรามี applicationContext.xml แล้ว ลืมบอกไปว่า ในแต่ละ งานก็จะมีการ cofig ทั้งหมด ใน xml นะครับ อย่างที่ เจ้าพ่อ xml config อย่างสปริงเค้าถนัด แต่เรื่องของการ เขียนเป็น annotation ผมไม่แน่ใจนะครับว่าได้หรือยัง เราก็ เลยขอ config ไปที่เราไปสร้าง bean ที่จะทำการ อ่าน และคัดแยก ตัว file ของเรากัเถอะครับ จะว่าไป มานั่งเขียน คลาส เพื่ออ่าน io file ก็ดูจะถึกไปหน่อย เค้ามีของดีมาให้ใช้ครับ คือ org.springframework.batch.item.file.FlatFileItemReader เอ้อ ฟังชื่อคลาสแล้วดูดีไปดูกันเลย เราก็ไป regis มันลง applicationContext


<bean id="inputFile" class="org.springframework.core.io.ClassPathResource">
	<constructor-arg value="/users.csv" />
</bean>
<bean id="mapper"
	class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
	<property name="targetType" value="com.spring66.tutorial.model.Users" />
</bean>

<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
	<property name="resource" ref="inputFile" />
	<property name="firstLineIsHeader" value="true" />
	<property name="lineTokenizer">
		<bean
			class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
			<property name="delimiter" value="," />
		</bean>
	</property>
	<property name="fieldSetMapper">
		<bean class="com.spring66.tutorial.batch.UsersMapper" />
	</property>
</bean>

เรา ก็ สร้าง reader ขึ้นมา ครับโดยมาดูกันซิว่าเรา ขาดอะไรอย่าเพิง test นะครับเด่วจะแดงแล้วก็โดนด่าเหมือนที่พี่ roofimon เค้าโดนประจำแต่ก็ TDD อ่ะนะ เอาเป็นว่าเรารู็้ทัน

<property name=”resource” ref=”inputFile”/>

ref ไป inputfie เราก็ไปขอความช่วยเหลือจาก org.springframework.core.io.ClassPathResource

<property name=”firstLineIsHeader” value=”true”/>

อันนี้หล่ะผมชอบใจ๊ชอบใจ ถึงมันจะดูเฉพาะงานไปหน่อยแต่ว่า เราก็เจอมันบ่อยเสียจริงๆ

lineTokenizer อันนี้ก็เท่ หล่อมาเลย บอกได้เลยว่าใช้ DelimitedLineTokenizer เป็นอะไรตัวนี้ น่าจะมาจาก String Tokenizer เอง

<property name=”fieldSetMapper”>
<bean class=”com.spring66.tutorial.batch.UsersMapper”/>
</property>

ตัว สุดท้ายคือตัว mapping class คุ้นๆ มั้ยครับคลาสที่ลงท้ายด้วยพวก mapper ทั้งหลาย ก็คือตัวที่จะ map model เ้ข้ากับ flele ที่อ่านมา นั้นหล่ะครับ แต่ด้วยความ ขี้ค้ราน ขั้นมหาศาลของผม เลยขอไปเอา Users model มาแก้นะครับ โดยลบ property อื่นๆ ออกให้เหลือแค่สามตัว เพื่อ map เข้ากับ csv ผม แห่ๆ

โดยเราก็ไปสร้าง คลาส Mapper ขึ้นมาหน้าตา ไม่ค่อยหล่อแบบนี้ครับ

package com.spring66.tutorial.batch;

import com.spring66.tutorial.model.Users;
import org.springframework.batch.item.file.mapping.FieldSet;
import org.springframework.batch.item.file.mapping.FieldSetMapper;

/**
 *
 * @author Phongsak
 */
public class UsersMapper implements FieldSetMapper {

	public Object mapLine(FieldSet fs) {

		Users user = new Users();

		int     idx = 0;

		user.setUsrId( fs.readString(idx++) );
		user.setUsrName( fs.readString(idx++) );
		user.setUsrPwd( fs.readString(idx++) );

		System.out.println("User is = " + user.toString());

		return user;
	}
}

สังเกตุว่า มันทำการ implements FieldSetMapper นะครับ

เพิ่ง สังเกตุอีกทีว่า โอ้ว นี่ มัน เขียนมาเยอะแ้ล้วนี่นา เลยขอ publish ก่อนนะครับ เดี๋ยวมาต่อ ภาค ต่อไป หมดแรงครับ ดองมานานแล้วด้วย อาจจะจบห้วนไปนิดนึงนะครับ แต่ เพื่อปลุกกระแสๆ อิอิ

blog แรกผมเลยนะครับผิดพลาดประการใดขออภัยครับ แนะนำผมด้วยนะครับ ขอบคุณครับ

เขียน Spring Application ภาคต่อจาก maven

January 14th, 2009

เริ่มต้นเขียน application แรกด้วยสปริง
หลังจากที่บทความที่แล้วเราเริ่มสร้างโปรเจคด้วย Maven เรียบร้อยต่อไปเราจะทำการ เขียนแอพพลิเคชั่นละครับ โดยเราจะเริ่มการทำงานด้วยการเขียน Test ก่อนเพราะเราจะทำงานทั้งหมด
ด้วยแนวคิด Test First แต่ก่อนที่จะเริ่มเขียน Test เราจะต้องเข้าใจแนวคิดพื้นฐานของ Spring Container ก่อนนิดหน่อย
Spring ไม่มอะไรยากครับ ให้จำไว้เสมอว่าเราจะไม่สร้าง เซอร์วิสเองถ้าไม่จำเป็น เราจะถามเอาจาก ApplicationContext ให้สร้างให้เราเสมอ เอ่าแล้ว ApplicationContext คืออะไรเอาง่ายๆสั้นแล้วกัน
ApplicationContext ก็คือ Space หรือ Memory ที่ถูกจองไว้เพื่อใช้เก็บเซอร์วิสที่เราประกาศไว้และเมื่อใดก็ตามที่เราต้องการใช้งานเราเราเพียงแค่ใส่ชื่อเซอร์วิสที่เราต้องการเข้าไปจากนั้นจะมีคนจัด
การโยน instance ของเซอร์วิสนั้นๆกลับมาให้
ดังนั้นก่อนการทำงานทุกๆครั้ง ApplicationContext จะต้องถูกสร้าง จากนั้นมันจะทำการโหลด Configuration File ที่เราประกาศเซอร์วิสของเราไว้เข้าไปเพื่อรอให้เรามาเรียกใช้ และเพื่อไม่ให้เสียเวลา
เราลองมาสร้าง Configuration File ที่ว่านั่นกันโดยจะขอเริ่มจากเซอร์วิสง่ายๆก่อนละกัน โดยเราจะเริ่มจากการสร้างเซอร์วิสปัญญาอ่อนชื่อ “helloWorld”
แต่ช้าก่อนเราจะไม่ทำอะไรเลยถ้าเราไม่ได้เขียน test ก่อนเหอๆๆๆๆๆ กังนั้นมาประสาทแดกกันด้วยการเขียน test ก่อน โดยเราหวังว่าเราจะมี ApplicationContext ที่โหลดไฟล์ชื่อ applicationContext.xml
เข้ามาเตรียมรอท่าเราไว้ ไม่รอท่าเช่นกันไปสร้างคลาส StupidApplicationContextTest เลยครับที่ {project_name}\src\test\java ให้มีรายละเอียดดังนี้ครับ

package com.spring66.tutorial;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
/**
 *
 * @author TwinP
 */
public class StupidApplicationContextTest extends AbstractDependencyInjectionSpringContextTests{
    protected final Log log = LogFactory.getLog(getClass());
    @Override
    protected String[] getConfigLocations() {
               setAutowireMode(AUTOWIRE_BY_NAME);
        return new String[] {
                "classpath:/applicationContext.xml",
                "classpath*:/applicationContext.xml", // for modular projects
                "classpath:**/applicationContext*.xml" // for web projects
            };
    }
    public void testLoadBean(){
        HelloWorld helloService = (HelloWorld)super.applicationContext.getBean("helloWorld");
        log.debug("Frucking");
        assertNotNull(helloService);
    }
}

แดงเถือกแน่นอนอับดับแรก HelloWorld ไม่มีช้าอยู่ทำไมไปสร้างคลาส HelloWorld เลยครับที่ {project_name}\src\main\java

package com.spring66.tutorial;
/**
 *
 * @author TwinP
 */
public class HelloWorld {
    private String message;
    /**
     * @return the message
     */
    public String getMessage() {
        return message;
    }
    /**
     * @param message the message to set
     */
    public void setMessage(String message) {
        this.message = message;
    }
}

ซ่อมได้ไปหนึ่งครับต่อไปไม่แดงแล้วลอง run test ดูครับว่าผ่านไหม (ผ่านคงผีหลอกครับ ==”) ไปที่ console โดยไปที่ root ของโปรเจคจากนั้นเรียก

mvn test -Dtest=StupidApplicationContextTest

ผลที่ออกมาคือ run 1 test, error 1 test ครับโดยสาเหตุที่ error สามารถดูได้ที่ {project_name}\target\surefire-reports\ ครับโดยมันจะด่าเราว่า

org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [applicationContext.xml]; nested exception is java.io.FileNotFoundException: บ่น ๆๆๆๆๆๆๆ

เนื่องจากเราพยายามโหลด

        return new String[] {
                "classpath:/applicationContext.xml",
                "classpath*:/applicationContext.xml" // for modular projects
                //"classpath:**/applicationContext*.xml" // for web projects
            };

แล้วมันหาไม่เจอก๋เลยด่าเรา ==” พระเอกไม่รอช้าแก้ปัญหาอย่างเร็วพลันโดยให้เราสร้างไฟล์ชื่อ applicationContext.xml ไว้ที่ {project_name}\src\main\resources และให้เติมรายละเอียดเหล่านี้เข้าไป

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
	<bean id="helloWorld" class="com.spring66.tutorial.HelloWorld">
		<property name="message" value="How are you?" />
	</bean>
</beans>

หลายคนคงพอจะเดาออกถึงความหมายของเซอร์วิสปัญญาอ่อนอันนี้ bean id=”???” เป็นการบอกชื่อของเซอร์วิสที่เราต้องการใช้ class=”???” เป็นชื่อคลาสแบบเต็มยศ
นั่นคือระบุชื่อแพกเกจเข้าไปด้วยนะครับ property mane=”???” เป็นการกำหนดค่าให้กับ attribute ของคลาส ณ จังหวะที่มันจะถูกเรียกใช้งาน
ลองอีกที

mvn test -Dtest=StupidApplicationContextTest

ผลที่ได้คือ
——————————————————————————-
Test set: com.spring66.tutorial.StupidApplicationContextTest
——————————————————————————-
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.125 sec
หล่อครับ พอแค่นี้ก่อนหมดพลัง