【Java代理】【静态代理】【动态代理】【动态代理的2种方式】

简介: 【Java代理】【静态代理】【动态代理】【动态代理的2种方式】

正文


简介讲述代理的一些知识点【Java代理】【静态代理】【动态代理】【动态代理的2种方式】


一、代理模式#


代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。

举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想在现实中的一个例子。

代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象。


二、代理的分类#


  • 静态代理
  • 动态代理


三、静态代理#


静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

我们一起看一下静态代理的例子:

1.定义一个接口


/**
 * @author :breakpoint/赵立刚
 * @date : 2020/04/29
 */
public interface SayHelloInterface {
    void sayHello(String name);
}


2.定义实现类


/**
 * @author :breakpoint/赵立刚
 * @date : 2020/04/29
 */
public class SayHelloInterfaceImpl implements SayHelloInterface {
    @Override
    public void sayHello(String name) {
        System.out.println("hello:" + name);
    }
}


3.定义我们的代理类


/**
 * @author :breakpoint/赵立刚
 * @date : 2020/04/29
 */
public class SayHelloProxy implements SayHelloInterface {
    private SayHelloInterface target;
    public SayHelloProxy(SayHelloInterface target) {
        this.target = target;
    }
    @Override
    public void sayHello(String name) {
        System.out.println("pre code");
        target.sayHello(name);
        System.out.println("after code");
    }
}


4.定义测试类


/**
 * @author :breakpoint/赵立刚
 * @date : 2020/04/29
 */
public class Main {
    public static void main(String[] args) {
        SayHelloInterface sayHelloInterface=new SayHelloInterfaceImpl();
        SayHelloProxy proxy=new SayHelloProxy(sayHelloInterface);
        proxy.sayHello("zhaoligang");
    }
}


5.运行结果


pre code
hello:zhaoligang
after code
Process finished with exit code 0


静态代理总结:

  • 1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
  • 2.缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护。

如何解决静态代理中的缺点呢?答案是可以使用动态代理方式。


四、动态代理#


动态代理有以下特点:

  • 1.代理对象,不需要实现接口
  • 2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
  • 3.动态代理也叫做:JDK代理,接口代理

JDK中生成代理对象的API

  • 代理类所在包:java.lang.reflect.Proxy
  • JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

看一下我们动态代理的例子:

1.接口也是使用的我们前面的接口:


public interface SayHelloInterface {
    void sayHello(String name);
}


2.定义我们的实现类


/**
 * @author :breakpoint/赵立刚
 * @date : 2020/04/29
 */
public class SayHelloInterfaceImpl implements SayHelloInterface {
    @Override
    public void sayHello(String name) {
        System.out.println("hello:" + name);
    }
}


  1. 定义 SayHelloProxyHandler


/**
 * @author :breakpoint/赵立刚
 * @date : 2020/04/29
 */
public class SayHelloProxyHandler  implements InvocationHandler{
    private Object target;
    public SayHelloProxyHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("pre Code");
        Object returnValue =method.invoke(target,args);
        System.out.println("after Code");
        return returnValue;
    }
}


4.测试


/**
 * @author :breakpoint/赵立刚
 * @date : 2020/04/29
 */
public class Main {
    public static void main(String[] args) {
//        SayHelloInterface sayHelloInterface=new SayHelloInterfaceImpl();
//
//        SayHelloProxy proxy=new SayHelloProxy(sayHelloInterface);
//
//        proxy.sayHello("zhaoligang");
        SayHelloInterface sayHelloInterface=new SayHelloInterfaceImpl();
        SayHelloProxyHandler sayHelloProxyHandler=new SayHelloProxyHandler(sayHelloInterface);
        SayHelloInterface sayhello=(SayHelloInterface)Proxy.newProxyInstance(sayHelloInterface.getClass().getClassLoader(),
                sayHelloInterface.getClass().getInterfaces(),sayHelloProxyHandler);
        sayhello.sayHello("zhaoligang");
    }
}


5.运行结果


pre Code
hello:zhaoligang
after Code
Process finished with exit code 0


最后的效果是一样的。

具体有如下四步骤:

  • 通过实现 InvocationHandler 接口创建自己的调用处理器;
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

五、Spring的2种代理方式#


spring容器在创建我们的对象以及注入,使用的是动态代理实现的,怎么看呢?我们看下图:


22.jpg


根据上面的图,我们很明显的看到,Spring容器给我们的对象是代理对象

spring 里面一共有2种代理方式:

  • JDK代理
  • GClib代理方式


5.1 JDK代理#


情况与上面的相似

看一下handler


23.jpg



final class JdkDynamicAopProxy implements AopProxy,
 InvocationHandler, Serializable {


5.2 GCLIB代理#


  • cglib(Code Generation Library )是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
  • cglib封装了asm,可以在运行期动态生成新的class。
  • cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。


5.3 他们之间的区别#


  • 原理区别

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

  • 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
  • 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  • 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?

1.添加CGLIB库,SPRING_HOME/cglib/*.jar

2.在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

JDK动态代理和CGLIB字节码生成的区别?

  • JDK动态代理只能对实现了接口的类生成代理,而不能针对类
  • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
  • 因为是继承,所以该类或方法最好不要声明成final
相关文章
|
28天前
|
Java
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
|
2月前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
32 0
[Java]静态代理与动态代理(基于JDK1.8)
|
2月前
|
Java
Java访问外网图片地址时,如何添加代理?
【10月更文挑战第14天】Java访问外网图片地址时,如何添加代理?
45 2
|
2月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
91 1
|
2月前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
2月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
4月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
284 0
|
Java
Java学习路线-57:静态代理和动态代理
Java学习路线-57:静态代理和动态代理
112 0
Java学习路线-57:静态代理和动态代理
Java学习路线-57:静态代理和动态代理
|
Java
Java学习路线-57:静态代理和动态代理
Java学习路线-57:静态代理和动态代理
108 0
下一篇
DataWorks