スッキリ!SpringでJSR-330を使うための最小サンプル

ℹ️ Spring 5.3版で書き直しました

SpringはJSR-330 (DI)を実装している。 JSR-330の実装でスタンドアローンで使うならGuiceだよねーという声が聞こえたので、Springでも結構さくっと使えるよっていうサンプル。 XMLはいまや不要ですよ。

全部でこれだけ

pom.xml

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>minimam-spring-jsr330</groupId>
	<artifactId>minimam-spring-jsr330</artifactId>
	<version>1.0.0-SNAPSHOT</version>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>3.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>
	</dependencies>
</project>

DIを使うだけならspring-contextだけで十分(AOPもついてきますが)

コンポーネント作成

package sample;

import javax.inject.Named;

@Named
public class GreetingService {
	public String hello(String message) {
		return "Hello " + message + "!";
	}
}

@Namedをつけておけば後で設定するコンポーネントスキャンにより、DIコンテナに管理されます。 JSR-330準拠だとデフォルトscopeがprototype(毎回生成)になります。

JSRにこだわらなければ@Serviceとか@Componentとか責務のわかるアノテーション付ける方が好き。

Injectionする

コンポーネントを使う側

package sample;

import javax.inject.Inject;

public class MyApp {
	@Inject
	protected GreetingService greetingService;

	public void run(String message) {
		System.out.println(greetingService.hello(message));
	}
}

@Injectでインジェクションします。

JavaConfig

SpringおなじみのBean定義XMLファイルの代わりにJavaでBean定義できます。

package sample;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Jsr330ScopeMetadataResolver;

@Configuration
@ComponentScan(basePackages = "sample", scopeResolver = Jsr330ScopeMetadataResolver.class)
public class AppConfig {
	@Bean
	public MyApp myApp() {
		return new MyApp();
	}
}

@ConfigurationつけるとBean定義ファイル相当になります。@Beanをつけたメソッドが返すオブジェクトがDIコンテナに管理されます。<bean class="sample.MyApp" />と同義。

また@ComponentScanで指定したパッケージ以下のクラスで@Namedがついているものは自動でDIコンテナに登録されます。<contect:component-scan base-packages="sample">相当。 Jsr330ScopeMetadataResolverを指定するとJSR-330準拠になり、デフォルトscopeがprototypeになります。指定しなければSpringの慣習に合わせてデフォルトscopeがsingletonになります。

エントリポイント

package sample;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

public class Main {

	public static void main(String[] args) {
		try (GenericApplicationContext applicationContext = 
				new AnnotationConfigApplicationContext(AppConfig.class)) {
			MyApp app = applicationContext.getBean(MyApp.class);
			app.run("World");
		}
	}

}

AnnotationConfigApplicationContextにJavaConfigファイルを渡してDIコンテナ作成。 getBeanでDIコンテナに管理されたBeanを取得できます。タイプセーフです。

簡単だったでしょ?Springはいまはかなりシンプルに使えます。

Todo

GuiceのサンプルとWeldのサンプルも作ろう