Java代理模式以及动态代理的两种实现

简介: Java代理模式 java静态代理模式 首先关于java的代理模式我的理解是:分为四个部分1 针对于客户端的部分2 有一个公共的接口3 有个被代理的类4 有个代理类而代理模式带来的优点是:我们可以对被代理类增加更多的处理,在Spring的AOP中就是使用的代理模式可以使得针对于切面进行编程。

Java代理模式

java静态代理模式

首先关于java的代理模式我的理解是:分为四个部分
1 针对于客户端的部分
2 有一个公共的接口
3 有个被代理的类
4 有个代理类
而代理模式带来的优点是:我们可以对被代理类增加更多的处理,在Spring的AOP中就是使用的代理模式可以使得针对于切面进行编程。
静态代理的例子
一个公共的接口Person.class

public interface Person{

public void giveMoney();

}

一个被代理的类

public class Student implements Person{

    private String name;
    public Student(String name){
      this.name = name;
}
public void giveMoney(){
System.out.println(name+"给了50块钱");

}

}

一个代理类

public class Proxy implements Person{

private Person person;
public Proxy(Person person){
this.person = person;
}
public void giveMoney(){
System.out.println("作业完成的很好");//这个就是在我们被代理的对象前面添加的方法。
person.giveMoney();
System.out.println("老师再见");//这个使我们在被代理的对象后面添加的方法类似于Spring AOP中的切面前置通知和前面后置通知
}
}

一个客户端的类

public class Client{

Student stu = new Student("jhc");
Proxy proxy = new Proxy(stu);
proxy.giveMoney();

}

Y6QEB540A72IDTYPEIFG50T
结果可以看到在我们交钱的时候增加了新的方法。
但是静态代理会有一个缺点就是:我们可能不存在只有一个代理类的情况,也许我们会有很多代理类,而添加而过每个代理类都通过对代理类增加方法的话那样操作起来会很麻烦。所以还有一个方式是动态代理。

动态代理模式

通过JDK的动态代理模式实现代理。

在java.lang.reflect包下面有一个Proxy类和InvocationHandler接口,通过这两个类去实现java的动态代理。

创建一个动态代理对象的步骤

  • 创建一个实现了InvocationHandler接口的对象

InvocationHandler stuHandler = new MyInvocationHandler(stu);

  • 使用Proxy类的静态方法得到一个动态代理的类

Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(),new Class<?>[]{Person.class});

  • 通过类然后由反射获得一个具有InvocationHandler类的构造函数

Constructor<?> cons = stuProxyClass.getConstructor(InvocationHandler.class);

  • 通过构造器constructor来创建一个动态实例stuProxy

Person person = (Person)cons.newInstance();

上面四个步骤可以简化成为两个步骤

InvocationHandler stuHandler = new MyInvocationHandler(stu);

Person proxy = (Person)Proxy.newInstance(Person.class.getClassLoader(),new Class[]{Person.class},stuHandler);

关于动态代理的实例模式其实一样

  • 首先是一个公共的接口Person

public interface Person {

public void giveMoney();

}

  • 其次是被代理类的实现Student

private String name;

  • 第三步是比较中的实现InvocationHandler的接口
   public class StuInvocationHandler<T> implements InvocationHandler<T>{
    private T target;
    public StuInvocationHandler(T target){
    this.target = target;
  }
  public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
      System.out.println("在这插入我们想要的方法");
      Object result = method.invoke(target,args);
      System.out.println("这里也可以插入我们想要的方法");
      return result;
  }
} 
  • 第四步是在客户端使用动态代理并测试
   public class ProxyTest{
          public static void main(String[] args){
            Person jhc = new Student("jhc");
            InvocationHandler stuHandler = new StuInvocationHandler<Person>(jhc);
            Person stuProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class[?]{Person.class},stuHandler);
            stuProxy.giveMoney();
             }
  }

结果如图
_55_9__TKHYL_YY7H_UU_0

下面是Proxy.newInstance()的源码的内容。

/*首先解释一下三个参数,第一个参数是为了能够加载出来Person即公共接口这个类,第二个参数是找出实现的全部的接口,第三个参数是为了动态类能够调用方法
*/

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);
        }

        /*
         *这里产生了代理类.
         */
       Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            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;
                    }
                });
            }
            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);
        }
    }

下面是大神关于怎么产生的代理类的分析,具体的做法我也不知道怎么回事,只知道他是生成了动态的代理类的文件并且对该文件进行了反编译。得到的内容是


   byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
        String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
           System.out.println("写文件错误");
        }```
这是把文件写入到硬盘上,利用javap可以把文件进行反编译

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
{
  //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字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("giveMoney", 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());
}

}

/**

  • *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。

*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void giveMoney()

throws 

{

try
{
  this.h.invoke(this, m3, null);
  return;
}
catch (Error|RuntimeException localError)
{
  throw localError;
}
catch (Throwable localThrowable)
{
  throw new UndeclaredThrowableException(localThrowable);
}

}

//注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。

}

这里大神写的很清楚,产生的动态代理类继承自Proxy类,并且构造参数自动的拥有了InvocationHandler的构造参数,并且实现了接口以及接口的方法m3,这里值得注意的是其实纵观全局来看的话,动态代理类只是一个中介类,他并没有实现被代理类的任何方法都是通过InvocationHandler里面具体的Target实现的,这样动态代理的优点在于便于修改动态代理类
###总结

---
   这是动态代理的JDK实现,缺点在于实现公共接口的类才能实现动态代理,没有办法实现对于继承来的类的处理。

主要参考的是大神的博客
https://www.cnblogs.com/gonjan-blog/p/6685611.html
相关文章
|
30天前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
25 0
[Java]静态代理与动态代理(基于JDK1.8)
|
1月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
19 1
|
1月前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
2月前
|
设计模式 Java 数据安全/隐私保护
Java设计模式-代理模式(7)
Java设计模式-代理模式(7)
|
1月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
4月前
|
缓存 Java 测试技术
day27:Java零基础 - 动态代理
【7月更文挑战第27天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
43 2
day27:Java零基础 - 动态代理
|
3月前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)
|
3月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
235 0
|
3月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
189 0
|
4月前
|
存储 设计模式 Java
Java面试题:解释代理模式的概念,并举例说明其应用场景。
Java面试题:解释代理模式的概念,并举例说明其应用场景。
64 0
下一篇
无影云桌面