如何學習Spring框架
了解Spring的出現(xiàn)是為了解決什么問題R迤稹@场!
了解Spring的出現(xiàn)是為了解決什么問題D铡R喂住!
了解Spring的出現(xiàn)是為了解決什么問題G钣肌M僚恪!
學習Spring的兩個核心概念以及其實現(xiàn)方式(IoC和AOP)
Spring 控制反轉 (IoC)
Spring(IOC:控制反轉):從前需要我們自己做的事肴熏,現(xiàn)在要交給方法的調用者來處理(Sprig的IOC容器來處理)
如何實現(xiàn)自己的IOC容器
創(chuàng)建beans.xml
<beans>
<bean id="u" class="com.foreknow.dao.impl.UserDAOImpl"></bean>
<bean id="userService" class="com.foreknow.service.impl.UserServiceImpl">
<!--UserDAO userDAO = new UserDAOImpl() -->
<!--依賴注入 /控制反轉IOC -->
<property name="userDAO" bean="u"></property>
</bean>
</beans>
解析XML的方式有幾種:
XML的解析方式分為四種:1鬼雀、DOM解析;2蛙吏、SAX解析源哩;3鞋吉、JDOM解析;4励烦、DOM4J解析谓着。其中前兩種屬于基礎方法,是官方提供的平臺無關的解析方式坛掠;后兩種屬于擴展方法赊锚,它們是在基礎的方法上擴展出來的,只適用于java平臺屉栓。
我們可以使用JDOM來解決以上問題
在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>com.foreknow</groupId>
<artifactId>foreknow_spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>foreknow_spring</name>
<dependencies>
<!-- https://mvnrepository.com/artifact/jdom/jdom -->
<dependency>
<groupId>jdom</groupId>
<artifactId>jdom</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
創(chuàng)建接口BeanFacory
package com.foreknow.spring;
public interface BeanFactory {
/**
* 根據key獲取到value
* @param id(key)
* @return Object
*/
public Object getBean(String id);
}
創(chuàng)建實現(xiàn)類ClassPathXmlApplicationContext
package com.foreknow.spring;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import com.foreknow.dao.UserDAO;
import com.foreknow.model.User;
import com.foreknow.service.UserService;
import com.foreknow.service.impl.UserServiceImpl;
public class ClassPathXmlApplicationContext implements BeanFactory {
//創(chuàng)建一個工廠容器
private Map<String, Object> beans = new HashMap<>();
//使用構造器來解析beans.xml文件
public ClassPathXmlApplicationContext() throws Exception{
//1.創(chuàng)建一個解析器
SAXBuilder saxBuilder = new SAXBuilder();
//2.解析并讀取beans.xml文件 構造Document文檔對象
Document document = saxBuilder.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml"));
//3.獲取到根節(jié)點
Element element = document.getRootElement();
//4.獲取到子節(jié)點
List<Element> list = element.getChildren("bean");
for(int i = 0;i<list.size();i++){
Element e = list.get(i);//bean
String id = e.getAttributeValue("id");//u
String clazz = e.getAttributeValue("class");//com.foreknow.dao.impl.UserDAOImpl
System.out.println(id);
System.out.println(clazz);
//要將clazz轉換為UserDAOImpl對象并將id以及對象保存到Map集合中
Object o = Class.forName(clazz).newInstance();
beans.put(id, o);
//獲取到bean中的子節(jié)點property
for(Element propertyElement:(List<Element>)e.getChildren("property")){
String name = propertyElement.getAttributeValue("name");//userDAO
String bean = propertyElement.getAttributeValue("bean");//u
//從Map中獲取到UserDAOImpl對象
Object beanObject = beans.get(bean);
//模擬調用public void setUserDAO(UserDAO userDAO)
String methodName = "set"+ name.substring(0, 1).toUpperCase()+name.substring(1);
System.out.println(methodName);
//需要java中的反射機制來自動調用setUserDAO方法
//o.getClass().getMethod獲取到UserServiceImpl對象中的某一個方法setUserDAO
//beanObject.getClass().getInterfaces()[0]獲取到方法的參數(shù)UserDAO
Method method = o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);
//方法的調用 o:UserServiceImpl對象中的setUserDAO(UserDAO userDAO)方法
//beanObject:調用此方法時需要傳入的參數(shù) UserDAOImpl對象
method.invoke(o, beanObject);
}
}
}
@Override
public Object getBean(String id) {
// TODO Auto-generated method stub
return beans.get(id);
}
public static void main(String[] args) {
try {
// UserServiceImpl userService = new UserServiceImpl();
// BeanFactory beanFactory = new ClassPathXmlApplicationContext();
// UserDAO userDAO = (UserDAO)beanFactory.getBean("u");//UserDAOImpl對象
// userService.setUserDAO(userDAO);
// userService.addInfo(new User());
BeanFactory beanFactory = new ClassPathXmlApplicationContext();
//從工廠容器中獲取到UserService對象
UserService userService = (UserService)beanFactory.getBean("userService");
userService.addInfo(new User());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
什么是IOC
IOC—Inversion of Control舷蒲,即“控制反轉”,不是什么技術友多,而是一種設計思想牲平。在Java開發(fā)中,IOC意味著將你設計好的對象交給容器控制域滥,而不是傳統(tǒng)的在你的對象內部直接控制纵柿。如何理解好IOC呢?理解好IOC的關鍵是要明確“誰控制誰启绰,控制什么昂儒,為何是反轉(有反轉就應該有正轉了),哪些方面反轉了”酬土,那我們來深入分析一下:
●誰控制誰荆忍,控制什么:傳統(tǒng)Java SE程序設計,我們直接在對象內部通過new進行創(chuàng)建對象撤缴,是程序主動去創(chuàng)建依賴對象刹枉;而IOC是有專門一個容器來創(chuàng)建這些對象,即由IOC容器來控制對 象的創(chuàng)建屈呕;誰控制誰微宝?當然是IOC容器控制了對象;控制什么虎眨?那就是主要控制了外部資源獲取蟋软。
●為何是反轉,哪些方面反轉了:有反轉就有正轉嗽桩,傳統(tǒng)應用程序是由我們自己在對象中主動控制去直接獲取依賴對象岳守,也就是正轉;而反轉則是由容器來幫忙創(chuàng)建及注入依賴對象碌冶;為何是反轉湿痢?因為由容器幫我們查找及注入依賴對象,對象只是被動的接受依賴對象,所以是反轉譬重;哪些方面反轉了拒逮?依賴對象的獲取被反轉了。
IOC能做什么
IOC 不是一種技術臀规,只是一種思想滩援,一個重要的面向對象編程的法則,它能指導我們如何設計出松耦合塔嬉、更優(yōu)良的程序玩徊。傳統(tǒng)應用程序都是由我們在類內部主動創(chuàng)建依賴對象,從而導致類與類之間高耦合邑遏,難于測試佣赖;有了IOC容器后恰矩,把創(chuàng)建和查找依賴對象的控制權交給了容器记盒,由容器進行注入組合對象,所以對象與對象之間是 松散耦合外傅,這樣也方便測試纪吮,利于功能復用,更重要的是使得程序的整個體系結構變得非常靈活萎胰。
其實IOC對編程帶來的最大改變不是從代碼上碾盟,而是從思想上,發(fā)生了“主從換位”的變化技竟。應用程序原本是老大冰肴,要獲取什么資源都是主動出擊,但是在IOC/DI思想中榔组,應用程序就變成被動的了熙尉,被動的等待IOC容器來創(chuàng)建并注入它所需要的資源了。
IOC很好的體現(xiàn)了面向對象設計法則之一—— 好萊塢法則:“別找我們搓扯,我們找你”检痰;即由IOC容器幫對象找相應的依賴對象并注入,而不是由對象主動去找锨推。
IOC和DI的關系
IOC和DI由什么關系呢铅歼?其實它們是同一個概念的不同角度描述,由于控制反轉概念比較含糊(可能只是理解為容器控制對象這一個層面换可,很難讓人想到誰來維護對象關系)椎椰,所以2004年大師級人物Martin Fowler又給出了一個新的名字:“依賴注入”,相對IoC 而言沾鳄,“依賴注入”明確描述了“被注入對象依賴IoC容器配置依賴對象”慨飘。
DI—Dependency Injection,即“依賴注入”:組件之間依賴關系由容器在運行期決定洞渔,形象的說套媚,即由容器動態(tài)的將某個依賴關系注入到組件之中缚态。依賴注入的目的并非為軟件系統(tǒng)帶來更多功能,而是為了提升組件重用的頻率堤瘤,并為系統(tǒng)搭建一個靈活玫芦、可擴展的平臺。通過依賴注入機制本辐,我們只需要通過簡單的配置桥帆,而無需任何代碼就可指定目標需要的資源,完成自身的業(yè)務邏輯慎皱,而不需要關心具體的資源來自何處老虫,由誰實現(xiàn)。
理解DI的關鍵是:“誰依賴誰茫多,為什么需要依賴祈匙,誰注入誰,注入了什么”天揖,那我們來深入分析一下:
●誰依賴于誰:當然是應用程序依賴于IOC容器夺欲;
●為什么需要依賴:應用程序需要IOC容器來提供對象需要的外部資源;
●誰注入誰:很明顯是IOC容器注入應用程序某個對象今膊,應用程序依賴的對象些阅;
●注入了什么:就是注入某個對象所需要的外部資源(包括對象、資源斑唬、常量數(shù)據)市埋。
Spring配制
聲明一個簡單的bean
第一個例子:
首先設置一個接口Perofrmance表示參賽者。
package com.moonlit.myspring;
public interface Performer {
void perform() throws PerformanceException;
}
創(chuàng)建一個Juggler(雜技師)類繼承Performer表示參賽者是雜技師恕刘。
package com.moonlit.myspring;
public class Juggler implements Performer {
private int beanBags = 3;
public Juggler() {
}
public Juggler(int beanBags) {
this.beanBags = beanBags;
}
public void perform() throws PerformanceException {
System.out.println("JUGGLING " + beanBags + " BEANBAGS");
}
}
在spring-idol.xml配置文件中定義一個名為duke的bean缤谎,他對應Juggler類(把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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="duke" class="com.moonlit.myspring.Juggler" />
</beans>
測試代碼:
package com.moonlit.practice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.moonlit.myspring.PerformanceException;
import com.moonlit.myspring.Performer;
public class FirstBean {
public static void main(String[] args) throws PerformanceException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-idol.xml");
Performer performer = (Performer) context.getBean("duke");
performer.perform();
}
}
運行結果如下:
JUGGLING 3 BEANBAGS
理解:首先定義了一個接口Performer雪营,然后寫了一個類Juggler繼承自Peformer,Juggler有一個私有成員變量beanBags,他的默認值是3弓千,然后Juggler實現(xiàn)了Performer的perform方法,方法的輸出帶有beanBags的數(shù)量献起。
然后在測試的程序中洋访,通過ApplicationContext類型對象加載了spring-idol.xml文件的內容,而在xml文件中定義了名為"duke"的bean谴餐,然后剛好就用到了姻政。
然后bean返回的是一個Juggler,所以將
Performer performer = (Performer) context.getBean("duke");
改成
Juggler performer = (Juggler) context.getBean("duke");
也是可以的岂嗓,但是在這里想看的效果是通過application context返回的是不是一個Juggler汁展,因為通過輸出的結果就可以知道了,所以這里用(Performer),對中輸出的效果顯示bean對應的Performer真的是一個Juggler食绿,這就是通過xml定義一個bean并通過application context獲得這個bean對象的整個過程侈咕。
構造器注入
之前講到的名為"duke"的bean有一個私有成員變量beanBags代表這個雜技師bean的一次性能夠拋出的最多的數(shù)量,Juggler有一個構造函數(shù)器紧,構造函數(shù)的第一個參數(shù)(這里只有一個參數(shù))beanBags是一個整型的值耀销,用于傳遞給Juggler的私有成員變量beanBags。
構造器注入的方法是:在bean中添加一個constructor-arg(如果構造函數(shù)的參數(shù)有兩個铲汪,那就添加兩個constructor-arg)熊尉。
在spring-idol.xml中修改bean "duke"如下:
<bean id="duke" class="com.moonlit.myspring.Juggler" >
<constructor-arg name="beanBags" value="15" />
</bean>
再次運行FirstBean程序,輸出如下:
JUGGLING 15 BEANBAGS
可以看到通過構造器諸如已經把duke的beanBags改為了15掌腰。
構造函數(shù)中的參數(shù)可能不是一個基礎類型的變量狰住,而可能是一個變量,這個時候只要把constructor-arg的value改成ref即可齿梁,ref對應的值需要被聲明稱一個bean元素催植。
使用一個會唱歌的雜技師PoeticJuggler類來演示,PoeticJuggler繼承自Juggler士飒,它具有一個Poem類型的私有成員變量poem查邢,代表他要朗誦的詩歌。
Poem類:
package com.moonlit.myspring;
public interface Poem
{
void recite();
}
定義一首名為Sonnet29的類用于表示名為一首sonnet29的詩:http://shakespeare-online.com/sonnets/29.html
Sonnet29實現(xiàn)了Poem接口酵幕。
package com.moonlit.myspring;
public class Sonnet29 implements Poem {
private static String[] LINES = {
"When, in disgrace with fortune and men's eyes,",
"I all alone beweep my outcast state,",
"And trouble deaf heaven with my bootless cries,",
"And look upon myself, and curse my fate,",
"Wishing me like to one more rich in hope,",
"Featur'd like him, like him with friends possess'd,",
"Desiring this man's art and that man's scope,",
"With what I most enjoy contented least;",
"Yet in these thoughts myself almost despising,",
"Haply I think on thee, and then my state,",
"Like to the lark at break of day arising",
"From sullen earth, sings hymns at heaven's gate;",
"For thy sweet love remember'd such wealth brings",
"That then I scorn to change my state with kings.",
};
public Sonnet29() {
}
public void recite() {
for (String line : LINES)
System.out.println(line);
}
}
有了Poem和他的一個實現(xiàn)類Sonnet29之后,開始來寫PoeticJuggler缓苛,他繼承自Juggler并且有一個Poem類型私有成員變量poem芳撒。
package com.moonlit.myspring;
public class PoeticJuggler extends Juggler {
private Poem poem;
public PoeticJuggler(Poem poem) {
super();
this.poem = poem;
}
public PoeticJuggler(int beanBags, Poem poem) {
super(beanBags);
this.poem = poem;
}
public void perform() throws PerformanceException {
super.perform();
System.out.println("While reciting...");
poem.recite();
}
}
并且,需要在xml文件中聲明Sonnet29和PoeticJuggler類對應的bean未桥。
<bean id="sonnet29" class="com.moonlit.myspring.Sonnet29" />
<bean id="poeticDuke" class="com.moonlit.myspring.PoeticJuggler">
<constructor-arg value="16" />
<constructor-arg ref="sonnet29" />
</bean>
可以看到笔刹,"poeticDuke"使用了兩個constructor-arg來聲明參數(shù),第一個參數(shù)使用value冬耿,第二個參數(shù)使用ref舌菜,Sonnet29類型的bean--"connet29"。
使用測試程序查看效果:
package com.moonlit.practice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.moonlit.myspring.PerformanceException;
import com.moonlit.myspring.Performer;
public class FirstBean {
public static void main(String[] args) throws PerformanceException {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-idol.xml");
Performer performer = (Performer) context.getBean("duke");
performer.perform();
}
}
程序輸出如下:
JUGGLING 16 BEANBAGS
While reciting...
When, in disgrace with fortune and men's eyes,
I all alone beweep my outcast state,
And trouble deaf heaven with my bootless cries,
And look upon myself, and curse my fate,
Wishing me like to one more rich in hope,
Featur'd like him, like him with friends possess'd,
Desiring this man's art and that man's scope,
With what I most enjoy contented least;
Yet in these thoughts myself almost despising,
Haply I think on thee, and then my state,
Like to the lark at break of day arising
From sullen earth, sings hymns at heaven's gate;
For thy sweet love remember'd such wealth brings
That then I scorn to change my state with kings.
理解:可以通過構造器注入來模擬構造函數(shù)傳入的參數(shù)亦镶,通過constructor-arg value="XX"傳遞一個基本類型的參數(shù)XX日月,通過constructor-arg ref="XX"傳遞一個bean。
注入各種bean屬性
這里通過一個MoonlightPoet類來演示了注入Bean屬性property的效果缤骨。
package com.moonlit.myspring;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Properties;
public class MoonlightPoet {
private String name;
private int age;
private Poem poem;
private List<String> list;
private Map<String, String> map;
public void perform() {
System.out.println("name : " + name);
System.out.println("age : " + age);
poem.recite();
for (String val : list)
System.out.println("in list : " + val);
for (Entry<String, String> entry : map.entrySet())
System.out.println("in map : " + entry.getKey() + " -- " + entry.getValue());
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-idol.xml");
MoonlightPoet moonlightPoet = (MoonlightPoet) context.getBean("moonlightPoet");
moonlightPoet.perform();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Poem getPoem() {
return poem;
}
public void setPoem(Poem poem) {
this.poem = poem;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
該bean在xml文件中定義如下:
<bean id="moonlightPoet" class="com.moonlit.myspring.MoonlightPoet">
<property name="name" value="moonlit" />
<property name="age" value="22" />
<property name="poem" ref="sonnet29" />
<property name="list">
<list>
<value>hello</value>
<value>world</value>
<!-- if bean, use <ref bean="XX"> -->
</list>
</property>
<property name="map">
<map>
<entry key="key1" value="value1" />
<entry key="key2" value="value2" />
<entry key="key3" value="value3" />
</map>
</property>
</bean>
輸出結果:
name : moonlit
age : 22
When, in disgrace with fortune and men's eyes,
I all alone beweep my outcast state,
And trouble deaf heaven with my bootless cries,
And look upon myself, and curse my fate,
Wishing me like to one more rich in hope,
Featur'd like him, like him with friends possess'd,
Desiring this man's art and that man's scope,
With what I most enjoy contented least;
Yet in these thoughts myself almost despising,
Haply I think on thee, and then my state,
Like to the lark at break of day arising
From sullen earth, sings hymns at heaven's gate;
For thy sweet love remember'd such wealth brings
That then I scorn to change my state with kings.
in list : hello
in list : world
in map : key1 -- value1
in map : key2 -- value2
in map : key3 -- value3
理解:
注入簡單值:
<property name="XX" value="YY" />
其中XX是變量名爱咬,YY是值。
引用其他Bean:
<property name="XX" ref="YY">
其中XX是變量名绊起,YY是引用的bean的id精拟。
裝配List:
<property name="XX">
<value>YY</value>
或者
<ref bean="ZZ">
</property>
其中XX是變量名,YY是值,ZZ是引用的bean蜂绎。
裝配Map:
<map>
<entry key="XX" value="YY" />
或者
<entry key="XX" value-ref="YY" />
或者
<entry key-ref="XX" value="YY" />
或者
<entry key-ref="XX" value-ref="YY" />
</map>
因為map的key和value可以對應一個基礎類型的值栅表,也可以對應一個bean,所以key,value對應值师枣,key-ref,value-ref對應bean谨读。
使用Spring的命名空間p裝配屬性
可以在beans中添加
xmlns:p="http:www.springframework.org/schema/beans"
來使用p:作為<bean>元素所有屬性的前綴來裝配Bean的屬性。用法如下:
<bean id="kenny" class="XX"
p:song = "Jingle Bells"
p:instrument-ref = "saxphone" />
-ref后綴作為一個標識來告知Spring應該裝配一個引用而不是字面值坛吁。
自動裝配bean屬性
Spring提供了四種類型的自動裝配策略:
byName – 把與Bean的屬性具有相同名字(或者ID)的其他Bean自動裝配到Bean的對應屬性中劳殖。
byType – 把與Bean的屬性具有相同類型的其他Bean自動裝配到Bean的對應屬性中。
constructor – 把與Bean的構造器入參具有相同類型的其他Bean自動裝配到Bean的對應屬性中拨脉。
autodetect – 首先使用costructor進行自動裝配哆姻。如果失敗,再嘗試使用byType進行自動裝配玫膀。
這里以關羽和青龍偃月刀為例: 首先定義一個武器接口Weapon:
package com.moonlit.myspring;
public interface Weapon {
public void attack();
}
然后定義一個Weapon接口的實現(xiàn)Falchion類:
package com.moonlit.myspring;
public class Falchion implements Weapon {
public void attack() {
System.out.println("falcon is attacking!");
}
}
定義一個英雄接口Hero:
package com.moonlit.myspring;
public interface Hero {
public void perform();
}
然后定義一個Hero接口的實現(xiàn)Guanyu類(代表關羽):
package com.moonlit.myspring;
public class GuanYu implements Hero {
private Weapon weapon;
public void perform() {
System.out.println("GuanYu pick up his weapon.");
weapon.attack();
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
}
在不涉及自動裝配的情況下矛缨,想要通過Spring的DI將Fachion類對象注入到Guanyu類的weapon屬性中,可以新建一個xml文件(這里取名為spring-idol.xml)并在里面填寫:
spring-idol.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu">
<property name="weapon" ref="falchion" />
</bean>
</beans>
其中最主要的內容就是兩個bean的聲明部分:
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu">
<property name="weapon" ref="falchion" />
</bean>
第一個bean標簽定義了一個Falchion類型的bean帖旨,第二個bean標簽中將第一個bean作為weapon的值裝配到了weapon屬性中箕昭。 然后可以寫一個測試程序來查看效果:
package com.moonlit.practice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.moonlit.myspring.Hero;
public class AutowirePractice {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-idol.xml");
Hero guanyu = (Hero) context.getBean("guanyu");
guanyu.perform();
}
}
輸出結果如下:
GuanYu pick up his weapon.
falcon is attacking!
到目前為止還沒有涉及到自動裝配的內容,接下來開始講述自動裝配的內容解阅。
byName自動裝配
改變spring-idol.xml中bean聲明內容的形式如下:
<bean id="weapon" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byName" />
得到一樣的結果落竹。
將Falchion類的id去了一個和Guanyu類的屬性weapon一樣的名字,并且在guanyu bean中添加了autowire="byName"用于指明裝配類型是byName自動裝配货抄。這個時候guanyu bean就是在上下文中找名為weapon的bean裝配到他自己的weapon屬性中述召。
byType自動裝配
改變spring-idol.xml中bean聲明內容的形式如下:
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />
得到一樣的結果。
這里已經不用關注Falchion類對應的bean的id是什么了蟹地,因為已經定義guanyu bean的autowire屬性為"byType"积暖。這個時候guanyu bean會在上下文中尋找和weapon具有相同類型的類對應的bean。
因為Guanyu類的weapon實現(xiàn)Weapon借口怪与,整個上下文中目前只有一個Weapon接口的實現(xiàn)Falchion類夺刑,所以以"byType"類型就檢測到了falchion bean并將其注入到了guanyu bean的weapon屬性中。
但是也會出現(xiàn)一種情況就是檢測的時候可能會出現(xiàn)多個相同type的bean分别,這個時候就不知道要裝配那個了遍愿。比如,在新建一個實現(xiàn)Weapon接口的方天畫戟類HalBerd:
package com.moonlit.myspring;
public class Halberd implements Weapon {
public void attack() {
System.out.println("halberd is attacking!!!");
}
}
并且在xml文件中聲明一個新的halberd bean:
<bean id="halberd" class="com.moonlit.myspring.Halberd" />
在這種情況下就會出錯茎杂,因為有兩個bean滿足byType的結果错览。
這個時候有兩種解決辦法:
第一種方法是將其中一個bean的primary屬性設為false,比如:將方天畫戟falchion bean的primary屬性設為true煌往,以防冠以使用方天畫戟(很好奇呂布死了之后倾哺,赤兔馬歸關羽了轧邪,方天畫戟去哪里了):
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="halberd" class="com.moonlit.myspring.Halberd" primary="true" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />
輸出結果如下:
GuanYu pick up his weapon.
halberd is attacking!!!
從輸出結果中可以看到,關羽沒有使用青龍偃月刀羞海,而是使用方天畫戟進行攻擊了忌愚。
primary的默認屬性是false。
第二種方法是設置其中一個bean的autowire-candidate屬性為false却邓,比如:將方天畫戟的autowire-candidate屬性設為false:
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="halberd" class="com.moonlit.myspring.Halberd" primary="true" autowire-candidate="false" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />
這個時候測試程序的輸出如下:
GuanYu pick up his weapon.
falcon is attacking!
可以看到這個時候關羽又重拾了青龍偃月刀硕糊。可以看到腊徙,當halberd bean的autowire-candidate屬性設為false時简十,他將不會作為自動裝配的競選bean之一,這個時候雖然halberd的primary屬性為true撬腾,但是halberd bean沒有參與自動裝配的競選螟蝙,所以自動裝配到了falchion。
這種感覺就好像:“隔壁村李小花覬覦已久民傻,但是一個要成為海賊王的男人胰默,于是拒絕了她……最終她嫁給了隔壁老王,過上了幸福的生活”漓踢。
使用注解裝配bean
使用@Autowired注解
從Spring2.5開始牵署,最有趣的一種裝配Spring Bean的方式是使用注解自動裝配Bean的屬性。
Spring默認禁用注解裝配喧半,最簡單的啟用方式是使用Spring的context命名空間配置中的<context:annotation-config>元素奴迅,如下所示:
<?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">
<context:annotation-config />
<!-- bean declarations here -->
</beans>
繼續(xù)上一節(jié)的例子,在xml文件中定義兩個bean:falchion bean和guanyu bean薯酝,為了實現(xiàn)@Autowired自動裝配半沽,在GuanYu類中的setWeapon()方法前添加了@Autowired注解,如下:
GuanYu.java:
package com.moonlit.myspring;
import org.springframework.beans.factory.annotation.Autowired;
public class GuanYu implements Hero {
@Autowired
private Weapon weapon;
public void perform() {
System.out.println("Guan Yu pick up his weapon.");
weapon.attack();
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
}
通過基于注解的方式吴菠,可以不用在xml文件中為guanyu bean添加autowire屬性了。
spring-idol內部的代碼:
<context:annotation-config />
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu" />
@Autowired注解存在一個限制:
匹配多個Bean
限定歧義性的依賴
有可能存在多個bean滿足裝配條件浩村,比如做葵,這里,falchion bean和halberd bean都滿足裝配到guanyu bean的weapon屬性中的條件心墅。此時如果只是用@Autowired注解的話就會出問題酿矢,才@Autowired注解下添加@Qualifier注解如下:
@Autowired
@Qualifier("falchion")
private Weapon weapon;
就會將falchion bean裝入到weapon中。
如上所示怎燥,@Qualifier注解將嘗試注入ID為falchion的Bean瘫筐。
除了通過Bean的ID來限定,也可以給Bean添加一個qualifier屬性铐姚,通過這個qualifier屬性來獲得限定策肝,如:
給halberd bean添加一個qualifier肛捍,值為"weaponOfGuanYu":
<bean id="halberd" class="com.moonlit.myspring.Halberd">
<qualifier value="weaponOfGuanYu" />
</bean>
然后對GuanYu類weapon類的注解如下:
@Autowired
@Qualifier("weaponOfGuanYu")
private Weapon weapon;
輸出如下:
Guan Yu pick up his weapon.
halberd is attacking!!!
可以看出,@qualifier降低了@Autowired的匹配范圍之众,最終篩選得到了halberd bean裝入weapon屬性拙毫。
這里的<qualifier>元素限定了方天畫戟(halberd)Bean是關羽使用的武器(weaponOgGuanYu)。除了可以在XML中指定qualifier棺禾,還可以使用Qualifier類來標注Halberd類:
package com.moonlit.myspring;
import org.springframework.beans.factory.annotation.Qualifier;
@Qualifier("weaponOfGuanYu")
public class Halberd implements Weapon {
public void attack() {
System.out.println("halberd is attacking!!!");
}
}
程序運行將得到相同的結果缀蹄。
即使<context:annotation-config>有助于完全消除Spring配置文件中的元素,但是還是不能完全消除膘婶,仍然需要使用<bean>元素顯示定義Bean缺前。因此<context:component-scan>元素出現(xiàn)了,它除了完成<context:annotation-config>一樣的工作悬襟,還允許Spring自動檢測Bean和定義Bean衅码。這就意味著不使用<bean>元素,Spring應用中的大多數(shù)(或者所有)Bean都能夠自動實現(xiàn)定義和裝配古胆。
自動檢測
為了配置Spring自動檢測肆良,需要使用<context:component-scan>元素來代替<context:annotation-config>元素:
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.moonlit.myspring">
</context:component-scan>
</beans>
<context:component-scan>元素會掃描指定的包以及所有子包,并查找出能夠自動注冊為Spring Bean的類逸绎。base-package屬性標示了<context:component-scan>元素所掃描的包惹恃。
為自動檢測標注Bean
默認情況下,<context:component-scan>查找使用構造型(stereotype)注解所標注的類棺牧,這些特殊的注解如下:
類型 說明
@component 通用的構造型注解巫糙,標示該類為Spring 組件。
@Controller 標識將該類定義為Spring MVC controller颊乘。
@Repository 標識將該類定義為數(shù)據倉庫(例如:Dao層)参淹。
@Service 標識將該類定義為服務(例如:Service層)。
效果相同,都是告訴Spring框架在啟動時就初始化對象乏悄,但是語義不同
@component("guanyu")
ApplicationContext applicationContext
= new ClassPathXmlApplicationContext("spring.xml");
Hero hero = (Hero)applicationContext.getBean("guanyu");
hero.perform();