[spring]從頭開始-Initializing Bean

開發工具:IntelliJ IDEA 13

開發環境:jdk1.6.0_45

Framework:spring 3

一般spring在注入時,並非只能單純inject 各個 field 而已,還可以加上 initial Method 來對該field做初始化/驗證的動作,這其中有幾種方法,先從最簡單的 init-method 開始

建立一個可以演繹的類別

SimpleBean1.java

package foo.bar;

import org.springframework.beans.factory.BeanCreationException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.GenericXmlApplicationContext;

/**

 * Created by Hsu on 2014/7/14.

 */

public class SimpleBean1 {

    private static final String DEFAULT_NAME = "Luke Skywalker";

    private String name = null;

    private int age = Integer.MIN_VALUE;

    public void setName(String name) {

        this.name = name;

    }

    public void setAge(int age) {

        this.age = age;

    }

    //用來初始化的Method

    public void init() {

        System.out.println("Initializing bean");

        if (name == null) {

            System.out.println("Using default name");

            name = DEFAULT_NAME;

        }

        if (age == Integer.MIN_VALUE) {

            throw new IllegalArgumentException(

                    "You must set the age property of any beans of type " + SimpleBean1.class);

        }

    }

    public String toString() {

        return "Name: " + name + "\nAge: " + age;

    }

    public static void main(String[] args) {

        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

        ctx.load("classpath:initMethod.xml");

        ctx.refresh(); // Refresh the ApplicationContext after XML config file loaded

        SimpleBean1 simpleBean1 = getBean("simpleBean1", ctx);

        SimpleBean1 simpleBean2 = getBean("simpleBean2", ctx);

        SimpleBean1 simpleBean3 = getBean("simpleBean3", ctx);

    }

    private static SimpleBean1 getBean(String beanName, ApplicationContext ctx) {

        try {

            SimpleBean1 bean =(SimpleBean1) ctx.getBean(beanName);

            System.out.println(bean);

            return bean;

        } catch (BeanCreationException ex) {

            System.out.println("An error occured in bean configuration: "

                    + ex.getMessage());

            return null;

        }

    }

}

接下來重要的配置檔也一併建立

initMethod.xml

..

..標頭的部分省略

    <bean id="simpleBean1"

          class="foo.bar.SimpleBean1"

          init-method="init">

        <property name="name">

            <value>Clarence Ho</value>

        </property>

        <property name="age">

            <value>100</value>

        </property>

    </bean>

    <bean id="simpleBean2"

          class="foo.bar.SimpleBean1"

          init-method="init">

        <property name="age">

            <value>100</value>

        </property>

    </bean>

    <bean id="simpleBean3"

          class="foo.bar.SimpleBean1"

          init-method="init">

        <property name="name">

            <value>Clarence Ho</value>

        </property>

    </bean>

..結尾省略

我們可以看到在配置檔中建立了三個bean,每個bean都各給一個init-method,對應到該bean中的 function init(),直接看結果可以看到其中的關係:

Initializing bean

Initializing bean

Using default name

Initializing bean

Name: Clarence Ho

Age: 100

Name: Luke Skywalker

Age: 100

Name: Clarence Ho

Age: 90

第一個 bean 我們有給 name/age,所以只有出現Initializing bean

第二個 bean 我們只有給 age,經過init Method 後,name套用在該方法中指定的值

第三個 bean 我們只有給 name,但是卻若沒有給age,經過init Method後,則會拋出例外

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simpleBean3' defined in class path resource [initMethod.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: You must set the age property of any beans of type class foo.bar.SimpleBean1

init-method之後,接著是使用Implement InitializingBean 這個類別來演繹

這個方式主要為將要初始化的類別 implement InitializingBean 這個類,先建立一個來看看:

SimpleBeanWithInterface.java

package foo.bar;

import org.springframework.beans.factory.BeanCreationException;

import org.springframework.beans.factory.InitializingBean;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.GenericXmlApplicationContext;

/**

 * Created by Hsu on 2014/7/14.

 */

public class SimpleBeanWithInterface implements InitializingBean {

    private static final String DEFAULT_NAME = "Luke Skywalker";

    private String name = null;

    private int age = Integer.MIN_VALUE;

    public void setName(String name) {

        this.name = name;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public void myInit() {

        System.out.println("My Init");

    }

    @Override

    public void afterPropertiesSet() throws Exception {

        System.out.println("Initializing bean");

        if (name == null) {

            System.out.println("Using default name");

            name = DEFAULT_NAME;

        }

        if (age == Integer.MIN_VALUE) {

            throw new IllegalArgumentException(

                    "You must set the age property of any beans of type " + SimpleBean.class);

        }

    }

    public String toString() {

        return "Name: " + name + "\nAge: " + age;

    }

    public static void main(String[] args) {

        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

        ctx.load("classpath:initInterface.xml");

        ctx.refresh();

        SimpleBeanWithInterface simpleBean1 = getBean("simpleBean1", ctx);

        SimpleBeanWithInterface simpleBean2 = getBean("simpleBean2", ctx);

        SimpleBeanWithInterface simpleBean3 = getBean("simpleBean3", ctx);

    }

    private static SimpleBeanWithInterface getBean(String beanName,

                                                   ApplicationContext ctx) {

        try {

            SimpleBeanWithInterface bean = (SimpleBeanWithInterface) ctx.getBean(beanName);

            System.out.println(bean);

            return bean;

        } catch (BeanCreationException ex) {

            System.out.println("An error occured in bean configuration: "

                    + ex.getMessage());

            return null;

        }

    }

}

最主要需要 Override afterPropertiesSet() 這個方法,將原本寫在init-method中的內容搬到裡面去

配置檔如下:

initInterface.xml

..

..標頭的部分省略

