Java 代理模式_java代理某个类的好处(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前在阿里

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以点击这里获取!

客户端调用
public class StaticProxyTest {
public static void main(String[] args) {
//被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
Person zhangsan = new Student(“张三”);

    //生成代理对象,并将张三传给代理对象
    Person proxy= new StudentsProxy(zhangsan);
    
    //班长代理上交班费
    proxy.handleFee();
}

}


静态代理的代码大家应该很好理解:真正上交班费的是班长代理对象:


看完静态代理的实现,我们看下动态代理:


**相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。** 比如说,想要在每个代理的方法前都加上一个处理方法:



//代理上交班费,调用被代理学生的上交班费行为
public void handleFee() {
     call();
    stu.handleFee();
}

如果针对上交班费的时候,老师让我们叫张三同学去她办公室,调用一个call方法、叫李四同学去打扫卫生、那么这还需要针对不同的学生,在交班费的时候有不同的处理逻辑,这个时候,我们就会频繁的去更改上交班费的实现逻辑,显得很麻烦。这个时候我们就需要用到动态代理了。



首先我们还是定义接口类
public interface Student{
//上交班费
void handleFee();
}

定义目标类:
public class TargetStudent implements Student{

private String name;
public TargetStudent(String name) {
    this.name = name;
}

@Override
public void handleFee() {
   System.out.println(name + "上交班费50元");
}

}


接口与目标类的实现与静态代理的方式差不太多。


主要的差异点,在与代理类的生成



public class StuInvocationHandler implements InvocationHandler {

/**
 * invocationHandler持有的被代理对象
 */
T target;

public StuInvocationHandler(T target){
    this.target = target;
}

/**
 * proxy:代表动态代理对象
 * method:代表正在执行的方法
 * args:代表调用目标方法时传入的实参
 */

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = method.invoke(target, args);
    return result;
}

}

客户端调用
public class ProxyClient {

public static void main(String[] args) {

    //创建一个实例对象,这个对象是被代理的对象
    Student zhangSan = new TargetStudent("张三");

    //创建一个与代理对象相关联的InvocationHandler
    InvocationHandler stuHandler = new StuInvocationHandler<Student>(zhangSan);

    //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
    Student stuProxy = (Student) Proxy.newProxyInstance(Student.class.getClassLoader(), new Class<?>[]{Student.class}, stuHandler);

    //代理执行上交班费的方法
    stuProxy.handleFee();
}

}


创建了一个需要被代理的学生张三,将zhangsan对象传给了stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数了的,上面也有说到所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。


**动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了**。例如,这里的方法计时,所有的被代理对象执行的方法都会被计时,然而我只做了很少的代码量。



接下来我们针对动态代理的原理进行分析:


看proxyClient客户端的代码:我们可以看到:


第一步:我们创建了目标对象的实例。


第二步:我们创建了一个和目标对象相关联的InvocationHandler 对象。


第三步:生成代理对象。


第四步:通过代理对象访问目标对象的方法。


我们从生成代理对象开始分析:


其生成的原理:通过java的反射生成一个proxy的类文件存放在内存中,并有缓存处理。


返回利用java的反射技术。获取到一个代理对象proxy 的实例。



private static Class<?> getProxyClass0(ClassLoader loader, Class<?>… interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException(“interface limit exceeded”);
}

     //通过类加载器获取到代理的class文件。并用反射生成proxy实例

//如果缓存中有,就从缓存中返回,如果没有,就在内存中新生成一个proxy的class文件返回
return proxyClassCache.get(loader, interfaces);
}


明白代理对象生成后,我们来看下,Proxy类与接口InvocationHandler之间的关系:



import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;

public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;

/**
*注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
*为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
*被代理对象的实例,不禁会想难道是…? 没错,就是你想的那样。
*
*super(paramInvocationHandler),是调用父类Proxy的构造方法。
*父类持有:protected InvocationHandler h;
*Proxy构造方法:

  • protected Proxy(InvocationHandler h) {
  •     Objects.requireNonNull(h);
    
  •     this.h = h;
    
  • }
    

*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}

//这个静态块本来是在最后的,我把它拿到前面来,方便描述
static
{
try
{
//看看这儿静态块儿里面有什么,是不是找到了handleFee方法。请记住handleFee通过反射得到的名字m3,其他的先不管
m1 = Class.forName(“java.lang.Object”).getMethod(“equals”, new Class[] { Class.forName(“java.lang.Object”) });
m2 = Class.forName(“java.lang.Object”).getMethod(“toString”, new Class[0]);
m3 = Class.forName(“proxy.Person”).getMethod(“handleFee”, new Class[0]);
m0 = Class.forName(“java.lang.Object”).getMethod(“hashCode”, new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}

/**
*
*这里调用代理对象的handleFee方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void handleFee()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}


从上面proxy Class文件生成的逻辑中,我们可以明白在代理对象的构造器中传入了一个InvocationHandler 对象,这就是为什么newProxyInstance方法的时候需要传入一个InvocationHandler对象. 


### 最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

![](https://img-blog.csdnimg.cn/img_convert/ae2e1f0fbd97a1c341a37d54a5793e23.png)

给大家整理的电子书资料:

  

![](https://img-blog.csdnimg.cn/img_convert/1ffe9fc1c3c5d58293dc463c39e81b16.png)



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

3750889)]



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值