什么是代理?
以租房子举例
房东能用钥匙开门
中介负责开门前
带领顾客过去,开门后
帮顾客讲解。
房东只要开门
就好,所有这个动作的前后都给中介去做。那么中介就是这个代理
。
用代码写就是
interface 开门接口{
void 开门();
}
public class 房东1 implement 开门接口{
//中介要求能开租房门的才能做房东
public void 开门(){
打开防盗门();
}
}
public class 中介 implement 开门接口{
开门接口 具备开门能力的人1;
public 中介(开门接口 具备开门能力的人){
this.具备开门能力的人1 = 具备开门能力的人;
}
public void 开门(){
//开门前中介带领顾客`
具备开门能力的人1.开门();
//开门后中介讲解`
}
}
//最终代码 虽然调用的是中介.开门,但最终会到房东开门
public static void main(String args[]){
new 中介(new 房东1()).开门();
}
静态代理
上述就是静态代理最终还是把有开门能力的房东传给了中介,中介调用房东开的门,他只做了之前和之后的服务。
缺点: 如果接口修改,那么对应的中介和房东都要修改。 代理只服务一种类型对象,多类型就要写很多代理类。
动态代理
这个代理类我就是不想写,能不能让机器给我写了?
核心动作是 房东.开门()
那么我可以如果用反射可以这么写 反射来的开门方法.invoke(房东,参数没有就null);
如果我自己生成一个中介,它的类构造函数中有个匿名内部类,然后匿名内部类的方法方法中调用 反射来的方法.invoke(房东,参数没有就null);
,中介调用任何方法都调用这个 方法就好啦。
用房东时,房东开门,前后自己做就好了。
那么我怎么做呢?核心就是通过开门接口class
生成代理对象
中介对象。反正两者挺像的。
package com.kent.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(final String[] args) {
final IOpenDoor maleWorker = new OpenDoorImpl();
IOpenDoor iOpenDoor = (IOpenDoor) Proxy.newProxyInstance(
maleWorker.getClass().getClassLoader(),
maleWorker.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("begin-------");
Object result = method.invoke(maleWorker, args);
System.out.println("end-------");
return result;
}
});
iOpenDoor.dowork();
}
public interface IOpenDoor {
void dowork();
}
public static class OpenDoorImpl implements IOpenDoor {
@Override
public void dowork() {
System.out.println(this.getClass().getSimpleName()+": dowork");
}
}
}
怎么做呢?
1、创建房东
和开门接口
2、创建InvocationHandler接口的实现类,也就是匿名方法,在里边用invoke实现房东真正做的事情
3、通过Proxy的静态方法newProxyInstance( ClassLoaderloader, Class[] interfaces, InvocationHandler h) 创建一个代理对象中介
4、使用代理对象中介
总结起来就是Proxy通过类加载器和接口接口class 动态创建了一个指定接口的代理对象。
看一下这个代理对象的class
public class EmployeeProxy extends Proxy implements HumanAct{
private static Method m1;
static {
try {
m1 = Class.forName("com.example.lib.HumanAct").getMethod("eating");
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
protected EmployeeProxy(InvocationHandler h) {
super(h);
}
@Override
public void eating() {
try {
//就是传进来了InvocationHandler匿名方法
super.h.invoke(this,m1,null);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
可以看到 静态代码块中 反射生成了接口的方法的method,并在内部也实现了接口,等这个类调用该实现接口的方法,便会调用外部的匿名内部类InvocationHandler的invoke方法,那么相当于把中介之前的要做的事情都在invoker中做了。这样动态生成的代理对象,就不用写代理类,接口修改也只用改房东,目标类。
能否不写代理类,而直接得到代理Class对象,然后根据它创建代理实例(反射)。 Class包含了一个类的所有信息,比如构造器 方法字段。
代理类和目标类实现相同接口,是为了保证代理对象和目标对象 内部结构一致,这个对代理对象的操作可以到目标对象上来。
Proxy的静态方法getProxyClass(ClassLoader, interfaces),通过类加载器和一组接口可以得到代理Class对象。
getProxyClass(),会从接口Class中,拷贝类结构信息到一个新的Class中,但新Class带有构造器,可以创建对象。
核心就是通过接口Class,创建一个代理Class,通过代理Class对象创建代理对象。
再来个不那么恰当的例子。
九千岁是接口,有童子功大法。但是不能够实例化,因为没有小丁丁(构造器)。那么他不想一身武艺,后继无人。于是想到了两种方法。
静态代理 认干儿子,让代理类实现他,干儿子有构造器,可以new实例
动态代理 找神医,Proxy用克隆大法Proxy.getProxy(),克隆一个干儿子 代理Class,因为有小dd所以克隆人Class可以创建实例,也就是代理对象