Spring学習メモ(Springとは~IoCコンテナ)

仕事でSpringを触る羽目になったことになったので、勉強した内容をメモしておく。

参考にしたのは公式リファレンスとPluralsightのSpringコース。

Spring Framework Reference Documentation

http://www.pluralsight.com/courses/spring-fundamentals

 

  • Springとは

Java開発フレームワーク。Java開発者は、さまざまなデザインパターン(Singleton、Factory、Abstract Factory、Template Method、Decorator、Service Locator…)を駆使してアプリを構築していくが、Springを使えばこれらを自前で実装することなく実現できる。

 

  • 主な特徴
  1. POJO(Plain Old Java Object)ベースなので再利用が容易
  2. J2EEに比べとても軽量
  3. コード横断的な機能を実現するため、AOP(Aspect Oriented Programming)やプロキシパターンを採用
  4. ベストプラクティス、デザインパターンを導入

 

  • 全体像

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/images/spring-overview.png
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/overview.html

 

約20のモジュールで構成される。コアコンテナ、データアクセス・統合、Web、AOP、Instrumentation、メッセージング、テストに分類される。一部のみを採用したり、別フレームワークのコンポーネントを導入したりすることも容易に可能で、これは、IoCコンテナで実現される。

 

 

  • IoCコンテナ ※SpringではDIコンテナではなくIoCコンテナと呼ぶようだ

インターフェースorg.springframework.context.ApplicationContextがSpringのIoCコンテナを表現しており、これがBeanのインスタンス化、コンフィグレーション、構築の役割を担う。IoCコンテナは、XML・Javaアノテーション・またはJavaで書かれた「コンフィグレーションメタデータ」を参照して動く。

 

コード例(http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.htmlより)

 

コンフィグレーションメタデータ(services.xml)

<?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.xsd">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>

IoCコンテナとインスタンスの生成

// create and configure beans
ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();
  1. services.xmlでPetStoreServiceImplクラスを、petStoreという名前で登録し、これをJavaコード側で呼び出している
  2. BeanはデフォルトでSingleton扱い
  3. xmlのpropertyにある「ref="xx"」は他Beanへの依存性。(コンテナが解決する)
  4. Javaコード側では、services.xmlからコンテナを生成
  5. getBean()でPetStoreServiceを取得

 

Beanのインスタンス化

デフォルトでは、コンテナはコンストラクタを使ってBeanを生成するが、Static Factory MethodやInstance Factory Methodを使うこともできる。

 

依存性注入の方法

コンストラクタベース、またはSetterベースの注入ができる。(例はいずれも 

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html より)

 

コンストラクタベースの例

package x.y; 

public class Foo { 
    public Foo(Bar bar, Baz baz) {
        // ... 
    } 
}
<beans> 
    <bean id="foo" class="x.y.Foo"> 
        <constructor-arg ref="bar"/> 
        <constructor-arg ref="baz"/> 
    </bean> 

    <bean id="bar" class="x.y.Bar"/> 

    <bean id="baz" class="x.y.Baz"/> 
</beans>

Setterベースの例

public class ExampleBean {
    private AnotherBean beanOne; 
    private YetAnotherBean beanTwo; 
    private int i; 

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne; 
    } 

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo; 
    } 

    public void setIntegerProperty(int i){
       this.i = i; 
    } 
}
<bean id="exampleBean" class="examples.ExampleBean"> 
    <!-- setter injection using the nested ref element --> 
    <property name="beanOne"> 
        <ref bean="anotherExampleBean"/> 
    </property> 

    <!-- setter injection using the neater ref attribute --> 
    <property name="beanTwo" ref="yetAnotherBean"/> 
    <property name="integerProperty" value="1"/> 
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/> 
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

なお後述するように、@Autowiredアノテーションを使って、Setterなしのメンバーに直接注入することも可能。

 

PropertyPlaceholderConfigurer

XMLに設定する実際の値は、別ファイルに定義することができる。(例はいずれも 

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html より)

 

<context:property-placeholder location="classpath:com/foo/jdbc.properties"/> 

<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"> 
    <property name="driverClassName" value="${jdbc.driverClassName}"/> 
    <property name="url" value="${jdbc.url}"/> 
    <property name="username" value="${jdbc.username}"/> 
    <property name="password" value="${jdbc.password}"/> 
</bean>

jdbc.properties

jdbc.driverClassName=org.hsqldb.jdbcDriver 
jdbc.url=jdbc:hsqldb:hsql://production:9002 
jdbc.username=sa 
jdbc.password=root

上記のように、使うためにはPropertyPlaceholderConfigurerの定義が必要。

 

Annotation-based Configuration

