@liayun
2016-09-20T16:32:53.000000Z
字数 4913
阅读 1393
java基础加强
动态代理技术在实际开发中用到的非常多,所以有必要详解一下这门技术。这门技术非常难,因此学起来还是比较困难的,但我们不怕困难,定要啃下这块硬骨头。
要想理解动态代理这门技术,必须明确两个概念:
现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。那么如何编写生成代理对象的类呢?
要想编写生成代理对象的类,必须考虑到两个要素:
Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:
初学者必须理解,或不理解必须记住的2件事情:
相关概念看完之后,大家可能还是处在云里雾里中,但没关系,我们接下来通过一个例子来领大家入门。
李宇春,网上称春哥(在此处我无意诋毁她),她在还没成名前,一些大老板可能会点她唱歌或跳舞,那么她就会自己去给这些金主唱歌或跳舞,可能是有些许迫不得已吧!但是她成名之后,就会有自己的代理人,即经纪人,一些金主点她唱歌或跳舞时,其代理人就会跳出来,拦截对李宇春的访问,这个时候由代理人出面,若要我代理的对象——李宇春唱歌,必须给1万美刀;若要我代理的对象——李宇春跳舞,必须给2万美刀。这个场景用代码来描述就应该是这样。
首先设计真实的业务对象,即李宇春。
public class Liyuchun {
public void sing() {
System.out.println("春哥唱歌了!!");
}
public void dance() {
System.out.println("春哥跳舞了!!");
}
}
接下来我们就要编写生成代理对象的类,首先明确其要代理的对象是李宇春,接着就要设计一个方法生成代理对象(即代理人)。
public class LiyuchunProxy {
private Person chunchun = new Liyuchun();
public Xxx(返回值) createProxy() {
return ...
}
}
这儿,我们就要思考生成代理对象的方法——createProxy()的返回值的类型应该是什么了?不可能是李宇春,即Liyuchun,那到底应该是什么类型呢?在Java里面有这样一个约定:要想创建某一个对象的代理对象,那么该对象必须实现一个接口。
由于李宇春是一个人,所以我们抽出一个人的接口。
public interface Person {
void sing();
void dance();
}
真实的业务对象(即李宇春)的代码就应修改为:
public class Liyuchun implements Person {
@Override
public void sing() {
System.out.println("春哥唱歌了!!");
}
@Override
public void dance() {
System.out.println("春哥跳舞了!!");
}
}
那这样我们就明确了生成代理对象的方法——createProxy()的返回值的类型了,即为Person,通过该方法产生李宇春的代理,李宇春的代理同样也是一个人,对不对?接下来,我们使用Java提供了的Proxy类,调用它的newInstance方法生成李宇春的代理对象(即代理人),代码大概是这样:
public class LiyuchunProxy {
private Person chunchun = new Liyuchun();
public Person createProxy() {
// 产生某个对象的代理对象
return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
/*
* proxy:把代理对象自身传递进来
* method:代表当前调用的方法
* args:当前调用方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
在这里面编写代码来指定产生的代理对象干什么事情...
return null;
}
});
}
}
假设这样的生成代理对象的类编写好了,有些金主来点春哥唱歌或跳舞,那么其代理人拦截下来之后,就调用代理人自己的唱歌或跳舞方法。这样代码就是这样的:
public class Demo1 {
public static void main(String[] args) {
LiyuchunProxy proxy = new LiyuchunProxy();
Person person = proxy.createProxy();
person.sing();
person.dance();
}
}
只要调用代理对象的唱歌、跳舞方法,实际上都是调用invoke方法里面的代码,即invoke方法由唱歌、跳舞方法来调用,当唱歌、跳舞方法来调用invoke方法做事情的时候,会给invoke方法传递一些参数进来。这说明代理对象到底干什么事情是由invoke方法里面的代码来决定的。
这样我们再回过头来彻底编写完生成代理对象的类。
public class LiyuchunProxy {
private Person chunchun = new Liyuchun();
public Person createProxy() {
// 产生某个对象的代理对象
return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
/*
* proxy:把代理对象自身传递进来
* method:代表当前调用的方法
* args:当前调用方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在这里面编写代码来指定产生的代理对象干什么事情
String methodName = method.getName();
if (methodName.equals("sing")) {
System.out.println("拿1万刀来!!!");
method.invoke(chunchun, args); // 春春唱歌了
} else if (methodName.equals("dance")) {
System.out.println("拿2万刀来!!!");
method.invoke(chunchun, args); // 春春跳舞了
} else {
System.out.println("春哥不支持这个功能!!!"); // 比如金主要春哥陪酒,但春哥怎能是那样的人呢!
}
return null;
}
});
}
}
动态代理技术怎可能像上面那样简单呢?动态代理入门之后,我们再来看看还算是比较复杂的动态代理。
金主除了点春哥唱歌之外,还要指定她专门唱哪首歌,春哥唱完歌之后还要对金主说一声谢谢;他们除了点春哥跳舞之外,还要指定她专门跳什么舞,春哥跳完舞之后还得给金主们一个飞吻!在这样的场景下,Person接口的代码应修改为:
public interface Person {
String sing(String name);
String dance(String name);
}
真实业务对象(即李宇春)的代码应修改为:
public class Liyuchun implements Person {
@Override
public String sing(String name) {
System.out.println("春哥唱"+name+"歌了!!");
return "谢谢哟!!!";
}
@Override
public String dance(String name) {
System.out.println("春哥跳"+name+"舞了!!");
return "飞吻!!!";
}
}
生成代理对象的类的代码应修改为:
public class LiyuchunProxy {
private Person chunchun = new Liyuchun();
public Person createProxy() {
// 产生某个对象的代理对象
return (Person) Proxy.newProxyInstance(LiyuchunProxy.class.getClassLoader(), chunchun.getClass().getInterfaces(), new InvocationHandler() {
/*
* proxy:把代理对象自身传递进来
* method:代表当前调用的方法
* args:当前调用方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在这里面编写代码来指定产生的代理对象干什么事情
String methodName = method.getName();
if (methodName.equals("sing")) {
System.out.println("拿1万刀来!!!");
/*
* 春春要唱歌了,唱完歌还要对金主们说声:谢谢哟!!!
* 由于是代理对象拦截了对真实业务对象的访问,所以若是不将"谢谢哟!!!"返回,
* 结果就是其返回给代理对象了,金主们肯定要不爽了。
*/
return method.invoke(chunchun, args);
} else if (methodName.equals("dance")) {
System.out.println("拿2万刀来!!!");
/*
* 春春要跳舞了,跳完舞还要给金主们一个飞吻!!!
* 同样,若是不将"飞吻!!!"返回,
* 结果就是其返回给代理对象了,金主们肯定要日娘了。
*/
return method.invoke(chunchun, args);
} else {
System.out.println("春哥不支持这个功能!!!"); // 比如金主要春哥陪酒,但春哥怎能是那样的人呢!
}
return null;
}
});
}
}
这样,金主要点春哥唱《江南皮革厂》,并且还要我们的春哥跳钢管舞,那么代码就该是这样的:
public class Demo {
public static void main(String[] args) {
LiyuchunProxy proxy = new LiyuchunProxy();
Person person = proxy.createProxy();
String result = person.sing("江南皮革厂");
System.out.println(result);
result = person.dance("钢管舞");
System.out.println(result);
}
}