Node.jsを触ってみた。

UbuntuにNode.jsをインストールし、Hello World!してみた。

フルスクリーン

apt-get install python g++ make
mkdir nodejs
cd nodejs
wget http://nodejs.org/dist/v0.8.6/node-v0.8.6.tar.gz
tar zxvf node-v0.8.6.tar.gz
cd node-v0.8.6/
./configure
sudo make
sudo make install
node --version
cd
mkdir nodejswork
cd nodejswork
mkdir hello
cd hello
vi hello.js

hello.js

var sys = require('sys');
var http = require('http');
var port = process.env.PORT || 8081;
 
var server = http.createServer(
    function (request, response) {
 
        response.writeHead(200, {'Content-Type': 'text/plain'});
        response.write('Hello World!!\n');
        response.end();
    }
).listen(port);

sys.log('Server running at http://127.0.0.1:'+ port + '/');

node hello.js

Spring3 tc Server起動時にエラー発生

Spring3 開発環境を整備しようの記事で、下記のように書きました。

[Servers]ペインの「VMWare vFabric tc Server Developper Edition」を右クリックでコンテキストメニューを開き、「Start」メニューを選択すると、STSにビルトインされているWebサーバ(vFabrid tc Server)が起動します。
http://localhost:8080」にアクセスして下記の画面が表示されれば、STSのインストールおよびWebサーバの起動が成功です。

私の環境では、1回目の起動はうまく言ったのですが、なぜか2回目の起動時には下記のようなエラーが出ます。

重大: クラス org.springframework.web.context.ContextLoaderListener のリスナインスタンスにコンテキスト初期化イベントを送信中の例外です
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0' defined in class path resource [com/springsource/insight/repo/app-config-db.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cache-with-props' defined in URL [jar:file:/C:/springsource/vfabric-tc-server-developer-2.7.0.RELEASE/spring-insight-instance/webapps/insight/WEB-INF/lib/insight-repo-1.8.3.RELEASE.jar!/com/springsource/insight/repo/app-config-gemfire.xml]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: The following required files could not be found: *.crf files with these ids: [1], *.drf files with these ids: [1].
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
	at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:710)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:410)
	at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:276)
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:197)
	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4779)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5273)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:895)
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:871)
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:615)
	at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:649)
	at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1585)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
	at java.util.concurrent.FutureTask.run(FutureTask.java:166)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
	at java.lang.Thread.run(Thread.java:722)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cache-with-props' defined in URL [jar:file:/C:/springsource/vfabric-tc-server-developer-2.7.0.RELEASE/spring-insight-instance/webapps/insight/WEB-INF/lib/insight-repo-1.8.3.RELEASE.jar!/com/springsource/insight/repo/app-config-gemfire.xml]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: The following required files could not be found: *.crf files with these ids: [1], *.drf files with these ids: [1].
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:398)
	at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:275)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:139)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.<init>(PersistenceExceptionTranslationInterceptor.java:79)
	at org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisor.<init>(PersistenceExceptionTranslationAdvisor.java:70)
	at org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor.setBeanFactory(PersistenceExceptionTranslationPostProcessor.java:99)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1439)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1408)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
	... 24 more
Caused by: java.lang.IllegalStateException: The following required files could not be found: *.crf files with these ids: [1], *.drf files with these ids: [1].
	at com.gemstone.gemfire.internal.cache.DiskInitFile.verifyOplogs(DiskInitFile.java:619)
	at com.gemstone.gemfire.internal.cache.DiskStoreImpl.loadFiles(DiskStoreImpl.java:1926)
	at com.gemstone.gemfire.internal.cache.DiskStoreImpl.<init>(DiskStoreImpl.java:326)
	at com.gemstone.gemfire.internal.cache.DiskStoreImpl.<init>(DiskStoreImpl.java:251)
	at com.gemstone.gemfire.internal.cache.DiskStoreImpl.<init>(DiskStoreImpl.java:248)
	at com.gemstone.gemfire.internal.cache.DiskStoreFactoryImpl.create(DiskStoreFactoryImpl.java:111)
	at com.gemstone.gemfire.internal.cache.xmlcache.CacheCreation.create(CacheCreation.java:323)
	at com.gemstone.gemfire.internal.cache.xmlcache.CacheXmlParser.create(CacheXmlParser.java:222)
	at com.gemstone.gemfire.internal.cache.GemFireCacheImpl.loadCacheXml(GemFireCacheImpl.java:3029)
	at org.springframework.data.gemfire.CacheFactoryBean.afterPropertiesSet(CacheFactoryBean.java:104)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
	at org.springfram
	ework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
	... 39 more