メタデータの一部をアノテーションを使ってJavaクラスに移すことにより、XML(とトータルの記載量)を削ることができる。(コード例はhttp://dev.classmethod.jp/server-side/java/spring-firstcontact/ より。一部改変)

 

@Autowired

<bean id="worker" class="jp.classmethod.sample.spring.start.SpringQueueWorker">
  <property name="sqs" ref="amazonSQSClient" />
  <property name="mailSender" ref="javaMailSenderImpl" />
  <property name="mailTemplate" ref="templateMessage" />
</bean>
public class QueueWorker {
  private AmazonSQS sqs;
  private MailSender mailSender;
  private SimpleMailMessage mailTemplate;
  
  public void setSqs(AmazonSQS sqs) {
    this.sqs = sqs;
  }
    
  public void setMailSender(MailSender mailSender) {
    this.mailSender = mailSender;
  }
    
  public void setMailTemplate(SimpleMailMessage mailTemplate) {
    this.mailTemplate = mailTemplate;
  }   
}

上記は、@Autowiredアノテーションで次のように書き換えできる。

<context:annotation-config /> 
<bean id="worker" class="jp.classmethod.sample.spring.start.SpringQueueWorker“ />
public class QueueWorker {
  private AmazonSQS sqs;
  private MailSender mailSender;
  private SimpleMailMessage mailTemplate;
 
  @Autowired 
  public void setSqs(AmazonSQS sqs) {
    this.sqs = sqs;
  }
  
  @Autowired
  public void setMailSender(MailSender mailSender) {
    this.mailSender = mailSender;
  }
  
  @Autowired
  public void setMailTemplate(SimpleMailMessage mailTemplate) {
    this.mailTemplate = mailTemplate;
  }   
}

コンテナは、自動的にターゲットのBeanをTypeから判別して取得・注入する。

public class QueueWorker {
  @Autowired
  private AmazonSQS sqs;

  @Autowired
  private MailSender mailSender;

  @Autowired
  private SimpleMailMessage mailTemplate;
}

フィールドに直接Autowireすることもできる。このとき、コンテナはリフレクションで値をセットする。

public class QueueWorker {
  private AmazonSQS sqs;
  private MailSender mailSender;
  private SimpleMailMessage mailTemplate;

  @Autowired 
  public QueueWorker(AmazonSQS sqs, MailSender mailSender, 
                     SimpleMailMessage mailTemplate) {
    this.sqs = sqs;
    this.mailSender = mailSender;
    this.mailTemplate = mailTemplate;
  }
}

コンストラクタベースの例。

 

@Component

コンポーネントスキャンを使うと、Beanの定義自体をXMLから削れる。

<context:annotation-config />
<context:component-scan base-package="jp.classmethod.sample.spring.start" />
<!-- <bean id="worker" class="jp.classmethod.sample.spring.start.SpringQueueWorker" /> -->
@Component
public class QueueWorker {
  @Autowired
  private AmazonSQS sqs;

  @Autowired
  private MailSender mailSender;

  @Autowired
  private SimpleMailMessage mailTemplate;
}

Springはターゲットになっているパッケージをスキャンし、@Componentアノテーションつきのクラスを自動でBeanとして登録する。

@Component以外にも、@Repository(データアクセスレイヤ用)、@Service(ビジネスサービスレイヤ用)、@Controller(MVCのコントローラ)が、コンポーネントスキャンの対象。(ステレオタイプアノテーションと呼ぶ)

Bean名は、小文字のクラス名になる(例:QueueWorker→queueWorker)が、指定することもできる。

@Component(“MyQueueWorker”)
public class QueueWorker {
  @Autowired
  private AmazonSQS sqs;

  @Autowired
  private MailSender mailSender;

  @Autowired
  private SimpleMailMessage mailTemplate;
}

 

IoCコンテナ関連のその他のトピック

  1. XMLのProperty値(文字列)は、ConversionServiceで変換されて使用される
  2. XMLのProperty部の記載量を減らすため、p-namespaceという方法がある
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" 
        destroy-method="close" 
        p:driverClassName="com.mysql.jdbc.Driver" 
        p:url="jdbc:mysql://localhost:3306/mydb" 
        p:username="root" 
        p:password="masterkaoli"/>
    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html より)
  3. Beanのスコープは、①Singleton(デフォルト)、②Prototype(リクエストのたびに新インスタンスを生成)、③Request(Web)、④Session(Web)、⑤Global Session(Web)、⑥Application(Web)が設定できる
  4. SingletonのBeanは、アプリケーションの初期化時にすべて生成されるが、遅延させるオプションもある
  5. コンフィグレーションメタデータは、XMLを使わず、すべてJavaで書くこともできる