   <!-- 等同使用imit-method -->

    <bean id="simpleBean1"

          class="foo.bar.SimpleBeanWithInterface">

    <property name="name">

        <value>Clarence Ho</value>

    </property>

        <property name="age">

            <value>100</value>

        </property>

    </bean>

    <bean id="simpleBean2"

          class="foo.bar.SimpleBeanWithInterface">

        <property name="age">

            <value>100</value>

        </property>

    </bean>

    <bean id="simpleBean3"

          class="foo.bar.SimpleBeanWithInterface">

        <property name="name">

            <value>Clarence Ho</value>

        </property>

        <property name="age">

            <value>50</value>

        </property>

    </bean>

..結尾省略

執行出來的結果如下:

Initializing bean

Initializing bean

Using default name

Initializing bean

Name: Clarence Ho

Age: 100

Name: Luke Skywalker

Age: 100

Name: Clarence Ho

Age: 50

若未設定Age的話,則將會得到與前一方法相同的 Exception

最後我們使用 Annotation 的方式來演繹相同的狀況,主要是靠 import javax.annotation.PostConstruct 這個類別,並使用 @PostConstruct 來達成。

範例如下:

SimpleBean2.java

package foo.bar;

import org.springframework.beans.factory.BeanCreationException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.GenericXmlApplicationContext;

import javax.annotation.PostConstruct;

/**

 * Created by Hsu on 2014/7/14.

 */

public class SimpleBean2 {

    private static final String DEFAULT_NAME = "Luke Skywalker";

    private String name = null;

    private int age = Integer.MIN_VALUE;

    public void setName(String name) {

        this.name = name;

    }

    public void setAge(int age) {

        this.age = age;

    }

    //宣告該方法為該 bean 的建構方法,對應的方法名稱可以自訂,不一定要使用init

    @PostConstruct

    public void init() {

        System.out.println("Initializing bean");

        if (name == null) {

            System.out.println("Using default name");

            name = DEFAULT_NAME;

        }

        if (age == Integer.MIN_VALUE) {

            throw new IllegalArgumentException(

                    "You must set the age property of any beans of type " + SimpleBean2.class);

        }

    }

    public String toString() {

        return "Name: " + name + "\nAge: " + age;

    }

    public static void main(String[] args) {

        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

        ctx.load("classpath:initMethodByAnnotation.xml");

        ctx.refresh(); // Refresh the ApplicationContext after XML config file loaded

        SimpleBean2 simpleBean1 = getBean("simpleBean1", ctx);

        SimpleBean2 simpleBean2 = getBean("simpleBean2", ctx);

        SimpleBean2 simpleBean3 = getBean("simpleBean3", ctx);

    }

    private static SimpleBean2 getBean(String beanName, ApplicationContext ctx) {

        try {

            SimpleBean2 bean =(SimpleBean2) ctx.getBean(beanName);

            System.out.println(bean);

            return bean;

        } catch (BeanCreationException ex) {

            System.out.println("An error occured in bean configuration: "

                    + ex.getMessage());

            return null;

        }

    }

}

接下來是對應的 spring configuration file

initMethodByAnnotation.xml

..

..標頭的部分省略

    <context:annotation-config />

    <context:component-scan base-package="foo.bar"/>

    <!-- 等同使用imit-method  使用 @PostConstruct-->

    <bean id="simpleBean1"

          class="foo.bar.SimpleBean2">

    <property name="name">

        <value>Clarence Ho</value>

    </property>

    <property name="age">

    <value>100</value>

    </property>

    </bean>

    <bean id="simpleBean2"

          class="foo.bar.SimpleBean2">

        <property name="age">

            <value>100</value>

        </property>

    </bean>

    <bean id="simpleBean3"

          class="foo.bar.SimpleBean2">

        <property name="name">

            <value>Clarence Ho</value>

        </property>

        <property name="age">

            <value>90</value>

        </property>

    </bean>

..結尾省略

執行之後的結果如下:

Name: Clarence Ho

Age: 100

Name: null

Age: 100

Name: Clarence Ho

Age: 90

三種initial的方式,其實看專案的配置方式,最好決定之後,都使用同一種方式,以利維護

demo程式(右鍵另開視窗下載)