5 26, 2012 12:30:55 午後 org.apache.catalina.core.StandardContext startInternal
重大: Error listenerStart
5 26, 2012 12:30:55 午後 org.apache.catalina.core.StandardContext startInternal
重大: 以前のエラーのためにコンテキストの起動が失敗しました [/insight]


この状態でも、http://localhost:8080/ にアクセスするとtc ServerのWebページが表示されるので、サーバは起動しているようですが、何か気持ちが悪いので調べてみました。

同じような問題で悩んでいる人を見つけました。

http://forum.springsource.org/showthread.php?125133

まさに、この人と同じくWindows7 64bit版で"C:\springsource"にインストールしていましたので、ビンゴ!!って思ったけど、

でも、回答が「質問をInsight forumに移したよ。Insight関連の質問に見えたので。」って。。。Insight forumって何???どこ???

よくわからんけど、その後色々と探して、別のエラーが出ている人でどうやらサーバを削除して作り直せば直った的な情報があったので、試してみることに。

サーバの削除

Serversペインで「VMWare vFabric tc Server Developper Edition」を右クリックでコンテキストメニューを開き、[delete]メニューを選択。

「Delete unused server configratioin(s)」にチェックして[OK]ボタンを押下。


サーバの追加

Serverペインの「new server wizard...」をクリック。

そのまま[Next>]ボタン押下。

ここでもそのまま[Next>]ボタン押下。

[name]に「loccalhost」と入力し、[Templates]は良くわからないけど、とりあえず「base」「bio」「bio-ssl」にチェックをつけてみた。

[Finish]ボタン押下。

動作確認


何度かtc Serverを起動してみたけど、エラーは出ていない様子。

それにしても、起動がえらく早くなったなぁ。「Templates」に3つしかチェックしなかったからかなぁ。

大丈夫かなぁ。まぁ、気にしないでおこうっと。

※続報
Spring Insightっていう機能があるみたいです。プロファイラのようなもののよう。

で、Templatesの中にもInsightっていうテンプレートがあって、これにチェックをつけてサーバを作ると、上記のエラーが再現しました。
なので、やはりInsight関連の問題のようです。

Spring3 AOPに挑戦(2)

それでは、ジョインポイントの情報をコンソールに表示してみましょう。SampleIntercepterのbeforeメソッドを下記のように修正します。

	@Before("execution(* jp.org.tomotaro.aop.*.*(..))")
	public void before(JoinPoint joinPoint) throws Throwable {
		System.out.println("Before Method started excuting...");
		Signature signature = joinPoint.getSignature();
		System.out.println(signature.getDeclaringTypeName()+ "." + signature.getName());
	}

コンソールに次のように表示されるはずです。

Before Method started excuting...
jp.org.tomotaro.aop.Service.getMessage
getMessage() called.
Hello world!

2行目の「jp.org.tomotaro.aop.Service.getMessage」が今回追加したジョインポイントの情報です。

このように、beforeメソッドにJoinPointの引数を追加するだけで、Springからジョインポイントの情報を受け取れるようになります。ちなみに、beforeメソッドのように特定のジョインポイントで実行されるアクションのことをAdviceと言います。

今回、Before Adviceのメソッド名をbeforeとしましたが、自由に変更することが出来ます。

また、AdviceにはBefore、After、AfterReturning、AfterThrowing、Aroundなどがあり、それぞれ次のようなタイミングで起動されます。

