Search on the blog

2013年2月10日日曜日

Learn Spring Framework (1)

Springについて復習しました。最近はアノテーションで楽に設定ができるようになりましたが、まずは基本からということでxml設定ファイルを利用したDIについてまとめます。

Setter Injection
一番基本的なインジェクションです。

Setter Injectionの説明に入る前に、Spring FrameworkからBeanを取得するためのファクトリークラスを作っておきます。
package com.kenjih.java.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanFactory {
    private static final String BEAN_CONFIG_FILE = "beans.xml";
    
    public static Object getBean(String name) {
        ApplicationContext context = new ClassPathXmlApplicationContext(BEAN_CONFIG_FILE);
        return context.getBean(name);
    }
}
以下のサンプルでは上記のクラスを使ってBeanを取得することとします。

それでは、Setter Injectionのサンプルに入ります。 まず、Beanの定義ファイルから。
<?xml version="1.0" encoding="UTF-8"?>
<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="DISample" class="com.kenjih.java.main.DISample">
        <property name="service">
            <ref bean="helloService" />
        </property>
    </bean>

    <bean id="helloService" class="com.kenjih.java.main.HelloServiceImpl">
    </bean>

</beans>
次に、クライアントクラス。HelloServiceインターフェースを実装したクラスを使用しますが具体的なクラス名をコード上から排除しています。ポイントはsetServiceメソッドです。IoCによって、Spring Frameworkからこのメソッドが呼ばれBeanが注入されます。
package com.kenjih.java.main;

public class DISample {
    private HelloService service;
    
    public void setService(HelloService service) {
        this.service = service;
    }
    
    public void doSomething() {
        service.say();
    }
    
    public static void main(String[] args) {
        DISample bean = (DISample) BeanFactory.getBean("DISample");
        bean.doSomething();
    }
}
次に、サービスクラスのインターフェース。
package com.kenjih.java.main;

public interface HelloService {
    public void say();
}
そんでもって、サービスクラスの実装とテスト用のモッククラス。クライアントクラスにはどの実装を用いるか書いてないので、ソースコードを変更することなくどちらのクラスを使用するか切り替えることができます。
package com.kenjih.java.main;

public class HelloServiceImpl implements HelloService {

    @Override
    public void say() {
        System.out.println("Hello, world!");
    }

}
package com.kenjih.java.test;

import com.kenjih.java.main.HelloService;

public class HelloServiceMock implements HelloService {

    @Override
    public void say() {
        System.out.println("Hello, world! -- mock -- ");
    }

}
Constructor Injection
コンストラクタを使ってインジェクションすることもできます。
<?xml version="1.0" encoding="UTF-8"?>
<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="DISample" class="com.kenjih.java.main.DISample">
        <constructor-arg>
            <ref bean="helloService" />
        </constructor-arg>
    </bean>

    <bean id="helloService" class="com.kenjih.java.main.HelloServiceImpl">
    </bean>

</beans>
のように設定ファイルを書き換えます。コンストラクタを通じてBeanを注入するので、コンストラクタでサービスクラスを受け取るようにします。
package com.kenjih.java.main;

public class DISample {
    private HelloService service;
    
    public DISample(HelloService service) {
        this.service = service;
    }
    
    public void doSomething() {
        service.say();
    }
    
    public static void main(String[] args) {
        DISample bean = (DISample) BeanFactory.getBean("DISample");
        bean.doSomething();
    }
}
Factory Method Injection
Staticなファクトリーメソッドを使ってBeanを生成する場合も、Constructor Injectionと同様にインジェクションが行えます。 設定ファイルは以下のようにします。beanタグのfactory-method属性でどのメソッドをファクトリーメソッドとして使用するか指定します。
<?xml version="1.0" encoding="UTF-8"?>
<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="DISample" class="com.kenjih.java.main.DISample" factory-method="createInstance">
        <constructor-arg>
            <ref bean="helloService" />
        </constructor-arg>
    </bean>

    <bean id="helloService" class="com.kenjih.java.main.HelloServiceImpl">
    </bean>

</beans>
こんな感じで使えます。
package com.kenjih.java.main;

public class DISample {
    private HelloService service;
    
    private DISample() {
        
    }
    
    public static DISample createInstance(HelloService service) {
        DISample obj = new DISample();
        obj.service = service;
        
        return obj;
    }
        
    public void doSomething() {
        service.say();
    }
    
    public static void main(String[] args) {
        DISample bean = (DISample) BeanFactory.getBean("DISample");
        bean.doSomething();
    }
}
Lookup Method Injection
これは知らなかったのですが、面白いです。DIコンテナ配下にあるBeanのメソッドの戻り値を書き換えることができます。djUnitの仮想モックオブジェクトに似てると思います。
<?xml version="1.0" encoding="UTF-8"?>
<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="DISample" class="com.kenjih.java.main.DISample">
        <lookup-method name="getService" bean="helloService" />
    </bean>

    <bean id="helloService" class="com.kenjih.java.test.HelloServiceMock" scope="prototype"/>
</beans>
のように設定すると、あら不思議。ソースコード上ではgetServiceでImplクラスを返しているのに、実行するとモッククラスが返されます。
package com.kenjih.java.main;

public class DISample {
    public HelloService getService() {
        return new HelloServiceImpl();
    }
    
    public static void main(String[] args) {
        DISample bean = (DISample) BeanFactory.getBean("DISample");
        
        HelloService service = bean.getService();
        service.say();
    }
}

0 件のコメント:

コメントを投稿