标签:目的 技术分享 追踪 turn 网络 get throws 不用 等等
在了解动态代理之前,先对代理有一个认识.
代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
打个比方:你买火车票的时候,并不直接花钱购买, 而是将钱预先垫付到抢票软件上, 使抢票软件为你购买, 你要做的行为就是买票,抢票软件就是你的代理
代理对象控制对被代理对象的访问:
这是代理的通用模型图
Subject:定义了被代理角色和代理角色的共同接口或者抽象类,也就是subject中定义了共同接口opration();
Realsubject:实现或者继承抽象主题角色,定义实现具体业务逻辑的实现。
Proxy:也就是代理人,porxy持有Realsubject的引用控制和实现. 并且有自己的处理逻辑.
代理分为静态和动态两种,先了解静态代理,知道其缺点后,再了解动态代理,会豁然开朗.
静态代理的作用:
静态代理通常用于对原有业务逻辑的扩充。比如持有二方包的某个类,并调用了其中的某些方法。然后出于某种原因,比如记录日志、打印方法执行时间,但是又不好将这些逻辑写入二方包的方法里。所以可以创建一个代理类实现和二方方法相同的方法,通过让代理类持有真实对象,然后在原代码中调用代理类方法,来达到添加我们需要业务逻辑的目的。
静态代理的实现:
这里方便理解 引入的Proxy是自己定义的,在下面动态代理的时候会使用java.reflect中提供的Proxy
package proxy;
//将业务层抽象出来的接口.
public interface Subject {
void doSomething();
}
package proxy;
//真实对象,也就是被代理者,理解为你自己就可以了,要进行买票行为.
public class RealObject implements Subject{
@Override
public void doSomething() {
System.out.println("这里是真实对象");
}
}
package proxy;
//代理者,进行代理事务的人,理解成抢票软件,要代替你进行买票行为.
public class Proxy implements Subject{
private Subject realObject;
Proxy(Subject realObject){
this.realObject=realObject;
//从这里知道需要代理的人是谁
}
@Override
public void doSomething() {
System.out.println("这里是代理对象");
//可以在调用真实对象前增加操作
//例如在开抢前告诉你,抢票要开始了.
realObject.doSomething();//在这里调用真实的代理对象
//可以在调用真实对象后增加操作.
//在抢票结束后,告诉你成功或者失败.
}
}
执行:
package proxy;
public class ProxyTest {
public static void main(String[] args){
RealObject realObject =new RealObject();
realObject.doSomething();//被代理对象做的事,或者说委托人想要做的事
System.out.println("-----------------");
Proxy proxy =new Proxy(new RealObject());//告知代理对象被代理对象是谁,或者说告诉执行者委托人要做什么.
proxy.doSomething();//通过代理对象调用被代理对象,实现代理.
/*
代理和被代理是不是有点晕,换一种说法来说.
以DOTA为例:
大哥本来要自己拉野,忽然发现一大波线袭来,于是就和酱油说,帮我拉两波野,一会带你喝汤! 大哥就是被代理者
酱油说: 是大哥! 酱油在去拉野的路上,吃了赏金符,拉完野之后配合队友杀了一波人. 酱油就是代理者
吃赏金符就是预处理.
杀人就是结果处理.
*/
}
}
大家有没有发现,这样虽然形成了代理行为,但是写法恨死,耦合度很高,现在是一个火车票,如果有飞机票,演唱会票等等一系列的需求和一系列的雇主,会导致代理类的代码量越来越大,并且会有重名的危险,这也就是静态代理的缺点.
为了解决这个问题,大佬们发明出了动态代理的方法.
JAVA为我们提供了动态代理的接口,使用起来非常方便,下边来了解一下.
需要将要扩展的功能写在一个InvocationHandler 实现类里:
这个Handler中的invoke方法中实现了代理类要扩展的公共功能。
静态代理的缺点:
有十个不同的RealObject,同时我们要去代理的方法是不同的,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing.
方法1:为每个代理创建代理类,这样会造成数个类
方法2: 在一个代理类中创建数个代理方法,当代理方法越来越多的时候容易造成代理类中的代码量越来越大 并且有重名的风险.
为此动态代理诞生了:
动态代理的使用:
先看一下动态代理的接口:
我们来看一下接口的结构:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
//这个接口只有一个方法 就是invoke方法,也就是说我们只要实现invoke方法就可以了
}
虽然这个参数不用我们传入,但是还是需要了解一下,第一个就是代理类的实例,第二个是方法, 第三个是对象的参数数组.
2.通过newProxyInstance方法创建代理实例:
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
通过这个方法返回我们代理类的实例, 也就是RealObject的Proxy
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序
从参数上看 第一个参数是类加载器, 第二个方法是个Class对象数组接口,第三个参数是我们上边实现的InvocationHandler
在这里不贴源码了,有兴趣的可以自己追踪或者查看下方我的参考资料.
动态代理实例:
沿用上边静态代理的类,下面只写测试类和处理类.
处理类:
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object realObject;
Object bind(Object realObject){
this.realObject=realObject;//给真实对象赋值
return Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
realObject.getClass().getInterfaces(),
this);
//通过Class对象得到该对象的构造器,接口方法数组,传入自己构造后的实例.
//java.reflect.Proxy方法最后返回的是一个代理实例.
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在这里拓展代理逻辑
System.out.println("rua!");
Object result= method.invoke(realObject,args);//方法的调用.
//这里的invoke调用是java进行调用,proxy对象,方法,和参数数组已经是我们之前传入的被代理类的了.
return result;
}
}
测试类:
public class DynamicProxy {
public static void main(String[] args){
DynamicProxyHandler pHandler=new DynamicProxyHandler();//创建我们动态代理处理类的对象
Subject proxy=(Subject)pHandler.bind(new RealObject());//对其进行动态代理的参数绑定然后返回一个代理实例
proxy.doSomething();//调用
}
}
我第一次看的时候也是云里雾里,不明白为什么这样就动态了, 这里的方法调用,都与方法对象无关,不直接进行调用.
不管你在Subject中定义了什么接口,都与调用无关,依靠反射,实时获取你的方法,参数,类的数据,再通过反射进行调用.
有十个不同的RealObject,同时我们要去代理的方法是不同的,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing.
以这个例子来说, 我传入10个不同的对象,对象中自然有自己的实现,我通过反射拿到实现后构建新的对象,不会对原有对象造成影响,再对新的对象进行方法调用操作,来100个 1000个对象也同样可以.
上面一切的条件都是建立在反射的基础上,如果反射了解的不太清楚,请返回去看反射,我的博客也有写,也可以通过网络自行了解.
参考资料:
1.https://blog.csdn.net/wangqyoho/article/details/77584832
2.https://www.zhihu.com/question/20794107/answer/23330381
3.Thinking in java page 592-598
标签:目的 技术分享 追踪 turn 网络 get throws 不用 等等
原文地址:https://www.cnblogs.com/Curry-Rice/p/8990904.html