為什么要有方法注入:
假設(shè)有一個(gè)Bean 依賴(lài)于另一個(gè)Bean,兩個(gè)的生命周期卻不一樣的時(shí)候,光靠配置和注解不能很好地解決。
如:一個(gè)單例的Bean A和一個(gè)非單例的bean B, A 依賴(lài) B,每次容器只會(huì)初始化一次 A,B卻每次都需要重新創(chuàng)建,當(dāng) B成為A的屬性時(shí),A內(nèi)的B就無(wú)法每次重新創(chuàng)建,這樣要么放棄控制反轉(zhuǎn),要么得想新辦法解決這個(gè)問(wèn)題。
所以,Spring 通過(guò)方法注入,來(lái)實(shí)現(xiàn)動(dòng)態(tài)改變A內(nèi)的B,注入利用了容器的覆蓋受容器管理的bean方法的能力,從而返回指定名字的bean實(shí)例。
介紹兩個(gè)方法注入的方式,分別是查找注入,和方法替換
查找注入
Spring的方法注入依賴(lài)于CGLIB,需要添加Jar包com.springsource.cn.sf.cglib-2.2.0.jar,Spring通過(guò)CGLib動(dòng)態(tài)修改字節(jié)碼,所以可以用配置方式,使用代理覆蓋或攔截指定的方法動(dòng)態(tài)生成子類(lèi),可分為查找方法注入和替換方法注入。
用于注入方法返回結(jié)果,也就是說(shuō)能通過(guò)配置方式替換方法返回結(jié)果。
CGLib,一個(gè)操作字節(jié)碼的庫(kù),玩的溜的話(huà)是不是可以寫(xiě)動(dòng)態(tài)病毒?
查找注入的方法必須符合下面的規(guī)則
- 必須有返回值
- 可以是抽象的,但必須是public或protected修飾可以被子類(lèi)訪問(wèn)的
- 必須是無(wú)參的
- 不能是final修飾的
適用情況
Bean是有生命周期的,方法注入是為了使方法獨(dú)立于Bean。
比如一個(gè)單例(singleton)的Bean,依賴(lài)了一個(gè)非單例的(prototype)普通Bean,當(dāng)單例模式的Bean被銷(xiāo)毀時(shí),會(huì)導(dǎo)致這個(gè)普通Bean也會(huì)銷(xiāo)毀,為了解決,可以使不被銷(xiāo)毀,使用方法注入。
看個(gè)例子
一個(gè)bean,非單例模式的
package com.zing.method_injection;
/**
* Created by zing on 16/5/18.
*/
public class BuityPeople {
private static int ID = 0;
private String name;
private String cast;
public BuityPeople() {
ID++;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name + ID;
}
public String getCast() {
return cast;
}
public void setCast(String cast) {
this.cast = cast + ID;
}
@Override
public String toString() {
return "BuityPeople{" +
"name='" + name + '\'' +
", cast='" + cast + '\'' +
'}';
}
}
另一個(gè)bean,單例模式的
package com.zing.method_injection;
/**
* Created by zing on 16/5/18.
*/
public interface Movie {
public BuityPeople getActor();
}
現(xiàn)在是Movie依賴(lài)BuityPeople,
寫(xiě)好配置,這里p 需要加頭
<bean id="buityPeople" class="com.zing.method_injection.BuityPeople"
p:name="群演" p:cast="路人" scope="prototype"></bean>
<bean id="movie" class="com.zing.method_injection.Movie" scope="singleton">
<lookup-method name="getActor" bean="buityPeople"></lookup-method>
</bean>
這樣方法注入的例子就配置好了,我們寫(xiě)一段代碼,測(cè)試一下
public class TestLookupMethod {
@Test
public void getMovieStar() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Movie movie = context.getBean("movie", Movie.class);
BuityPeople p1 = movie.getActor();
BuityPeople p2 = movie.getActor();
System.out.println(p1.toString());
System.out.println(p2.toString());
}
}

看出來(lái)演員是兩個(gè)不同的人,但是Movie對(duì)象一直是單例的,沒(méi)有變化。
坑:class字節(jié)碼的組成,ps:還沒(méi)看懂,提供給想了解的小伙伴
方法替換

這個(gè)是利用自己的方法去替換另一個(gè)bean的方法,適用場(chǎng)景未知,目前沒(méi)用過(guò)。
我們嘗試玩一下,用明星來(lái)演電影,把配角換成主角。還是用到了上面的BuityPeople類(lèi)
package com.zing.method_injection;
/**
* Created by zing on 16/5/18.
*/
public class MovieStar {
private BuityPeople getActor() {
BuityPeople p = new BuityPeople();
p.setName("普通明星");
p.setCast("配角");
return p;
}
}
package com.zing.method_injection;
/**
* Created by zing on 16/5/18.
*/
//一定要實(shí)現(xiàn)MethodReplacer接口
public class SuperStar implements MethodReplacer{
@Override
public Object reimplement(Object o, Method method, Object[] objects) throws Throwable {
BuityPeople p = new BuityPeople();
p.setName("超級(jí)明星");
p.setCast("主角");
return p;
}
}
添加配置
<bean id="movieStar" class="com.zing.method_injection.MovieStar">
<replaced-method name="getActor" replacer="superStarMovie"></replaced-method>
</bean>
<bean id="superStarMovie" class="com.zing.method_injection.SuperStar"></bean>
配置完成寫(xiě)一段代碼測(cè)試一下
@Test
public void goodMovieStar() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
System.out.println("測(cè)試前的演員————————————————————————");
MovieStar movieStar1 = new MovieStar();
System.out.println(movieStar1.getActor().toString());
System.out.println("測(cè)試后的演員————————————————————————");
MovieStar movieStar2 = context.getBean("movieStar", MovieStar.class);
System.out.println(movieStar2.getActor().toString());
}

大家看到了,方法被替換了,演員也換了,角色也換了。侵入性很強(qiáng)!所以一般不使用方法替換,只是使用第一種,查找注入。