Autowiredアノテーションの春
Springフレームワーク内では、Spring @Autowiredアノテーションを使用して自動的に依存関係の注入が行われます。Springフレームワークは依存性の注入をベースに構築されており、クラスの依存関係をSpring Beanの設定ファイルを介して注入します。
@Autowiredアノテーションの春
通常、SpringのBean設定ファイルでBeanの設定詳細を提供し、ref属性を使用して他のBeanに注入するBeanも指定します。しかし、Springフレームワークは、Beanの注入詳細を明示的に提供する必要がない場合でも、自動ワイヤリング機能を提供します。SpringのBeanを自動ワイヤリングする方法はいくつかあります。
-
- autowire byName – このタイプのオートワイヤリングでは、依存関係のインジェクションにはセッターメソッドが使用されます。また、依存関係をインジェクションするクラスとSpring Beanの設定ファイルで変数名が同じである必要があります。
-
- autowire byType – このタイプのオートワイヤリングでは、クラスのタイプが使用されます。そのため、Spring Beanの設定ファイルにはこのタイプ用に1つだけのBeanが設定されている必要があります。
-
- autowire by constructor – これはほぼautowire byTypeと同じですが、違いはコンストラクタが依存関係のインジェクションに使用されることです。
-
- autowire by autodetect – Spring 3.0以前のバージョンを使用している場合、このオートワイヤリングのオプションが利用可能です。このオプションは、SpringコンテナによってコンストラクタまたはbyTypeのオートワイヤリングとして決定されました。既に多くのオプションがあるため、このオプションは非推奨となっています。このチュートリアルではこのオプションは扱いません。
-
- @Autowired注釈 – Springの@Autowired注釈を使用して、Spring Beanのオートワイヤリングを行うことができます。@Autowired注釈は、byTypeのオートワイヤリングのために変数とメソッドに適用することができます。また、constructorを使用したコンストラクタベースのSpringのオートワイヤリングでも@Autowired注釈を使用することができます。@Autowired注釈を使用するには、Spring Beanの設定ファイルで注釈ベースの設定を有効にする必要があります。これは、context:annotation-config要素を定義するか、org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor型のBeanを定義することで行うことができます。
- @Qualifier注釈 – この注釈は、Beanのマッピングでの競合を回避するために使用され、オートワイヤリングに使用するBean名を指定する必要があります。このようにすることで、同じタイプの複数のBeanが定義されている場合の問題を回避することができます。この注釈は通常、@Autowired注釈と一緒に使用されます。複数の引数を持つコンストラクタの場合、この注釈をメソッド内の引数名とともに使用することができます。
デフォルトでは、Spring Beanの自動ワイヤリングは無効になっています。Spring Beanのautowireのデフォルト値は「default」であり、これは自動ワイヤリングが行われないことを意味します。autowire値「no」も同じような挙動を示します。Spring Beanの自動ワイヤリングの使用例を示すために、シンプルなSpring Mavenプロジェクトを作成しましょう。最終的なプロジェクトは以下の画像のようになります。各autowireオプションを1つずつ見てみましょう。そのために、モデルのBeanとサービスクラスを作成し、モデルのBeanをインジェクションします。
Springの@Autowiredアノテーションとは、Mavenの依存関係です。
春の自動ワイヤリングには、追加の依存関係を追加する必要はありません。私たちのpom.xmlファイルには、Springフレームワークのコア依存関係が含まれており、以下のようになります。
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringBeanAutowiring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.2.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
<dependencies>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
スプリングの @Autowired アノテーションによるモデルビーン
簡単なJava Beanである「Employee」という名前のクラスを作りましょう。このBeanには、ゲッターとセッターがあるプロパティを1つ持たせます。このプロパティの値は、SpringのBeanの設定ファイルで初期化します。
package com.scdev.spring.autowiring.model;
public class Employee {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Springの@Autowiredアノテーション – サービスクラス
「Springの自動ワイヤリングを通じて、Employeeのbeanをインジェクトするサービスクラスを作成しましょう。」
package com.scdev.spring.autowiring.service;
import com.scdev.spring.autowiring.model.Employee;
public class EmployeeService {
private Employee employee;
// constructor is used for autowire by constructor
public EmployeeService(Employee emp) {
System.out.println("Autowiring by constructor used");
this.employee = emp;
}
// default constructor to avoid BeanInstantiationException for autowire
// byName or byType
public EmployeeService() {
System.out.println("Default Constructor used");
}
// used for autowire byName and byType
public void setEmployee(Employee emp) {
this.employee = emp;
}
public Employee getEmployee() {
return this.employee;
}
}
私たちは、SpringのbyName、byType、およびコンストラクタによる自動ワイヤリングに対して同じサービスクラスを使用します。
セッターメソッドは、byNameとbyTypeのSpring自動ワイヤリングに使用されます。一方、コンストラクタベースのインジェクションは、コンストラクタ自動ワイヤリング属性によって使用されます。
SpringのbyNameまたはbyTypeを使用する場合、デフォルトコンストラクタが使用されます。そのため、私たちはEmployeeServiceビーンに明示的にデフォルトコンストラクタを定義しています。
Springの@Autowiredアノテーションは、byTypeの自動ワイヤリングの例です。
「Springの@Autowiredアノテーションを使用して、autowiring byTypeのために別のクラスを作成しましょう。」
package com.scdev.spring.autowiring.service;
import org.springframework.beans.factory.annotation.Autowired;
import com.scdev.spring.autowiring.model.Employee;
public class EmployeeAutowiredByTypeService {
//Autowired annotation on variable/setters is equivalent to autowire="byType"
@Autowired
private Employee employee;
@Autowired
public void setEmployee(Employee emp){
this.employee=emp;
}
public Employee getEmployee(){
return this.employee;
}
}
注意してください、Employee変数とそのセッターメソッドの両方に、Springの@Autowiredアノテーションを付けましたが、これらのうちのいずれか1つがSpring Beanの自動連携に十分です。
Springの@Autowiredアノテーションとconstructorの例では、@Qualifierビーンの自動ワイヤリングが行われます。
別のサービスクラスを作成しましょう。ここでは、コンストラクタベースのインジェクションに@Autowiredアノテーションを使用します。また、@Qualifierアノテーションの使い方も確認しましょう。
package com.scdev.spring.autowiring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.scdev.spring.autowiring.model.Employee;
public class EmployeeAutowiredByConstructorService {
private Employee employee;
//Autowired annotation on Constructor is equivalent to autowire="constructor"
@Autowired(required=false)
public EmployeeAutowiredByConstructorService(@Qualifier("employee") Employee emp){
this.employee=emp;
}
public Employee getEmployee() {
return this.employee;
}
}
この豆がSpringフレームワークによって初期化されると、名前が「employee」という豆が自動ワイヤリングに使用されます。Springの@Autowiredアノテーションは、「required」という名前の引数を受け入れます。これはデフォルト値がTRUEのブール値です。適切な豆が自動ワイヤリング用に見つからない場合に、Springフレームワークが例外をスローしないように、「false」に定義することができます。
スプリングの@Autowiredアノテーションとは、Beanの設定ファイルです。
スプリングのアプリケーションにおいて、Spring Beanの設定ファイルは主要な部分です。まずは私たちのSpring Bean設定ファイルの外見を見て、それぞれの部分を調べてみましょう。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.0.xsd"
default-autowire="byName" default-autowire-candidates="*" >
<bean name="employee" class="com.scdev.spring.autowiring.model.Employee">
<property name="name" value="Pankaj"></property>
</bean>
<bean name="employee1" class="com.scdev.spring.autowiring.model.Employee" autowire-candidate="false">
<property name="name" value="Dummy Name"></property>
</bean>
<!-- autowiring byName, bean name should be same as the property name -->
<bean name="employeeServiceByName" class="com.scdev.spring.autowiring.service.EmployeeService" autowire="byName" />
<!-- autowiring byType, there should be only one bean definition for the mapping -->
<bean name="employeeServiceByType" class="com.scdev.spring.autowiring.service.EmployeeService" autowire="byType" />
<!-- autowiring by constructor -->
<bean name="employeeServiceConstructor" class="com.scdev.spring.autowiring.service.EmployeeService" autowire="constructor" />
<!-- Enable Annotation based configuration -->
<context:annotation-config />
<!-- using @Autowiring annotation in below beans, byType and constructor -->
<bean name="employeeAutowiredByTypeService" class="com.scdev.spring.autowiring.service.EmployeeAutowiredByTypeService" />
<bean name="employeeAutowiredByConstructorService" class="com.scdev.spring.autowiring.service.EmployeeAutowiredByConstructorService" />
</beans>
Spring Beanの設定ファイルについての重要なポイントは以下の通りです。
- beans element default-autowire is used to define the default autowiring method. Here I am defining the default autowiring method to be byName.
- beans element default-autowire-candidates is used to provide the pattern for bean names that can be used for autowiring. For simplicity I am allowing all the bean definitions to be eligible for autowiring, however if we can define some pattern for autowiring. For example, if we want only DAO bean definitions for autowiring, we can specify it as default-autowire-candidates=”*DAO”.
- autowire-candidate=”false” is used in a bean definition to make it ineligible for autowiring. It’s useful when we have multiple bean definitions for a single type and we want some of them not to be autowired. For example, in above spring bean configurations “employee1” bean will not be used for autowiring.
- autowire attribute byName, byType and constructor is self understood, nothing much to explain there.
- context:annotation-config is used to enable annotation based configuration support. Notice that employeeAutowiredByTypeService and employeeAutowiredByConstructorService beans don’t have autowire attributes.
Springの@Autowiredアノテーション – テストプログラム
私たちの春のアプリケーションは、すべてのタイプのスプリングオートワイヤリングを備えて準備ができましたので、予想通りに機能するかどうかを確認するために、簡単なテストプログラムを作成しましょう。
package com.scdev.spring.autowiring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.scdev.spring.autowiring.service.EmployeeAutowiredByConstructorService;
import com.scdev.spring.autowiring.service.EmployeeAutowiredByTypeService;
import com.scdev.spring.autowiring.service.EmployeeService;
public class SpringMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
EmployeeService serviceByName = ctx.getBean("employeeServiceByName", EmployeeService.class);
System.out.println("Autowiring byName. Employee Name="+serviceByName.getEmployee().getName());
EmployeeService serviceByType = ctx.getBean("employeeServiceByType", EmployeeService.class);
System.out.println("Autowiring byType. Employee Name="+serviceByType.getEmployee().getName());
EmployeeService serviceByConstructor = ctx.getBean("employeeServiceConstructor", EmployeeService.class);
System.out.println("Autowiring by Constructor. Employee Name="+serviceByConstructor.getEmployee().getName());
//printing hashcode to confirm all the objects are of different type
System.out.println(serviceByName.hashCode()+"::"+serviceByType.hashCode()+"::"+serviceByConstructor.hashCode());
//Testing @Autowired annotations
EmployeeAutowiredByTypeService autowiredByTypeService = ctx.getBean("employeeAutowiredByTypeService",EmployeeAutowiredByTypeService.class);
System.out.println("@Autowired byType. Employee Name="+autowiredByTypeService.getEmployee().getName());
EmployeeAutowiredByConstructorService autowiredByConstructorService = ctx.getBean("employeeAutowiredByConstructorService",EmployeeAutowiredByConstructorService.class);
System.out.println("@Autowired by Constructor. Employee Name="+autowiredByConstructorService.getEmployee().getName());
ctx.close();
}
}
プログラムはシンプルです。ただスプリングアプリケーションコンテキストを作成し、異なるビーンを取得し、従業員の名前を表示しています。上記のアプリケーションを実行すると、以下の出力が得られます。
Mar 31, 2014 10:41:58 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: startup date [Mon Mar 31 22:41:58 PDT 2014]; root of context hierarchy
Mar 31, 2014 10:41:58 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Default Constructor used
Default Constructor used
Autowiring by constructor used
Autowiring byName. Employee Name=Pankaj
Autowiring byType. Employee Name=Pankaj
Autowiring by Constructor. Employee Name=Pankaj
21594592::15571401::1863015320
@Autowired byType. Employee Name=Pankaj
@Autowired by Constructor. Employee Name=Pankaj
Mar 31, 2014 10:41:58 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: startup date [Mon Mar 31 22:41:58 PDT 2014]; root of context hierarchy
以下のように、autowire byNameとbyTypeでは、デフォルトの引数なしコンストラクタを使用してビーンを初期化しています。autowire by constructorでは、パラメータベースのコンストラクタを使用しています。全ての変数のハッシュコードから、全てのスプリングビーンが異なるオブジェクトであり、同じオブジェクトを参照していないことを確認しました。”employee1″をautowiringの対象から除外したため、ビーンのマッピングに混乱はありませんでした。もし”employee1″の定義からautowire-candidate=”false”を削除すると、上記のmainメソッドを実行する際に以下のエラーメッセージが表示されます。
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'employeeServiceByType' defined in class path resource [spring.xml]: Unsatisfied dependency expressed through bean property 'employee': : No qualifying bean of type [com.scdev.spring.autowiring.model.Employee] is defined: expected single matching bean but found 2: employee,employee1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.scdev.spring.autowiring.model.Employee] is defined: expected single matching bean but found 2: employee,employee1
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1278)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1170)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at com.scdev.spring.autowiring.main.SpringMain.main(SpringMain.java:12)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.scdev.spring.autowiring.model.Employee] is defined: expected single matching bean but found 2: employee,employee1
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:967)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:855)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1263)
... 13 more
春の@AutowiredアノテーションとSpringの自動配線機能については以上です。以下のリンクからサンプルプロジェクトをダウンロードし、より詳しく学ぶために分析してください。
スプリングビーンのオートワイヤリングプロジェクトをダウンロードしてください。