我們根據(jù)加載被代理類的時(shí)機(jī)不同,將代理分為靜態(tài)代理和動(dòng)態(tài)代理蜘腌。如果我們在代碼編譯時(shí)就確定了被代理的類是哪一個(gè)饵隙,那么就可以直接使用靜態(tài)代理金矛;如果不能確定,那么可以使用類的動(dòng)態(tài)加載機(jī)制娶耍,在代碼運(yùn)行期間加載被代理的類這就是動(dòng)態(tài)代理伺绽,比如RPC框架和Spring AOP機(jī)制嗜湃。
本文介紹如下三種代理:
JDK靜態(tài)代理购披、JDk動(dòng)態(tài)代理春瞬、CGLIB動(dòng)態(tài)代理
一狼忱、靜態(tài)代理
public interface Person {
public void sayHello(String content, int age);
public void sayGoodBye(boolean seeAgin, double time);
}
public class Student implements Person{
@Override
public void sayHello(String content, int age) {
// TODO Auto-generated method stub
System.out.println("student say hello" + content + " "+ age);
}
@Override
public void sayGoodBye(boolean seeAgin, double time) {
// TODO Auto-generated method stub
System.out.println("student sayGoodBye " + time + " "+ seeAgin);
}
}
public class ProxyTest implements Person{
private Person o;
public ProxyTest(Person o){
this.o = o;
}
public void sayHello(String content, int age) {
// TODO 自動(dòng)生成的方法存根
System.out.println("ProxyTest sayHello begin");
//在代理類的方法中 間接訪問被代理對象的方法
o.sayHello(content, age);
System.out.println("ProxyTest sayHello end");
}
public void sayGoodBye(boolean seeAgin, double time) {
// TODO 自動(dòng)生成的方法存根
System.out.println("ProxyTest sayHello begin");
//在代理類的方法中 間接訪問被代理對象的方法
o.sayGoodBye(seeAgin, time);
System.out.println("ProxyTest sayHello end");
}
}
public class Client {
public static void main(String[] args) {
// TODO 自動(dòng)生成的方法存根
//s為被代理的對象太防,某些情況下 我們不希望修改已有的代碼氓皱,我們采用代理來間接訪問
Student s = new Student();
//創(chuàng)建代理類對象
ProxyTest proxy = new ProxyTest(s);
//調(diào)用代理類對象的方法
proxy.sayHello("welcome to java", 20);
System.out.println("******");
//調(diào)用代理類對象的方法
proxy.sayGoodBye(true, 100);
}
}
運(yùn)行結(jié)果:
ProxyTest sayHello begin
student say hellowelcome to java 20
ProxyTest sayHello end
******
ProxyTest sayHello begin
student sayGoodBye 100.0 true
ProxyTest sayHello end
二波材、動(dòng)態(tài)代理
我們先直接上動(dòng)態(tài)代理的代碼廷区,之后再分析代碼的行為贾铝,上面的Person接口和Student被代理類保持不變
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler{
private Object object;
public MyInvocationHandler(Object object){
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO 自動(dòng)生成的方法存根
System.out.println("MyInvocationHandler invoke begin");
System.out.println("proxy: "+ proxy.getClass().getName());
System.out.println("method: "+ method.getName());
for(Object o : args){
System.out.println("arg: "+ o);
}
//通過反射調(diào)用 被代理類的方法
method.invoke(object, args);
System.out.println("MyInvocationHandler invoke end");
return null;
}
}
import java.lang.reflect.Proxy;
public class Client2 {
public static void main(String[] args) {
// TODO 自動(dòng)生成的方法存根
//創(chuàng)建需要被代理的類
Student s = new Student();
//這一句是生成代理類的class文件垢揩,前提是你需要在工程根目錄下創(chuàng)建com/sun/proxy目錄水孩,不然會(huì)報(bào)找不到路徑的io異常
//System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//獲得加載被代理類的 類加載器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
//指明被代理類實(shí)現(xiàn)的接口
Class<?>[] interfaces = s.getClass().getInterfaces();
// 創(chuàng)建被代理類的委托類,之后想要調(diào)用被代理類的方法時(shí)俘种,都會(huì)委托給這個(gè)類的invoke(Object proxy, Method method, Object[] args)方法
MyInvocationHandler h = new MyInvocationHandler(s);
//生成代理類
Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h);
//通過代理類調(diào)用 被代理類的方法
proxy.sayHello("yujie.wang", 20);
proxy.sayGoodBye(true, 100);
System.out.println("end");
/*或者
//創(chuàng)建需要被代理的類
Student s = new Student();
// 創(chuàng)建被代理類的委托類,之后想要調(diào)用被代理類的方法時(shí)宙刘,都會(huì)委托給這個(gè)類的invoke(Object proxy, Method method, Object[] args)方法
InvocationHandler handler = new MyInvocationHandler(s);
//生成代理類
Person p = (Person) Proxy.newProxyInstance(handler.getClass().getClassLoader(), s.getClass().getInterfaces(), handler);
/*
* 通過Proxy的newProxyInstance方法來創(chuàng)建我們的代理對象,我們來看看其三個(gè)參數(shù)
* 第一個(gè)參數(shù) handler.getClass().getClassLoader() 衙猪,我們這里使用handler這個(gè)類的ClassLoader對象來加載我們的代理對象
* 第二個(gè)參數(shù)realSubject.getClass().getInterfaces()垫释,我們這里為代理對象提供的接口是真實(shí)對象所實(shí)行的接口,表示我要代理的是該真實(shí)對象显蝌,這樣我就能調(diào)用這組接口中的方法了
* 第三個(gè)參數(shù)handler曼尊, 我們這里將這個(gè)代理對象關(guān)聯(lián)到了上方的 InvocationHandler 這個(gè)對象上
*/
//通過代理類調(diào)用 被代理類的方法
p.sayHello("yujie.wang", 20);
p.sayGoodBye(true, 100);*/
}
}
運(yùn)行結(jié)果:
MyInvocationHandler invoke begin
proxy: com.sun.proxy.$Proxy0
method: sayHello
arg: yujie.wang
arg: 20
student say helloyujie.wang 20
MyInvocationHandler invoke end
MyInvocationHandler invoke begin
proxy: com.sun.proxy.$Proxy0
method: sayGoodBye
arg: true
arg: 100.0
student sayGoodBye 100.0 true
MyInvocationHandler invoke end
end
對比上面兩種代理的實(shí)現(xiàn)骆撇,發(fā)現(xiàn)很明顯的一個(gè)區(qū)別父叙,就是靜態(tài)代理中,代理類中已經(jīng)指明了要被代理的類(也就是Student類屿岂,我們就實(shí)現(xiàn)了他的接口Person)鲸匿,這個(gè)邏輯是在寫代碼的時(shí)候就已經(jīng)知道带欢,但若寫代碼的時(shí)候不知道要代理哪個(gè)類乔煞,就只能通過動(dòng)態(tài)代理實(shí)現(xiàn)了,動(dòng)態(tài)代理中逗宜,代理類中指定的是Object 對象空骚,也就是可以代理任何的類囤屹,然后在運(yùn)行的時(shí)候,把Student賦予給代理類(InvocationHandler handler = new MyInvocationHandler(s);)乡括,這樣就完成了動(dòng)態(tài)代理的過程。
總結(jié)一下:
jdk的代理讓我們在不直接訪問某些對象的情況下诲泌,通過代理機(jī)制也可以訪問被代理對象的方法盲赊,這種技術(shù)可以應(yīng)用在很多地方比如RPC框架,Spring AOP機(jī)制档礁,但是我們看到j(luò)dk的代理機(jī)制必須要求被代理類實(shí)現(xiàn)某個(gè)方法,這樣在生成代理類的時(shí)候才能知道重新那些方法吝沫。這樣一個(gè)沒有實(shí)現(xiàn)任何接口的類就無法通過jdk的代理機(jī)制進(jìn)行代理呻澜,當(dāng)然解決方法是使用cglib的代理機(jī)制進(jìn)行代理。
三惨险、cglib動(dòng)態(tài)代理
public class Person {
public void sayHello(String name, int age){
System.out.println(name+"的年齡為:"+age);
};
}
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class PersonMethodInterceptor implements MethodInterceptor{
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
// TODO 自動(dòng)生成的方法存根
System.out.println("before:"+method.getName());
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("after:"+method.getName());
return object;
}
}
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// TODO 自動(dòng)生成的方法存根
Enhancer enchancer = new Enhancer();
enchancer.setSuperclass(Person.class);//繼承被代理的類
enchancer.setCallback(new PersonMethodInterceptor());//設(shè)置回調(diào)
Person p = (Person) enchancer.create();//生成代理對象
p.sayHello("caililiang", 20);//在調(diào)用代理類中的方法時(shí)會(huì)被我們實(shí)現(xiàn)的方法攔截器進(jìn)行攔截
}
}
運(yùn)行結(jié)果:
before:sayHello
caililiang的年齡為:20
after:sayHello
總結(jié):