代理模式提供了對(duì)目標(biāo)對(duì)象的另外的訪問方式旭咽,相較于直接訪問對(duì)象,通過代理訪問對(duì)象可以在目標(biāo)對(duì)象的功能基礎(chǔ)上對(duì)目標(biāo)對(duì)象的功能經(jīng)行擴(kuò)展废登。
靜態(tài)代理
靜態(tài)代理的目標(biāo)對(duì)象和代理對(duì)象都實(shí)現(xiàn)一個(gè)共同的接口或者繼承一個(gè)共同的父類革娄,這樣就可以通過調(diào)用代理對(duì)象的相同方法來調(diào)用目標(biāo)對(duì)象翘地。
舉個(gè)栗子:
UserDao.java
public interface UserDao {
public void save();
}
UserDaoImpl.java
public class UserDaoImpl implements UserDao{
@Override
public void save() {
System.out.println("save...");
}
}
UserDaoProxy.java
public class UserDaoProxy implements UserDao{
private UserDao user;
public UserDaoProxy(UserDao user) {
this.user = user;
}
@Override
public void save() {
System.out.println("preparing for save...");
user.save();
System.out.println("save complete!");
}
}
ProxyTest.java
public class ProxyTest {
public static void main(String[] args) {
UserDao user = new UserDaoImpl();
UserDaoProxy proxy = new UserDaoProxy(user);
proxy.save();
}
}
輸出結(jié)果:
preparing for save...
save...
save complete!
靜態(tài)代理雖然能實(shí)現(xiàn)在不修改目標(biāo)對(duì)象的功能的前提下進(jìn)行擴(kuò)展,但是滚粟,試想一下寻仗,如果目標(biāo)對(duì)象需要增加一個(gè)方法的話,要怎么修改呢凡壤?答案是要修改目標(biāo)類和所有目標(biāo)類的代理類(當(dāng)然還有它們實(shí)現(xiàn)的接口)署尤!這樣維護(hù)的代價(jià)是很大的。動(dòng)態(tài)代理就可以解決這個(gè)問題亚侠。
動(dòng)態(tài)代理
動(dòng)態(tài)代理不需要實(shí)現(xiàn)接口曹体,只需要接口,我們利用jdk中的api可以動(dòng)態(tài)地在內(nèi)存中構(gòu)建代理的對(duì)象硝烂,java的動(dòng)態(tài)代理也叫jdk代理或者接口代理箕别。
我們可以直接利用上邊的UserDao接口對(duì)動(dòng)態(tài)代理進(jìn)行測(cè)試,舉個(gè)栗子:
ProxyFactory.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("preparing for save... ...");
Object returnValue = method.invoke(target, args);
System.out.println("save complete!!");
return returnValue;
}
});
}
}
DynamicProxyTest.java
public class DynamicProxyTest {
public static void main(String[] args) {
UserDao user = new UserDaoImpl();
UserDao proxy = (UserDao) new ProxyFactory(user).getProxyInstance();
proxy.save();
}
}
輸出結(jié)果:
preparing for save... ...
save...
save complete!!
這里就有很多要說明的地方了首先Proxy.newProxyInstance這個(gè)方法滞谢,返回的是一個(gè)經(jīng)過擴(kuò)展了的代理類串稀,這個(gè)方法需要三個(gè)參數(shù):
- ClassLoader loader:這個(gè)參數(shù)是要目標(biāo)類的類加載器,任何類都可以通過.getClass().getClassLoader()獲得它自己的類加載器(其實(shí)通過我們?cè)趧?dòng)態(tài)工廠中設(shè)定的目標(biāo)類為Object就可得知)
- Class<?>[] interfaces:一樣狮杨,任何類都可以通過.getClass().getInterfaces()來獲得它實(shí)現(xiàn)的所有接口
- InvocationHandler h:這個(gè)接口只定義了一個(gè)方法:invoke母截,這個(gè)是用來處理代理實(shí)例上的方法,我們直接緊跟著在這個(gè)參數(shù)后邊實(shí)現(xiàn)了它橄教。
jdk1.8的文檔上對(duì)InvocationHandler接口的描述是這樣的:
每個(gè)代理實(shí)例都有一個(gè)關(guān)聯(lián)的調(diào)用處理程序清寇。當(dāng)在代理實(shí)例上調(diào)用方法時(shí),方法條用將被編碼并分配到其調(diào)用處理程序的invoke方法颤陶。
也就是說颗管,當(dāng)我們執(zhí)行代理實(shí)例proxy的save方法時(shí),我們實(shí)際上是執(zhí)行了我們重寫的invoke方法滓走。InvocationHandler的invoke方法和Method的invoke方法要進(jìn)行區(qū)分:在我們重寫的invoke方法里垦江,method.invoke 執(zhí)行的是target關(guān)聯(lián)的實(shí)例的對(duì)應(yīng)方法,args表示方法所需要的參數(shù)搅方。
這樣比吭,我們通過ProxyFactory.getProxyInstance()方法,就得到了一個(gè)擴(kuò)展過的目標(biāo)類的代理類姨涡。
靜態(tài)代理和動(dòng)態(tài)代理都需要目標(biāo)類實(shí)現(xiàn)一個(gè)接口衩藤,但如果我們的目標(biāo)類是一個(gè)單獨(dú)的沒有實(shí)現(xiàn)任何接口的對(duì)象怎么辦?這時(shí)候我們就可以使用Cglib代理涛漂。
Cglib代理
Cglib代理通過創(chuàng)建目標(biāo)對(duì)象的子類來實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的代理赏表,所以也叫做子類代理检诗。
因?yàn)閟pring中包括了cglib的功能,所以我們可以通過引入spring的spring-core-4.3.6.RELEASE.jar包來使用cglib瓢剿。
上栗子:
CustomerDao.java
public class CustomerDao {
public void save() {
System.out.println("save...");
}
}
CglibFactory.java
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibFactory implements MethodInterceptor {
private Object target;
public CglibFactory(Object target) {
this.target = target;
}
// 代理方法
public Object createProxy() {
// 創(chuàng)建一個(gè)動(dòng)態(tài)對(duì)象
Enhancer hancer = new Enhancer();
// 確定要增強(qiáng)的類(目標(biāo)對(duì)象)逢慌,設(shè)置其為父類
hancer.setSuperclass(target.getClass());
// 添加回調(diào)函數(shù)(?)
hancer.setCallback(this);
// 返回創(chuàng)建的代理類
return hancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("prepering for save... ... ...");
Object returnValue = method.invoke(target, args);
System.out.println("saving completed!!!");
return returnValue;
}
}
CglibTest.java
public class CglibTest {
public static void main(String[] args) {
CustomerDao customer = new CustomerDao();
CustomerDao proxy = (CustomerDao) new CglibFactory(customer).createProxy();
proxy.save();
}
}
輸出結(jié)果:
prepering for save... ... ...
save...
saving completed!!!
這里的intercept方法類似于動(dòng)態(tài)代理的invoke方法间狂,就不多加敘述了攻泼。