Before Advice: ジョインポイントの実行前に起動されるアドバイス
After Advice: ジョインポイントの実行後に起動されるアドバイス。ジョインポイントが正常終了時、例外発生時、いずれの場合でも起動される。
AfterReturning Advice: ジョインポイントの正常終了後に起動されるアドバイス
AfterThrowing Advice: ジョインポイントの例外発生後に起動されるアドバイス
Around Advice: ジョインポイントの周囲で起動するアドバイス。アドバイス内でジョインポイントを起動する。

SampleIntercepterに他のAdviceも実装してみました。

ちなみに、先ほどは@Beforeアノテーションでジョインポイントを直接指定していましたが、他のアドバイスにもそれぞれ直接ジョインポイントを指定してしまうと、ジョインポイントを変更したい場合に、すべてを変更しなくてはならなくなってしまい、メンテナンス性が良くありません。

したがって、ポイントカットにジョインポイントを指定し、それぞれのアノテーションにはポイントカットを指定するようにしています。

package jp.org.tomotaro.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class SampleIntercepter {
	@Pointcut("execution(* jp.org.tomotaro.aop.*.*(..))")
	public void pointCut()	{
	}

	//@Before("execution(* jp.org.tomotaro.aop.*.*(..))")
	@Before("pointCut()")
	public void before(JoinPoint joinPoint) throws Throwable {
		System.out.println("Before Method started excuting...");
		Signature signature = joinPoint.getSignature();
		System.out.println(signature.getDeclaringTypeName()+ "." + signature.getName());
	}
	
	@After("pointCut()")
	public void after(JoinPoint joinPoint) throws Throwable {
		System.out.println("After Method executed...");
		Signature signature = joinPoint.getSignature();
		System.out.println(signature.getDeclaringTypeName() + "." + signature.getName());		
	}
	
	@AfterReturning(value = "pointCut()",  returning = "returnValue")
	public void afterReturning(JoinPoint joinPoint, Object returnValue) throws Throwable {
		System.out.println("After Method returning...");
		Signature signature = joinPoint.getSignature();
		System.out.println(signature.getDeclaringTypeName() + "." + signature.getName());
		System.out.println("returnValue=" + returnValue.toString());
	}
	
	@AfterThrowing(value = "pointCut()", throwing = "throwable")
	public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
		System.out.println("After Exception throwing...");
		Signature signature = joinPoint.getSignature();
		System.out.println(signature.getDeclaringTypeName() + "." + signature.getName());
		System.out.println(throwable.getMessage());
	}
	
	@Around(value = "pointCut()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("Around start...");
		Signature signature = joinPoint.getSignature();
		System.out.println(signature.getDeclaringTypeName() + "." + signature.getName());

		Object returnValue = joinPoint.proceed();
		
		System.out.println("Around end...");
		if(returnValue != null) {
			System.out.println("Around returnValue=" + returnValue.toString());
		}
		
		return returnValue;
	}
}

そして、ServiceとExampleServiceにthrowExceptionメソッドを追加します。

package jp.org.tomotaro.aop;

public interface Service {
	String getMessage();
	void throwException() throws Exception;
}

 

package jp.org.tomotaro.aop;

import org.springframework.stereotype.Component;

@Component
public class ExampleService implements Service {
	public String getMessage() {
		System.out.println("getMessage() called.");
		return "Hello world!";	
	}

	public void throwException() throws Exception {
		throw new Exception("Test!!");
	}
}

最後にTesterのmainメソッドにServiceのthrowExceptionメソッド呼び出しを追加します。

package jp.org.tomotaro.test;

import jp.org.tomotaro.aop.Service;

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

public class Tester {
	public static void main(String[] args) {
		ApplicationContext appContext = 
			new ClassPathXmlApplicationContext(
				"classpath:./META-INF/spring/app-context.xml");		
		Service service = appContext.getBean(Service.class);
		String message = service.getMessage();
		System.out.println(message);
		
		try {
			service.throwException();
		} catch (Exception e) {
		}
	}
}

実行すると、コンソールに下記のように表示されます。

