一.认识动态代理
1.定义:
动态代理是一种设计模式,它允许在运行时创建代理对象,并将方法调用重定向到不同的实际对象。它使我们能够在不修改现有代码的情况下增加或改变某个对象的行为。
2.作用:
代理可以无侵入式地给对象增强其它功能,实现在不修改对象代码的前提下为对象增加新功能。
3.实例:
对象与代理之间的关系类似于明星与经济公司的关系,接下来我们用明星与经纪公司来举例以理解动态代理。
一个明星A拥有如下两种才艺:
(1)唱歌
(2)跳舞
但是明星A如果要给大众唱歌,他还需要准备:唱歌场地,宣发,演出结束后还要收集大众的反响。这些事情明星A要是嫌麻烦不想自己做,这时候他就需要签一个经纪公司。经纪公司既能做这些准备工作与善后工作,还能安排明星A去演出。
因此经纪公司便拥有如下多种功能:
(1)准备场地
(2)宣发
(3)安排明星进行:唱歌、跳舞
(4)收集大众反响
当主办方要邀请明星A来演出时,主办方只需找到明星的经济公司即可,因为经纪公司在能实现明星A的功能的基础上还能有额外的功能。
上述的明星A就是对象,经纪公司就是一个代理。代理可以看作是对最终调用目标的一个封装,我们能够通过操作代理对象来调用目标类,却不用直接访问对象,实现了保护目标对象和增强目标对象。
二.使用动态代理
1.先把代理目标的所有要被代理的方法写入一个接口中,再使目标类继承该接口。
(1)这是一种本末倒置的写法,一般是先有接口,再用类去实现接口以使用接口中的方法。但动态代理是先有明确的类及成员方法,根据成员方法写接口,再用类去实现接口。
2.动态代理在Java中的实例就是一个实现了上述接口的实例对象。可以理解为:代理并不是要代理目标对象本身,而是要代理目标对象的成员方法。只要才艺,不要人。
3.创建动态代理的代码:
(1)java.lang.reflect.Proxy类:提供了为对象产生代理的静态方法方法。
(2)Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
参数一loader:指定用哪个类加载器去加载生成的代理类。类名.class.getClassLoader()
参数二interfaces: 一个由所有代理方法接口的.class文件构成的数组。new Class[]{interface1.class,interface2.class...};
参数三h:是一个继承了InvocationHandler接口的类,通过重写InvocationHandler接口中的invoke方法来用来指定生成的代理要干什么事情。以匿名内部类的形式实现。
(3)newProxyInstance()方法的返回值是一个实现了代理方法接口的Object对象,我们直接将其强转成接口类,并用一个接口的实例对象接收。也就是说,这个接口的实例对象就是代理。
(4)public Object invoke(Object proxy, Method method, Object[] args){}
参数一proxy:代理
参数二method:被代理的方法
参数三args[]:执行method方法要传入的实参
invoke()方法内部分为两部分:新拓展的功能 + 通过反射:method.invoke()执行原功能。
4.实例
以明星和经纪公司为例体现动态代理。
(1)明星类:拥有sing和dance两种方法
public class Star {
String name;
public Star(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
//唱歌
public String sing(String songname){
System.out.println(name+"正在唱"+songname);
return "演出完毕";
}
//跳舞
public String dance(String dancename){
System.out.println(name+"正在跳"+dancename);
return "演出完毕";
}
}
(2)根据明星类要被代理的sing和dance方法创建对应接口
public interface Activities {
String sing(String songname);
String dance(String dancename);
}
再反过来让明星类继承该接口
(3) 再创建一个类专门用于为Star类创建动态代理
public static class StarProxy {
//为obj对象创建动态代理
public Activities createProxy(Object obj){
//使用Proxy类的newProxyInstance方法创建代理对象,返回的Object对象用接口实例接收
Activities activities = (Activities) Proxy.newProxyInstance(StarProxy.class.getClassLoader(), new Class[]{Activities.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//要拓展的内容1
if("sing".equals(method.getName())){
System.out.println("准备唱歌场地,宣发");
}else if("dance".equals(method.getName())){
System.out.println("准备跳舞场地,宣发");
}
//通过反射:method.invoke使用原方法,注意原方法有返回值
Object str = method.invoke(obj,args);
//要拓展的内容2
System.out.println("收集反响");
//返回原方法的返回值
return str;
}
}
);
//返回这个接口实例,也就是动态代理本身
return activities;
}
}
(4)执行
public class Test {
public static void main(String[] args) {
//创建明星对象
Star star = new Star("ZhangSan");
//获取明星对象的代理
Activities activities = StarProxy.createProxy(star);
//执行方法
String str1 = activities.sing("LovelySong");
//输出返回值
System.out.println(str1);
String str2 = activities.dance("ElegantDance");
System.out.println(str2);
}
}
5.思考
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法中最后一个参数的类型是InvocationHandler接口,而接口是不能有实例方法的,为什么我们能重写接口中的invoke()方法以实现功能拓展呢?
因为这里体现了Java多态的特性:接口 接口实例 = new 接口实现类();
我们实际上是新建了一个类去实现InvocationHandler接口并重写其invoke()方法,再new了一个该类的对象,由于该类实现了InvocationHandler接口,是InvocationHandler的接口实现类,所以用InvocationHandler接口去接收这个对象。传入的参数就是InvocationHandler接口类。
也就是说上述创建明星代理的代码实际可以写成:
//InvocationHandler的实现类
class IHC implements InvocationHandler{
Object obj;
public IHC(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//要拓展的内容1
if("sing".equals(method.getName())){
System.out.println("准备唱歌场地,宣发");
}else if("dance".equals(method.getName())){
System.out.println("准备跳舞场地,宣发");
}
//通过反射:method.invoke使用原方法,注意原方法有返回值
Object str = method.invoke(obj,args);
//要拓展的内容2
System.out.println("收集反响");
//返回原方法的返回值
return str;
}
}
public class StarProxy {
//为obj对象创建动态代理
public static Activities createProxy(Object obj){
//实现类对象用接口类接收
InvocationHandler h = new IHC(obj);
//使用Proxy类的newProxyInstance方法创建代理对象,返回的Object对象用接口实例接收
Activities activities = (Activities) Proxy.newProxyInstance(StarProxy.class.getClassLoader(), new Class[]{Activities.class},h);
//返回这个接口实例,也就是动态代理本身
return activities;
}
}