如何學(xué)習(xí)Spring框架
了解Spring的出現(xiàn)是為了解決什么問(wèn)題!K恕脾歇!
了解Spring的出現(xiàn)是為了解決什么問(wèn)題!L约瘛藕各!
了解Spring的出現(xiàn)是為了解決什么問(wèn)題!=钩座韵!
學(xué)習(xí)Spring的兩個(gè)核心概念以及其實(shí)現(xiàn)方式(IoC和AOP)
Spring 控制反轉(zhuǎn) (IoC)
Spring(IOC:控制反轉(zhuǎn)):從前需要我們自己做的事,現(xiàn)在要交給方法的調(diào)用者來(lái)處理(Sprig的IOC容器來(lái)處理)
如何實(shí)現(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() -->
<!--依賴注入 /控制反轉(zhuǎn)IOC -->
<property name="userDAO" bean="u"></property>
</bean>
</beans>
解析XML的方式有幾種:
XML的解析方式分為四種:1踢京、DOM解析誉碴;2、SAX解析瓣距;3黔帕、JDOM解析;4蹈丸、DOM4J解析成黄。其中前兩種屬于基礎(chǔ)方法呐芥,是官方提供的平臺(tái)無(wú)關(guān)的解析方式;后兩種屬于擴(kuò)展方法奋岁,它們是在基礎(chǔ)的方法上擴(kuò)展出來(lái)的思瘟,只適用于java平臺(tái)。
我們可以使用JDOM來(lái)解決以上問(wèn)題
在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 {
/**
* 根據(jù)key獲取到value
* @param id(key)
* @return Object
*/
public Object getBean(String id);
}
創(chuàng)建實(shí)現(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)建一個(gè)工廠容器
private Map<String, Object> beans = new HashMap<>();
//使用構(gòu)造器來(lái)解析beans.xml文件
public ClassPathXmlApplicationContext() throws Exception{
//1.創(chuàng)建一個(gè)解析器
SAXBuilder saxBuilder = new SAXBuilder();
//2.解析并讀取beans.xml文件 構(gòu)造Document文檔對(duì)象
Document document = saxBuilder.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml"));
//3.獲取到根節(jié)點(diǎn)
Element element = document.getRootElement();
//4.獲取到子節(jié)點(diǎn)
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轉(zhuǎn)換為UserDAOImpl對(duì)象并將id以及對(duì)象保存到Map集合中
Object o = Class.forName(clazz).newInstance();
beans.put(id, o);
//獲取到bean中的子節(jié)點(diǎn)property
for(Element propertyElement:(List<Element>)e.getChildren("property")){
String name = propertyElement.getAttributeValue("name");//userDAO
String bean = propertyElement.getAttributeValue("bean");//u
//從Map中獲取到UserDAOImpl對(duì)象
Object beanObject = beans.get(bean);
//模擬調(diào)用public void setUserDAO(UserDAO userDAO)
String methodName = "set"+ name.substring(0, 1).toUpperCase()+name.substring(1);
System.out.println(methodName);
//需要java中的反射機(jī)制來(lái)自動(dòng)調(diào)用setUserDAO方法
//o.getClass().getMethod獲取到UserServiceImpl對(duì)象中的某一個(gè)方法setUserDAO
//beanObject.getClass().getInterfaces()[0]獲取到方法的參數(shù)UserDAO
Method method = o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);
//方法的調(diào)用 o:UserServiceImpl對(duì)象中的setUserDAO(UserDAO userDAO)方法
//beanObject:調(diào)用此方法時(shí)需要傳入的參數(shù) UserDAOImpl對(duì)象
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對(duì)象
// userService.setUserDAO(userDAO);
// userService.addInfo(new User());
BeanFactory beanFactory = new ClassPathXmlApplicationContext();
//從工廠容器中獲取到UserService對(duì)象
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闻伶,即“控制反轉(zhuǎn)”滨攻,不是什么技術(shù),而是一種設(shè)計(jì)思想蓝翰。在Java開(kāi)發(fā)中光绕,IOC意味著將你設(shè)計(jì)好的對(duì)象交給容器控制,而不是傳統(tǒng)的在你的對(duì)象內(nèi)部直接控制畜份。如何理解好IOC呢诞帐?理解好IOC的關(guān)鍵是要明確“誰(shuí)控制誰(shuí),控制什么爆雹,為何是反轉(zhuǎn)(有反轉(zhuǎn)就應(yīng)該有正轉(zhuǎn)了)停蕉,哪些方面反轉(zhuǎn)了”,那我們來(lái)深入分析一下:
●誰(shuí)控制誰(shuí)钙态,控制什么:傳統(tǒng)Java SE程序設(shè)計(jì)慧起,我們直接在對(duì)象內(nèi)部通過(guò)new進(jìn)行創(chuàng)建對(duì)象,是程序主動(dòng)去創(chuàng)建依賴對(duì)象驯绎;而IOC是有專門一個(gè)容器來(lái)創(chuàng)建這些對(duì)象完慧,即由IOC容器來(lái)控制對(duì) 象的創(chuàng)建;誰(shuí)控制誰(shuí)剩失?當(dāng)然是IOC容器控制了對(duì)象屈尼;控制什么?那就是主要控制了外部資源獲取拴孤。
●為何是反轉(zhuǎn)脾歧,哪些方面反轉(zhuǎn)了:有反轉(zhuǎn)就有正轉(zhuǎn),傳統(tǒng)應(yīng)用程序是由我們自己在對(duì)象中主動(dòng)控制去直接獲取依賴對(duì)象演熟,也就是正轉(zhuǎn)鞭执;而反轉(zhuǎn)則是由容器來(lái)幫忙創(chuàng)建及注入依賴對(duì)象;為何是反轉(zhuǎn)芒粹?因?yàn)橛扇萜鲙臀覀儾檎壹白⑷胍蕾噷?duì)象兄纺,對(duì)象只是被動(dòng)的接受依賴對(duì)象,所以是反轉(zhuǎn)化漆;哪些方面反轉(zhuǎn)了估脆?依賴對(duì)象的獲取被反轉(zhuǎn)了。
IOC能做什么
IOC 不是一種技術(shù)座云,只是一種思想疙赠,一個(gè)重要的面向?qū)ο缶幊痰姆▌t付材,它能指導(dǎo)我們?nèi)绾卧O(shè)計(jì)出松耦合、更優(yōu)良的程序圃阳。傳統(tǒng)應(yīng)用程序都是由我們?cè)陬悆?nèi)部主動(dòng)創(chuàng)建依賴對(duì)象厌衔,從而導(dǎo)致類與類之間高耦合,難于測(cè)試捍岳;有了IOC容器后富寿,把創(chuàng)建和查找依賴對(duì)象的控制權(quán)交給了容器,由容器進(jìn)行注入組合對(duì)象祟同,所以對(duì)象與對(duì)象之間是 松散耦合作喘,這樣也方便測(cè)試理疙,利于功能復(fù)用晕城,更重要的是使得程序的整個(gè)體系結(jié)構(gòu)變得非常靈活。
其實(shí)IOC對(duì)編程帶來(lái)的最大改變不是從代碼上窖贤,而是從思想上砖顷,發(fā)生了“主從換位”的變化。應(yīng)用程序原本是老大赃梧,要獲取什么資源都是主動(dòng)出擊滤蝠,但是在IOC/DI思想中,應(yīng)用程序就變成被動(dòng)的了授嘀,被動(dòng)的等待IOC容器來(lái)創(chuàng)建并注入它所需要的資源了物咳。
IOC很好的體現(xiàn)了面向?qū)ο笤O(shè)計(jì)法則之一—— 好萊塢法則:“別找我們,我們找你”蹄皱;即由IOC容器幫對(duì)象找相應(yīng)的依賴對(duì)象并注入览闰,而不是由對(duì)象主動(dòng)去找。
IOC和DI的關(guān)系
IOC和DI由什么關(guān)系呢巷折?其實(shí)它們是同一個(gè)概念的不同角度描述压鉴,由于控制反轉(zhuǎn)概念比較含糊(可能只是理解為容器控制對(duì)象這一個(gè)層面,很難讓人想到誰(shuí)來(lái)維護(hù)對(duì)象關(guān)系)锻拘,所以2004年大師級(jí)人物Martin Fowler又給出了一個(gè)新的名字:“依賴注入”油吭,相對(duì)IoC 而言,“依賴注入”明確描述了“被注入對(duì)象依賴IoC容器配置依賴對(duì)象”署拟。
DI—Dependency Injection婉宰,即“依賴注入”:組件之間依賴關(guān)系由容器在運(yùn)行期決定,形象的說(shuō)推穷,即由容器動(dòng)態(tài)的將某個(gè)依賴關(guān)系注入到組件之中心包。依賴注入的目的并非為軟件系統(tǒng)帶來(lái)更多功能,而是為了提升組件重用的頻率缨恒,并為系統(tǒng)搭建一個(gè)靈活谴咸、可擴(kuò)展的平臺(tái)轮听。通過(guò)依賴注入機(jī)制,我們只需要通過(guò)簡(jiǎn)單的配置岭佳,而無(wú)需任何代碼就可指定目標(biāo)需要的資源血巍,完成自身的業(yè)務(wù)邏輯,而不需要關(guān)心具體的資源來(lái)自何處珊随,由誰(shuí)實(shí)現(xiàn)述寡。
理解DI的關(guān)鍵是:“誰(shuí)依賴誰(shuí),為什么需要依賴叶洞,誰(shuí)注入誰(shuí)鲫凶,注入了什么”,那我們來(lái)深入分析一下:
●誰(shuí)依賴于誰(shuí):當(dāng)然是應(yīng)用程序依賴于IOC容器衩辟;
●為什么需要依賴:應(yīng)用程序需要IOC容器來(lái)提供對(duì)象需要的外部資源螟炫;
●誰(shuí)注入誰(shuí):很明顯是IOC容器注入應(yīng)用程序某個(gè)對(duì)象,應(yīng)用程序依賴的對(duì)象艺晴;
●注入了什么:就是注入某個(gè)對(duì)象所需要的外部資源(包括對(duì)象昼钻、資源、常量數(shù)據(jù))封寞。
Spring配制
聲明一個(gè)簡(jiǎn)單的bean
第一個(gè)例子:
首先設(shè)置一個(gè)接口Perofrmance表示參賽者然评。
package com.moonlit.myspring;
public interface Performer {
void perform() throws PerformanceException;
}
創(chuàng)建一個(gè)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配置文件中定義一個(gè)名為duke的bean狈究,他對(duì)應(yīng)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>
測(cè)試代碼:
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();
}
}
運(yùn)行結(jié)果如下:
JUGGLING 3 BEANBAGS
理解:首先定義了一個(gè)接口Performer,然后寫(xiě)了一個(gè)類Juggler繼承自Peformer,Juggler有一個(gè)私有成員變量beanBags,他的默認(rèn)值是3抖锥,然后Juggler實(shí)現(xiàn)了Performer的perform方法亿眠,方法的輸出帶有beanBags的數(shù)量。
然后在測(cè)試的程序中宁改,通過(guò)ApplicationContext類型對(duì)象加載了spring-idol.xml文件的內(nèi)容缕探,而在xml文件中定義了名為"duke"的bean,然后剛好就用到了还蹲。
然后bean返回的是一個(gè)Juggler爹耗,所以將
Performer performer = (Performer) context.getBean("duke");
改成
Juggler performer = (Juggler) context.getBean("duke");
也是可以的,但是在這里想看的效果是通過(guò)application context返回的是不是一個(gè)Juggler谜喊,因?yàn)橥ㄟ^(guò)輸出的結(jié)果就可以知道了潭兽,所以這里用(Performer),對(duì)中輸出的效果顯示bean對(duì)應(yīng)的Performer真的是一個(gè)Juggler斗遏,這就是通過(guò)xml定義一個(gè)bean并通過(guò)application context獲得這個(gè)bean對(duì)象的整個(gè)過(guò)程山卦。
構(gòu)造器注入
之前講到的名為"duke"的bean有一個(gè)私有成員變量beanBags代表這個(gè)雜技師bean的一次性能夠拋出的最多的數(shù)量,Juggler有一個(gè)構(gòu)造函數(shù)诵次,構(gòu)造函數(shù)的第一個(gè)參數(shù)(這里只有一個(gè)參數(shù))beanBags是一個(gè)整型的值账蓉,用于傳遞給Juggler的私有成員變量beanBags枚碗。
構(gòu)造器注入的方法是:在bean中添加一個(gè)constructor-arg(如果構(gòu)造函數(shù)的參數(shù)有兩個(gè),那就添加兩個(gè)constructor-arg)铸本。
在spring-idol.xml中修改bean "duke"如下:
<bean id="duke" class="com.moonlit.myspring.Juggler" >
<constructor-arg name="beanBags" value="15" />
</bean>
再次運(yùn)行FirstBean程序肮雨,輸出如下:
JUGGLING 15 BEANBAGS
可以看到通過(guò)構(gòu)造器諸如已經(jīng)把duke的beanBags改為了15。
構(gòu)造函數(shù)中的參數(shù)可能不是一個(gè)基礎(chǔ)類型的變量箱玷,而可能是一個(gè)變量怨规,這個(gè)時(shí)候只要把constructor-arg的value改成ref即可,ref對(duì)應(yīng)的值需要被聲明稱一個(gè)bean元素锡足。
使用一個(gè)會(huì)唱歌的雜技師PoeticJuggler類來(lái)演示波丰,PoeticJuggler繼承自Juggler,它具有一個(gè)Poem類型的私有成員變量poem舶得,代表他要朗誦的詩(shī)歌掰烟。
Poem類:
package com.moonlit.myspring;
public interface Poem
{
void recite();
}
定義一首名為Sonnet29的類用于表示名為一首sonnet29的詩(shī):http://shakespeare-online.com/sonnets/29.html
Sonnet29實(shí)現(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和他的一個(gè)實(shí)現(xiàn)類Sonnet29之后扩灯,開(kāi)始來(lái)寫(xiě)PoeticJuggler媚赖,他繼承自Juggler并且有一個(gè)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類對(duì)應(yīng)的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"使用了兩個(gè)constructor-arg來(lái)聲明參數(shù)捻撑,第一個(gè)參數(shù)使用value,第二個(gè)參數(shù)使用ref缤底,Sonnet29類型的bean--"connet29"顾患。
使用測(cè)試程序查看效果:
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.
理解:可以通過(guò)構(gòu)造器注入來(lái)模擬構(gòu)造函數(shù)傳入的參數(shù),通過(guò)constructor-arg value="XX"傳遞一個(gè)基本類型的參數(shù)XX个唧,通過(guò)constructor-arg ref="XX"傳遞一個(gè)bean江解。
注入各種bean屬性
這里通過(guò)一個(gè)MoonlightPoet類來(lái)演示了注入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>
輸出結(jié)果:
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
理解:
注入簡(jiǎn)單值:
<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>
因?yàn)閙ap的key和value可以對(duì)應(yīng)一個(gè)基礎(chǔ)類型的值灭翔,也可以對(duì)應(yīng)一個(gè)bean,所以key,value對(duì)應(yīng)值辣苏,key-ref,value-ref對(duì)應(yīng)bean肝箱。
使用Spring的命名空間p裝配屬性
可以在beans中添加
xmlns:p="http:www.springframework.org/schema/beans"
來(lái)使用p:作為<bean>元素所有屬性的前綴來(lái)裝配Bean的屬性哄褒。用法如下:
<bean id="kenny" class="XX"
p:song = "Jingle Bells"
p:instrument-ref = "saxphone" />
-ref后綴作為一個(gè)標(biāo)識(shí)來(lái)告知Spring應(yīng)該裝配一個(gè)引用而不是字面值。
自動(dòng)裝配bean屬性
Spring提供了四種類型的自動(dòng)裝配策略:
byName – 把與Bean的屬性具有相同名字(或者ID)的其他Bean自動(dòng)裝配到Bean的對(duì)應(yīng)屬性中煌张。
byType – 把與Bean的屬性具有相同類型的其他Bean自動(dòng)裝配到Bean的對(duì)應(yīng)屬性中读处。
constructor – 把與Bean的構(gòu)造器入?yún)⒕哂邢嗤愋偷钠渌鸅ean自動(dòng)裝配到Bean的對(duì)應(yīng)屬性中。
autodetect – 首先使用costructor進(jìn)行自動(dòng)裝配唱矛。如果失敗罚舱,再嘗試使用byType進(jìn)行自動(dòng)裝配。
這里以關(guān)羽和青龍偃月刀為例: 首先定義一個(gè)武器接口Weapon:
package com.moonlit.myspring;
public interface Weapon {
public void attack();
}
然后定義一個(gè)Weapon接口的實(shí)現(xiàn)Falchion類:
package com.moonlit.myspring;
public class Falchion implements Weapon {
public void attack() {
System.out.println("falcon is attacking!");
}
}
定義一個(gè)英雄接口Hero:
package com.moonlit.myspring;
public interface Hero {
public void perform();
}
然后定義一個(gè)Hero接口的實(shí)現(xiàn)Guanyu類(代表關(guān)羽):
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;
}
}
在不涉及自動(dòng)裝配的情況下绎谦,想要通過(guò)Spring的DI將Fachion類對(duì)象注入到Guanyu類的weapon屬性中管闷,可以新建一個(gè)xml文件(這里取名為spring-idol.xml)并在里面填寫(xiě):
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>
其中最主要的內(nèi)容就是兩個(gè)bean的聲明部分:
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu">
<property name="weapon" ref="falchion" />
</bean>
第一個(gè)bean標(biāo)簽定義了一個(gè)Falchion類型的bean,第二個(gè)bean標(biāo)簽中將第一個(gè)bean作為weapon的值裝配到了weapon屬性中窃肠。 然后可以寫(xiě)一個(gè)測(cè)試程序來(lái)查看效果:
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();
}
}
輸出結(jié)果如下:
GuanYu pick up his weapon.
falcon is attacking!
到目前為止還沒(méi)有涉及到自動(dòng)裝配的內(nèi)容包个,接下來(lái)開(kāi)始講述自動(dòng)裝配的內(nèi)容。
byName自動(dòng)裝配
改變spring-idol.xml中bean聲明內(nèi)容的形式如下:
<bean id="weapon" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byName" />
得到一樣的結(jié)果冤留。
將Falchion類的id去了一個(gè)和Guanyu類的屬性weapon一樣的名字碧囊,并且在guanyu bean中添加了autowire="byName"用于指明裝配類型是byName自動(dòng)裝配。這個(gè)時(shí)候guanyu bean就是在上下文中找名為weapon的bean裝配到他自己的weapon屬性中纤怒。
byType自動(dòng)裝配
改變spring-idol.xml中bean聲明內(nèi)容的形式如下:
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" /
得到一樣的結(jié)果糯而。
這里已經(jīng)不用關(guān)注Falchion類對(duì)應(yīng)的bean的id是什么了,因?yàn)橐呀?jīng)定義guanyu bean的autowire屬性為"byType"泊窘。這個(gè)時(shí)候guanyu bean會(huì)在上下文中尋找和weapon具有相同類型的類對(duì)應(yīng)的bean熄驼。
因?yàn)镚uanyu類的weapon實(shí)現(xiàn)Weapon借口,整個(gè)上下文中目前只有一個(gè)Weapon接口的實(shí)現(xiàn)Falchion類烘豹,所以以"byType"類型就檢測(cè)到了falchion bean并將其注入到了guanyu bean的weapon屬性中瓜贾。
但是也會(huì)出現(xiàn)一種情況就是檢測(cè)的時(shí)候可能會(huì)出現(xiàn)多個(gè)相同type的bean,這個(gè)時(shí)候就不知道要裝配那個(gè)了携悯。比如祭芦,在新建一個(gè)實(shí)現(xiàn)Weapon接口的方天畫(huà)戟類HalBerd:
package com.moonlit.myspring;
public class Halberd implements Weapon {
public void attack() {
System.out.println("halberd is attacking!!!");
}
}
并且在xml文件中聲明一個(gè)新的halberd bean:
<bean id="halberd" class="com.moonlit.myspring.Halberd" />
在這種情況下就會(huì)出錯(cuò),因?yàn)橛袃蓚€(gè)bean滿足byType的結(jié)果。
這個(gè)時(shí)候有兩種解決辦法:
第一種方法是將其中一個(gè)bean的primary屬性設(shè)為false,比如:將方天畫(huà)戟falchion bean的primary屬性設(shè)為true棺克,以防冠以使用方天畫(huà)戟(很好奇呂布死了之后盯漂,赤兔馬歸關(guān)羽了,方天畫(huà)戟去哪里了):
<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" />
輸出結(jié)果如下:
GuanYu pick up his weapon.
halberd is attacking!!!
從輸出結(jié)果中可以看到,關(guān)羽沒(méi)有使用青龍偃月刀,而是使用方天畫(huà)戟進(jìn)行攻擊了。
primary的默認(rèn)屬性是false避矢。
第二種方法是設(shè)置其中一個(gè)bean的autowire-candidate屬性為false,比如:將方天畫(huà)戟的autowire-candidate屬性設(shè)為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" />
這個(gè)時(shí)候測(cè)試程序的輸出如下:
GuanYu pick up his weapon.
falcon is attacking!
可以看到這個(gè)時(shí)候關(guān)羽又重拾了青龍偃月刀∩笮兀可以看到亥宿,當(dāng)halberd bean的autowire-candidate屬性設(shè)為false時(shí),他將不會(huì)作為自動(dòng)裝配的競(jìng)選bean之一砂沛,這個(gè)時(shí)候雖然halberd的primary屬性為true烫扼,但是halberd bean沒(méi)有參與自動(dòng)裝配的競(jìng)選,所以自動(dòng)裝配到了falchion碍庵。
這種感覺(jué)就好像:“隔壁村李小花覬覦已久映企,但是一個(gè)要成為海賊王的男人,于是拒絕了她……最終她嫁給了隔壁老王静浴,過(guò)上了幸福的生活”堰氓。
使用注解裝配bean
使用@Autowired注解
從Spring2.5開(kāi)始,最有趣的一種裝配Spring Bean的方式是使用注解自動(dòng)裝配Bean的屬性苹享。
Spring默認(rèn)禁用注解裝配双絮,最簡(jiǎn)單的啟用方式是使用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文件中定義兩個(gè)bean:falchion bean和guanyu bean囤攀,為了實(shí)現(xiàn)@Autowired自動(dòng)裝配,在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;
}
}
通過(guò)基于注解的方式焚挠,可以不用在xml文件中為guanyu bean添加autowire屬性了。
spring-idol內(nèi)部的代碼:
<context:annotation-config />
<bean id="falchion" class="com.moonlit.myspring.Falchion" />
<bean id="guanyu" class="com.moonlit.myspring.GuanYu" />
@Autowired注解存在一個(gè)限制:
匹配多個(gè)Bean
限定歧義性的依賴
有可能存在多個(gè)bean滿足裝配條件哪怔,比如宣蔚,這里,falchion bean和halberd bean都滿足裝配到guanyu bean的weapon屬性中的條件认境。此時(shí)如果只是用@Autowired注解的話就會(huì)出問(wèn)題,才@Autowired注解下添加@Qualifier注解如下:
@Autowired
@Qualifier("falchion")
private Weapon weapon;
就會(huì)將falchion bean裝入到weapon中挟鸠。
如上所示叉信,@Qualifier注解將嘗試注入ID為falchion的Bean。
除了通過(guò)Bean的ID來(lái)限定艘希,也可以給Bean添加一個(gè)qualifier屬性硼身,通過(guò)這個(gè)qualifier屬性來(lái)獲得限定,如:
給halberd bean添加一個(gè)qualifier覆享,值為"weaponOfGuanYu":
<bean id="halberd" class="com.moonlit.myspring.Halberd">
<qualifier value="weaponOfGuanYu" />
</bean>
然后對(duì)GuanYu類weapon類的注解如下:
@Autowired
@Qualifier("weaponOfGuanYu")
private Weapon weapon;
輸出如下:
Guan Yu pick up his weapon.
halberd is attacking!!!
可以看出佳遂,@qualifier降低了@Autowired的匹配范圍,最終篩選得到了halberd bean裝入weapon屬性撒顿。
這里的<qualifier>元素限定了方天畫(huà)戟(halberd)Bean是關(guān)羽使用的武器(weaponOgGuanYu)丑罪。除了可以在XML中指定qualifier,還可以使用Qualifier類來(lái)標(biāo)注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!!!");
}
}
程序運(yùn)行將得到相同的結(jié)果。
即使<context:annotation-config>有助于完全消除Spring配置文件中的元素吩屹,但是還是不能完全消除跪另,仍然需要使用<bean>元素顯示定義Bean。因此<context:component-scan>元素出現(xiàn)了煤搜,它除了完成<context:annotation-config>一樣的工作免绿,還允許Spring自動(dòng)檢測(cè)Bean和定義Bean。這就意味著不使用<bean>元素擦盾,Spring應(yīng)用中的大多數(shù)(或者所有)Bean都能夠自動(dòng)實(shí)現(xiàn)定義和裝配嘲驾。
自動(dòng)檢測(cè)
為了配置Spring自動(dòng)檢測(cè),需要使用<context:component-scan>元素來(lái)代替<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>元素會(huì)掃描指定的包以及所有子包迹卢,并查找出能夠自動(dòng)注冊(cè)為Spring Bean的類距淫。base-package屬性標(biāo)示了<context:component-scan>元素所掃描的包。
為自動(dòng)檢測(cè)標(biāo)注Bean
默認(rèn)情況下婶希,<context:component-scan>查找使用構(gòu)造型(stereotype)注解所標(biāo)注的類榕暇,這些特殊的注解如下:
類型 說(shuō)明
@component 通用的構(gòu)造型注解,標(biāo)示該類為Spring 組件喻杈。
@Controller 標(biāo)識(shí)將該類定義為Spring MVC controller彤枢。
@Repository 標(biāo)識(shí)將該類定義為數(shù)據(jù)倉(cāng)庫(kù)(例如:Dao層)。
@Service 標(biāo)識(shí)將該類定義為服務(wù)(例如:Service層)筒饰。
效果相同,都是告訴Spring框架在啟動(dòng)時(shí)就初始化對(duì)象缴啡,但是語(yǔ)義不同
@component("guanyu")
ApplicationContext applicationContext
= new ClassPathXmlApplicationContext("spring.xml");
Hero hero = (Hero)applicationContext.getBean("guanyu");
hero.perform();
springAOP
首先創(chuàng)建一個(gè)狗類的接口
package com.foreknow.proxy;
/**
* 創(chuàng)建一個(gè)狗類的接口
* @author Administrator
*
*/
public interface Dog {
public void info();
public void run();
}
狗類的實(shí)現(xiàn)類
package com.foreknow.proxy;
import javax.sound.midi.MidiDevice.Info;
public class GouhuiImpl implements Dog {
@Override
public void info() {
System.out.println("Info......");
}
@Override
public void run() {
System.out.println("run......");
}
}
定義一個(gè)攔截器
package com.foreknow.proxy;
/**
* 攔截器
* 攔截某一個(gè)方法后要在之前或之后的操作方法
* @author Administrator
*
*/
public class DogInterceptor {
public void method1() {
System.out.println("mothod1");
}
public void method2() {
System.out.println("method2");
}
}
創(chuàng)建一個(gè)工廠類
package com.foreknow.proxy;
import java.lang.reflect.Proxy;
/**
* 創(chuàng)建一個(gè)工廠類
* Proxy它是在程序運(yùn)行時(shí)生成的類。當(dāng)然Proxy不會(huì)代替你去做實(shí)際的工作瓷们,是由動(dòng)態(tài)代理類ProxyHandler完成實(shí)際的工作
* @author Administrator
*
*/
public class MyProxyFactory {
public static Object getProxy(Object obj) {
ProxyHandler h = new ProxyHandler();
//設(shè)置目標(biāo)對(duì)象
h.setTarget(obj);
//loader - 定義代理類的類加載器
//interfaces - 代理類要實(shí)現(xiàn)的接口列表
//h - 指派方法調(diào)用的調(diào)用處理程序
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), h);
}
}