Before Method started excuting...
jp.org.tomotaro.aop.Service.getMessage
Around start...
jp.org.tomotaro.aop.Service.getMessage
getMessage() called.
After Method executed...
jp.org.tomotaro.aop.Service.getMessage
After Method returning...
jp.org.tomotaro.aop.Service.getMessage
returnValue=Hello world!
Around end...
Around returnValue=Hello world!
Hello world!
Before Method started excuting...
jp.org.tomotaro.aop.Service.throwException
Around start...
jp.org.tomotaro.aop.Service.throwException
After Method executed...
jp.org.tomotaro.aop.Service.throwException
After Exception throwing...
jp.org.tomotaro.aop.Service.throwException
Test!!

サンプルソースAOPSample(2).zip

Spring3 AOPに挑戦(1)

さて、今度はAOPに挑戦です。

HelloWorldと同様に[File]-[New]-[Spring Template Project]メニューで[Simple Spring Utility Project]でプロジェクトを生成するのが手っ取り早いです。
[Project name]に「AOPSample」、top-level packageに「jp.org.tomotaro.aop」と入力して[Finish]ボタンを押下する。

src/main/resources/META-INF/spring/app-context.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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<description>Example configuration to get you started.</description>

	<aop:aspectj-autoproxy />
	<context:component-scan base-package="jp.org.tomotaro.aop" />

</beans>

次に、pom.xmlに下記のようにspring-aop、aspectjrt、aspectjweaverのdependencyを追加する。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples.spring</groupId>
	<artifactId>spring-utility</artifactId>
	<version>1.0.0.CI-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>Spring Utility</name>
	<url>http://www.springframework.org</url>
	<description>
		<![CDATA[
      This project is a minimal jar utility with Spring configuration.
    ]]>
	</description>
	<properties>
		<maven.test.failure.ignore>true</maven.test.failure.ignore>
		<spring.framework.version>3.0.6.RELEASE</spring.framework.version>
	</properties>	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.framework.version}</version>
		</dependency> 
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.11</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.6.11</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.14</version>
		</dependency>
	</dependencies>

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

ExampleService.javaのgetMessage()メソッドを下記のように修正する。

	public String getMessage() {
		System.out.println("getMessage() called.");
		return "Hello world!";	
	}


[File]-[New]-[Class]で「jp.org.tomotaro.aop.SampleIntercepter」を生成し、下記のように修正する。

package jp.org.tomotaro.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class SampleIntercepter {
	@Before("execution(* jp.org.tomotaro.aop.*.*(..))")
	public void before() throws Throwable {
		System.out.println("Before Method started excuting...");
	}
}

@Beforeアノテーションの引数の「"execution(* jp.org.tomotaro.aop.*.*(..))"」は、アスペクトの挿入先であるジョインポイントを指定しています。

executionは「<戻り値の型> <パッケージ名>.<クラス名>.<メソッド名>(<引数>)」の形で指定します。

引数は下記のように指定します。
()     パラメータなし
(..)    どんな引数でも良い(引数なしでもいい)
(*)     どんな引数でも良いが引数の数はひとつだけ
(*,String) 二つ引数を取り、一つ目の型は何でもよいが、二つ目はString型。

つまり、「"execution(* jp.org.tomotaro.aop.*.*(..))"」は戻り値の型と引数は何でも良く、jp.org.tomotaro.aopパッケージのすべてのクラスのすべてのメソッドにアスペクトを挿入することを指定しています。

[File]-[New]-[Class]で「jp.org.tomotaro.test.Tester」を生成し、下記のように修正する。

package jp.org.tomotaro.test;

import jp.org.tomotaro.aop.Service;

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

public class Tester {
	public static void main(String[] args) {
		ApplicationContext appContext = 
			new ClassPathXmlApplicationContext(
				"classpath:./META-INF/spring/app-context.xml");
		Service service = appContext.getBean(Service.class);
		String message = service.getMessage();
		System.out.println(message);
	}
}


パッケージエクスプローラでTester.javaを選択し、[Run As]-[AspectJ/Java Application]を選択して実行すると、下記のようにコンソールに表示されます。

Before Method started excuting...
getMessage() called.
Hello world!

ExampleServiceのgetMessageメソッドがコールする前にSampleIntercepterのbeforeメソッドが実行されたことが確認できます。

