Auto-wiring
config設定中autowire屬性可以依據當時的的狀況(no(預設)、byName、byType、constructor、autodetect)去自動綁定,而不需要去大量去撰寫的程式碼。
- byName
Customer.java
package com.example; public class Customer { private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Override public String toString() { return "Customer [address=" + address + "]"; } }
Address.java
package com.example; public class Address { private String fulladdress; public String getFulladdress() { return fulladdress; } public void setFulladdress(String fulladdress) { this.fulladdress = fulladdress; } @Override public String toString() { return "Address [fulladdress=" + fulladdress + "]"; } }
Main.java
package com.example; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "Beans.xml"); Customer cust = (Customer) context.getBean("customer"); System.out.println(cust); } }
修改前Beans.xml
<bean id="customer" class="com.example.Customer" >
<property name="address" ref="address" />
</bean>
<bean id="address" class="com.example.Address" >
<property name="fulladdress" value="Taipei, TW" />
</bean>
修改後Beans.xml
<bean id="customer" class="com.example.Customer" autowired="byName"/>
<bean id="address" class="com.example.Address" >
<property name="fulladdress" value="Taipei, TW" />
</bean>
修改前/後結果顯示如下所示:
Customer [address=Address [fulladdress=Taipei, TW]]
當autowire是使用byName時,address bean id的名稱需和customer bean的名稱(address)相同。若是設定不同則會產生下列結果:
Beans.xml
<bean id="customer" class="com.example.Customer" autowire="byName"/>
<bean id="addressA" class="com.example.Address" >
<property name="fulladdress" value="Taipei, TW" />
</bean>
結果顯示如下所示:
Customer [address=null]
- byType
Person.java
package com.example1; public class Person { private Ability ability; public Ability getAbility() { return ability; } public void setAbility(Ability ability) { this.ability = ability; } @Override public String toString() { return "Person [ability=" + ability + "]"; } }
Ability.java
package com.example1; public class Ability { private String skill; public String getSkill() { return skill; } public void setSkill(String skill) { this.skill = skill; } @Override public String toString() { return "Ability [skill=" + skill + "]"; } }
Main.java
package com.example1; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "Beans1.xml"); Person person = (Person) context.getBean("person"); System.out.println(person); } }
修改前Beans1.xml
<bean id="person" class="com.example1.Person" >
<property name="ability" ref="invisible" />
</bean>
<bean id="invisible" class="com.example1.Ability" >
<property name="skill" value="Invisible" />
</bean>
修改後Beans1.xml
<bean id="person" class="com.example1.Person" autowire="byType" />
<bean id="invisible" class="com.example1.Ability" >
<property name="skill" value="Invisible" />
</bean>
修改前/後結果顯示如下所示:
Person [ability=Ability [skill=Invisible]]
需要注意的是當autowire設定為byType時,bean的data type必須是唯一的,否則會出現下列錯誤:
Beans1.xml
<bean id="person" class="com.example1.Person" autowire="byType" />
<bean id="steal" class="com.example1.Ability" >
<property name="skill" value="Steal" />
</bean>
<bean id="invisible" class="com.example1.Ability" >
<property name="skill" value="Invisible" />
</bean>
結果顯示如下所示:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'person' defined in class path resource [Beans1.xml]: Unsatisfied dependency expressed through bean property 'ability': :** No qualifying bean of type [com.example1.Ability] is defined: expected single matching bean but found 2: steal,invisible; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.example1.Ability] is defined: expected single matching bean but found 2: steal,invisible**
- constructor
In Spring, “Autowiring by Constructor” is actually autowiring by Type in constructor argument. It means, if data type of a bean is same as the data type of other bean constructor argument, auto wire it.
Developer.java
package com.example2; public class Developer { private Language language; public Developer(Language language) { this.language = language; } @Override public String toString() { return "Developer [language=" + language + "]"; } }
Language.java
package com.example2; public class Language { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Language [name=" + name + "]"; } }
Main.java
package com.example2; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "Beans2.xml"); Developer developer = (Developer) context.getBean("developer"); System.out.println(developer); } }
修改前Beans2.xml
<bean id="developer" class="com.example2.Developer">
<constructor-arg>
<ref bean="language"/>
</constructor-arg>
</bean>
<bean id="language" class="com.example2.Language" >
<property name="name" value="Java" />
</bean>
修改後Beans2.xml
<bean id="developer" class="com.example2.Developer" autowire="constructor"/>
<bean id="language" class="com.example2.Language" >
<property name="name" value="Java" />
</bean>
修改前/後結果顯示如下所示:
Developer [language=Language [name=Java]]
- autodetect
autowire若是為autodetect時,default autowire為constructor,若找不到則是改為byType。
autowire並沒有辦法在設定檔自動偵測是否有綁定,因此可透過dependency- check去檢查綁定狀況,主要有四種相依檢查方式:simple、objects、all、none(預設)。simple是檢查primitivey資料型態、objects是檢查物件屬性、all則是檢查所有屬性。
Beans2.xml
<bean id="developer" class="com.example2.Developer" dependency-check="all"/>
<bean id="language" class="com.example2.Language" >
<property name="name" value="Java" />
</bean>
如果相依檢查發現有未完成的依賴關係,則會丟出Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example2.Developer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.example2.Developer.()
需注意的是使用dependency-check需將http://www.springframework.org/schema/beans/spring-beans-3.0.xsd改為http://www.springframework.org/schema/beans/spring-beans-2.5.xsd,否則會出現下列訊息:
Exception in thread "main" org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 6 in XML document from class path resource [Beans2.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 6; columnNumber: 101; cvc-complex-type.3.2.2: 不允許屬性 'dependency-check' 出現在元素 'bean' 中。