読者です 読者をやめる 読者になる 読者になる

感謝のプログラミング 10000時間

たどり着いた結果(さき)は、感謝でした。

@Autowiredと@Serviceアノテーションで、Dependency Injectionしてみる。

Spring
<スポンサーリンク>

Spring Frameworkを使う利点のひとつに、依存性注入が簡単に使えることがあります。
Spring MVCコンテナに依存性を注入する一番簡単な方法は、@Autowiredというアノテーションを使うことです。
org.springframework.beans.factory.annotationというパッケージにあります。

クラスが依存性を見つけるためには、@Serviceというアノテーションが付けられていなければなりません。
Serviceというアノテーションが付けられたクラスは、「サービスである」という印になります。
org.springframework.stereotypeパッケージにあります。

また、設定ファイルには、component-scanエレメントを加える必要があります。

<context:component-scan base-package="dependencyPackage" />


サービス(@Service)として宣言したクラスを、@Autowiredを使ってインジェクションしてみます。

全体のフォルダ構成は以下のとおりで、前に書いたSpringの記事のものとほとんど同じです。
このブログの「Spring」タグの記事を見てもらえればと思います。


f:id:sho322:20140603180409j:plain
f:id:sho322:20140603180429j:plain


まずはサービスから。

・FriendService

package service;

import domain.Friend;

public interface FriendService {
	Friend add(Friend friend);
	Friend get(long id);
}

上のインターフェースを実装したのが以下のクラスです。
FriendというJavaBeansのようなものを、IDと共にハッシュマップで持っています。

・FriendServiceImpl

package service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import org.springframework.stereotype.Service;

import domain.Friend;

@Service
public class FriendServiceImpl implements FriendService {
	private Map<Long, Friend> friends = new HashMap<Long, Friend>();
	private AtomicLong generator = new AtomicLong();
	
	public FriendServiceImpl() {
		Friend friend = new Friend();
		friend.setName("urabe sho");
		friend.setAge(28);
		friend.setHeight(180.5F);
		add(friend);
	}
	
	public Friend add(Friend friend) {
		long newId = generator.incrementAndGet();
		friend.setId(newId);
		friends.put(newId,friend);
		return friend;
	}
	
	public Friend get(long id) {
		return friends.get(id);
	}
	
	
}

で、上で作成したServiceクラスをインジェクションしてみます。

package controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import service.FriendService;
import domain.Friend;
import form.FriendForm;

@Controller
public class FriendController {

	private static final Log logger =
			LogFactory.getLog(FriendController.class);
	
	@Autowired
	private FriendService friendService;
	
	@RequestMapping(value="/friend_input")
	public String inputFriend() {
		logger.info("inputFriend called");
		return "FriendForm";
	}
	
	@RequestMapping(value="/friend_save")
	public String saveFriend(FriendForm friendForm, RedirectAttributes redirectAttributes) {
		logger.info("saveFriend called");
		
		Friend friend = new Friend();
		friend.setName(friendForm.getName());
		friend.setAge(Integer.parseInt(friendForm.getAge()));
		friend.setHeight(Float.parseFloat(friendForm.getHeight()));
		
		
		//★このfriendServiceはnewで明示的にインスタンス化しなくても使える!!★
		Friend savedFriend = friendService.add(friend);
		
		redirectAttributes.addFlashAttribute("message","友人の登録に成功しました!");
		
		return "redirect:/friend_view/" + savedFriend.getId();
	}
	
	@RequestMapping(value = "/friend_view/{id}")
	public String viewFriend(@PathVariable Long id, Model model) {
		Friend friend = friendService.get(id);
		model.addAttribute("friend",friend);
		return "FriendView";
	}
}

@Autowiredをつけることで、インジェクションが可能となり、

Friend savedFriend = friendService.add(friend);

のように、friendServiceはnewで明示的にインスタンス化しなくても使えるようになります。

ただ、このサービスを使うためには、設定ファイルにサービスが存在するパッケージを記載しなければいけません。
context:component-scanの記載に注目です。

■springmvc-config.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
	<context:component-scan
		base-package="controller"/>
	<context:component-scan base-package="service"/>
	<mvc:annotation-driven/>
	<!--  
	<mvc:resources mapping="/*.html" location="/" />
	<mvc:resources mapping="/css/**" location="/css/" />
	-->
	<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />
	
	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
</beans>

以下のように、サービスが存在するパッケージをSpring MVCに知らせます。

<context:component-scan base-package="service"/>

そうすることで、サービスを依存性注入することができるようになります。

Spring MVCでPOSTした日本語が以下のように、文字化けする場合は、web.xmlに以下を追記してください。

f:id:sho322:20140603180506j:plain

   <filter>
        <filter-name>encoding-filter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>encoding-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>    

フィルターをかませることによって、エンコードが統一されて、文字化けを解消できます・

f:id:sho322:20140603180527j:plain


<参考文献>

日本語のSpring関連の書籍は記載が古いので、英語が読めるならこの本をKindleで購入するのがオススメです。

Kindle Paperwhite(ニューモデル)

Kindle Paperwhite(ニューモデル)

Kindleは技術書を読むのにものすごく便利。
10秒でダウンロードできるし、技術書はだいたい電子化されてるし。
英語の勉強にもなります。