AOPの使いどころとしてはロギング処理が多いと思います。ロギング処理ではコールされたメソッドのクラス名やメソッド名、引数などをロギングしたいと思いますが、SampleIntercepterのbeforeメソッドではそういった情報が取れません。

どうすれば良いのでしょうか?

それは、。。。(次回へ続く)

サンプルソース:AOPSample(1).zip

Spring3 DIでHello World

開発環境も整ったところで、定番のHello WorldをSpring3のDIを使ってやってみました。


[File]-[New]-[Spring Template Project]を選択して「New Template Project」ウィザードを起動します。

①「Simple Spring Utility Project」を選択して[Next]ボタン押下
②[Project name]に「HelloWorld」、top-level packageに「jp.org.tomotaro」を入力して[Finish]ボタン押下


すると、次のようなファイル群が生成されます。

app-context.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"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<description>Example configuration to get you started.</description>

	<context:component-scan base-package="jp.org.tomotaro" />

</beans>

context:component-scanタグでBeanをjp.org.tomotaroパッケージから検索することを指定しています。

Service.javaは下記のような内容のインタフェースです。

package jp.org.tomotaro;

public interface Service {
	String getMessage();
}

getMessageメソッドのみを定義しています。

ExampleService.javaがServiceインタフェースの実装クラスです。

package jp.org.tomotaro;

import org.springframework.stereotype.Component;

@Component
public class ExampleService implements Service {
	public String getMessage() {
		return "Hello world!";	
	}
}


getMessageメソッドで「Hello world!」という文字列を返しているだけです。
注目すべきは@Componentアノテーションです。このアノテーションを付けることによって、component-scanの検索対象のBeanとなります。

ExampleConfigurationTests.javaとExampleServiceTests.javaはExampleSerivseクラスのJUnitテストケースです。
それぞれを選択して右クリックでコンテキストメニューを開き、[Run As]-[JUnit Test]を選択してJUnitを走らせてみると、テストが成功すると思います。

それでは、Serviceを利用するアプリケーションを作りましょう。

[File]-[New]-[Class]メニューでMain.javaを生成して、下記のように入力します。

package jp.org.tomotaro;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class Main {
	@Autowired
	private Service service;

	public void sayHello() {
		System.out.println(service.getMessage());
	}
	
	public static void main(String[] args) {
		ApplicationContext appContext = 
				new ClassPathXmlApplicationContext("classpath:./META-INF/spring/app-context.xml");
		Main main = appContext.getBean(Main.class);
		main.sayHello();
	}
}

mainメソッドでは、app-context.xmlを読み込んでApplicationContextを生成し、MainクラスのBeanを取得してsayHelloメソッドを呼び出しています。
sayHelloメソッドでは@Autowiredアノテーションで自動的にインジェクトされたServiceインタフェース実装オブジェクトのgetMessageメソッドで取得したメッセージをコンソールに表示しています。
app-context.xmlでBeanのスキャン対象を「jp.org.tomotaro」にしているので、serviceにはExampleServiceのインスタンスが生成・挿入されます。

実行してみると、コンソールに「Hello world!」と無事表示されました。


しかし、mainメソッドに

System.out.println("Hello world!");

と1行書くだけのプログラムと見た目の動作自体は変わりませんが、なぜこんな回りくどいことをするんでしょうか?

以下のようなjp.org.tomotaro.stub.ServiceStub.javaを作ってみましょう。

package jp.org.tomotaro.stub;

import jp.org.tomotaro.Service;

import org.springframework.stereotype.Component;

@Component
public class ServiceStub implements Service {
	public String getMessage() {
		return "This is a Stub of Service.";
	}
}

そして、app-context.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"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<description>Example configuration to get you started.</description>

	<context:component-scan base-package="jp.org.tomotaro.stub" />
	<bean id="main" class="jp.org.tomotaro.Main" />

</beans>

Beanの検索対象を「jp.org.tomotaro.stub」に変更しました。
しかし、これだとMainクラスのBeanが検索できないので、beanタグを追加しました。

実行してみると、コンソールに「This is a Stub of Service.」と表示されました。

mainメソッドに直接System.out.println(...);と書いていると、プログラムそのものを修正する必要がありますが、DIを利用すると設定だけで実装を切り替えることが出来ます。
単体試験時にはDAOをスタブに切り替えたいといった場面などに重宝します。

