标题
代理模式是一种比较常见的设计模式,在框架中也是应用的比较多.最典型的应用就是Aop
本文只讲解静态代理和动态代理,动态代理只使用基于接口JDK动态代理,基于类的cglib动态代理。动态代理实现有很多,像基于字节码的Javassist等。
静态代理
借用一下狂神老师的租房案例
平时如果我们租房的话,第一时间应该想到的都是找一个公司问问房源,问问价格,或者是打开手机APP去看。APP暂且不谈。
我们为什么找中介公司?
房东为什么把房子丢给中介公司?
接下来我们先讨论一下这个问题再去写代码就很好理解了
-
我们为什么去找中介公司?
因为方便,我们很容易就能能找到中介公司,生活中大街小巷中介应该很常见吧。房源信息多等
-
房东为什么把房子丢给中介?
因为房东找不到租客,房东总不能在路上见个人就问租房子吗?可以但不合适,房东的目的就是把房子租出去,租给谁,他不管,他只要见到租金.
理清了这两个问题我们开始写代码
//租房的接口,房东和中介联系起来的原因不就是因为有共同的租房方法吗?
public interface Rent {
/**
* 租房
*/
public void rent();
}
//房东的类,不同的房东可能有不同的房子
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要租房了");
}
}
//中介的类,里面可能有其他方法,像看房子、签合同等,
public class Medium {
private Rent rent;
public void setRent(Rent rent){
this.rent = rent;
}
//租房子,这个方法的名字是随便的,这就相当于中介对租客说租房子,或者租赁房子一样
public void rent(){
rent.rent();
}
}
//进行验证
public class ProxyTest {
public static void main(String[] args) {
Host host = new Host();
//生成代理对象
Medium medium = new Medium();
medium.setRent(host);
//通过代理对象去调用方法
medium.rent();//房东要租房了
}
}
解释一下
这里的工作模型就是租客 --> 中介 --> 房东
这里我们很明显发现是中介在真正的跑腿在干活
大家一定要记住这一点,这对接下来理解动态代理很关键
ps:这里只是简单演示静态代理,将模型简单的抽象化了,别再问我有什么用了
动态代理
这里的代理对象我们是自己写的,但是当房东比较多的话,我们就要不停的写代理类,或者修改代理类,很明显这不是我们想要的代码
动态代理就是让我们动态的生成代理对象
先看代码
//租房接口
public interface Rent {
public void rent();
}
//房东租房子
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要租房了....");
}
}
官方解释是:InvocationHandler是由代理实例的调用处理程序实现的接口 。
每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke
方法。
读着比较拗口,还不容易理解,因为文档是我们翻译过来的,和外国人理解有出入很正常。
我个人理解就是你把它当做一个跑腿的,就是为中介服务的,等会我们可以看看是不是这样的
//
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("中介带我去看房子");
Object invoke = method.invoke(target, args);
System.out.println("签订合同");
return invoke;
}
}
public class ProxyTest {
public static void main(String[] args) {
//创建你一个跑腿的
ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler();
//将房子交给跑腿的
invocationHandler.setTarget(new Host());
/**
* 动态生成代理对象
* 返回指定接口的代理类实例
* 跑腿的把房子所有信息都交给中介
*/
Rent proxyInstance = (Rent) Proxy.newProxyInstance(Rent.class.getClassLoader(), new Class[]{Rent.class}, invocationHandler);
/**
* 通过代理实例调用方法时,会将分配到其调用处理程序(InvocationHandler),
* 生成代理对象时传的哪一个处理程序,通过哪一个调用程序的invoke方法进行执行
* 中介会让跑腿的去带我们看房子或者其他的任务
* */
proxyInstance.rent();
}
}
Rent proxyInstance = (Rent) Proxy.newProxyInstance(Rent.class.getClassLoader(), new Class[]{Rent.class}, invocationHandler);
这一步是动态的创建代理对象
我们可以看一下源码
/*
ClassLoader loader 类加载器,会根据类加载器和Class在Cache中寻找,如果找不到会创建代理对象
InvocationHandler h 跑腿的,将跑腿的和中介联系起来就是代理对象做的事.代理对象只负责分配跑腿的任务,具体怎么完成就不在关心了
*/
@CallerSensitive
public static Object newProxyInstance( ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//查询或者创建代理类的class
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//得到Proxy参数为InvocationHandler的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//创建并返回proxy代理对象,其中包含InvocationHandler(跑腿的)
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
总结:
这个流程就变成了 租客 --> 中介(代理对象) —> 跑腿的(InvocationHandler)—> 房东(真实对象)
个人理解总结,可能不太完善,期待大家留言讨论