ちなみに、Beanの検索はcomponent-scanで指定したパッケージ以下のBeanを検索するようです。つまり、サブパッケージ内のBeanも検索されると言うことです。

ですので、この状態でcomponent-scanを「jp.org.tomotaro」に戻すと、Serviceインタフェース実装クラスが複数見つかったと言う意味の例外が発生します。

従いまして、ExampleServiceのパッケージをjp.org.tomotaro.exampleに変更し、component-scanには「jp.org.tomotaro.example」を指定するようにしました。

サンプルソースHelloWorld.zip

(次回はAOPに挑戦。つづく)

※しかし、なんでソースコードの字が小さくって、行間が開いてしまうんかなぁ???誰か解決方法を知っていたら教えてください。m(__)m

Spring3 開発環境を整備しよう

こんにちは。ブログの更新をだいぶサボっていました。

先日まで、業務で初めてSpring3 MVCを使用した開発を行っていました。

Springって聞くと、XMLだらけでめんどっちいイメージがあったのですが、がらりと印象が変わりました。

2.5あたりからアノテーションを使用した設定が出来るようになっていたみたいです。

良いですねぇ。Spring MVC。Play!のMVCや.NET MVCに近い感じがしました。

残念なことに、仕事ではSpringのDIやAOPの機能を使用する機会がありませんでした。

Spring MVCが気に入ったついでに、GW中にDIやAOPな部分も深堀りしてみようと思いついた次第です。

あと、実際に調べてみて分かったのですが、Spring3に関する日本語の情報が意外と少ないようですね。なので、せっかくだから、調べた内容をメモ書きとしてブログに投稿しておこうと思った次第です。

何はともあれ、まずは開発環境の整備から。


JDKのインストール

せっかくなので、下記URLからJava7(update 4)をダウンロードしてインストールしてみました。

http://www.oracle.com/technetwork/java/javase/downloads/index.html

IDEのインストール

Eclipseプラグインをインストールする方法もあるようですが、SpringSource Tool Suite(STS)というEclipseベースのオールインワンパッケージがあるので、そちらをインストールしたほうが楽なので、下記URLからダウンロードしてインストールしてみました。

STSにはMavenJUnitなどもあらかじめ含まれているので便利です。

http://www.springsource.org/springsource-tool-suite-download

インストールの途中でSTSのインストール先のパスを設定する画面が出てきますので、適当なディレクトリを指定すれば良いのですが、Windowsの場合は「C:\Program Files」以下のパスを指定しない方が良いようです。近頃のJavaは、スペースを含むパスでも正常に処理できるようですが、初期のJavaではそれがトラブルの種になることもしばしばありました。あと、各種設定を保存しようとした際に「C:\Program Files」以下には保存する権限がないと言ったようなエラーが出ることもあるようです。

ですので今回は「C:\springsource」にインストールするようにしてみました。

また、JDKのパスを設定する画面が出てくるので、先ほどインストールしたJDKのパスを設定します。(JREではなく、JDKのパスです。デフォルトではJDKは「C:\Program Files\Java\jdk1.7.0_04」にインストールされているはずです。こちらもパスにスペースが含まれていますが、このままでも問題ないようです。)


STSの起動

起動すると、ワークスペースのパスを確認するダイアログが出てくるので、そのままでも良いし、適当なパスを指定してOKボタンを押下します。

Welcomeページを閉じると下記のような画面になります。

[Servers]ペインの「VMWare vFabric tc Server Developper Edition」を右クリックでコンテキストメニューを開き、「Start」メニューを選択すると、STSにビルトインされているWebサーバ(vFabrid tc Server)が起動します。

その際に、下記のようなダイアログが出ますが、[Yes]ボタンを押下します。

コンソールに「情報: Server startup in xxxxx ms」と表示されたら、[Open Web Browser]ボタンを押下するか、他のブラウザを起動して「http://localhost:8080」にアクセスして下記の画面が表示されれば、STSのインストールおよびWebサーバの起動が成功です。


(次回へつづく)