Java动态代理

简介: 代理模式代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式来访问服务,代理类通常会持有一个委托类对象,代理类不会自己实现真正服务,而是通过调用委托类对象的相关方法,来提供服务,所以其实我们调用的还是委托类的服务,但是中间隔了一个代理类。这么做是有好处的,我们可以在访问服务之前或者之后加一下我们需要的操作。例如Spring的面向切面编程,我们可以在切入点之前执行一些操作,切入点之后执行一些操作。这个切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。根据

代理模式
代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式来访问服务,代理类通常会持有一个委托类对象,代理类不会自己实现真正服务,而是通过调用委托类对象的相关方法,来提供服务,所以其实我们调用的还是委托类的服务,但是中间隔了一个代理类。这么做是有好处的,我们可以在访问服务之前或者之后加一下我们需要的操作。例如Spring的面向切面编程,我们可以在切入点之前执行一些操作,切入点之后执行一些操作。这个切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

根据代理类的创建时间又可以分为:

静态代理
动态代理
两者的区别:

静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
但是不管是静态代理还是动态代理,他们都有一个共同点就是:都是通过接口进行代理。委托方通过接口去寻找代理方,而代理方也是通过去实现接口来完成代理对象的创建。并且代理方都会在方法中通过引用去调用委托方。

静态代理
​某个对象提供一个代理,代理角色固定,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。

静态代理的特点

目标角色固定

在应用程序执行前就得到目标角色

代理对象会增强目标对象的行为

有可能存在多个代理 引起"类爆炸"(缺点)

针对静态代理使用以下的例子:

委托方为People类
代理接口为ProxyInterface
静态代理类为StaticProxy
接下来我们直接看代码:

ProxyInterface代理接口:

public interface ProxyInterface {
String sing();
}
1
2
3
委托方People:

public class People implements ProxyInterface {

private String name;

public People(String name) {
    this.name = name;
}

public String sing(){
    System.out.println(name + "正在唱歌");
    return name+"表示感谢";
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
静态代理方StaticProxy

public class StaticProxy implements ProxyInterface{

//委托方引用
private People people;

public StaticProxy(People people) {
    this.people = people;
}

public String sing() {
    System.out.println("这里是代理对People的增强");
    return  people.sing();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
然后我们测试一下:

public class ProxyTest {
public static void main(String[] args) {
StaticProxy zsProxy = new StaticProxy(new People("张三"));
System.out.println(zsProxy.sing());
}
}
1
2
3
4
5
6
结果:

我们可以知道:

静态代理就是程序员在编写代码的时候就已经把代理类的源码写好了,编译后就会生成.class文件。

动态代理
相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。

动态代理的特点

目标对象不固定

在应用程序执行时动态创建目标对象

代理对象会增强目标对象的行为

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。然后通过反射在invoke方法中执行代理类的方法。在代理过程中,在执行代理类的方法前或者后可以执行自己的操作,这就是spring AOP的原理。

针对动态代理我们使用如下的例子:

代理接口ProxyInterface
委托方People
代理方使用Proxy.newProxyInstance方法动态生成
InvocationHandler回调处理程序的实现类DynamicProxy
其中代理接口ProxyInterface和委托方People的代码与前面一样,DynamicProxy的代码如下:

public class DynamicProxy implements InvocationHandler {

private final T target;

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

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

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (method.getName().equals("sing")) {
        System.out.println("这里是对sing方法动态代理的增强");
    }
    return method.invoke(target,args);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
测试代码:

public class ProxyTest {
public static void main(String[] args) {
// StaticProxy zsProxy = new StaticProxy(new People("张三"));
// System.out.println(zsProxy.sing());

    People zs = new People("张三");
    ProxyInterface proxyInterface = (ProxyInterface) Proxy.newProxyInstance(
            ProxyTest.class.getClassLoader(),
            new Class[]{ProxyInterface.class},
            new DynamicProxy<People>(zs)
    );
    System.out.println(proxyInterface.sing());
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
理解:
我们的InvocationHandler中涉及代理对象的引用和invoke方法,我们使用newProxyInstance创建代理对象的时候,传入代理接口就是为了对接口里面的方法进行拦截,然后全部替换成执行invoke方法。

结果:

CGLIB动态代理
CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。此外,因为 CGLIB 采用整型变量建立了方法索引,从而可以快速的查找对应的方法代理,这比使用 JDK 动态代理更快(使用 Java 反射技术创建代理类的实例)。

在底层实现上,CGLIB 使用字节码处理框架 ASM,该框架用于转换字节码并生成新的类。但是不鼓励直接使用 ASM,因为它要求对于 JVM 的内部结构包括 class 文件的格式和 JVM 指令都很熟悉,若一旦出现错误,会导致 JVM 崩溃级别的异常。

虽然说 CGLIB 与 JDK 动态代理相比,具有更好的优势,当并不是说使用 CGLIB 会更好,这里先总结下 CGLIB 和 JDK 动态代理之间的区别:

JDK 动态代理只能对接口进行代理,不能对普通的类进行代理,这是因为 - JDK 动态代理生成的代理类,其父类是 Proxy,且 Java 不支持类的多继承。
CGLIB 能够代理接口和普通的类,但是被代理的类不能被 final 修饰,且接口中的方法不能使用 final 修饰。
JDK 动态代理使用 Java 反射技术进行操作,在生成类上更高效。
CGLIB 使用 ASM 框架直接对字节码进行修改,使用了 FastClass 的特性。在某些情况下,类的方法执行会比较高效。
我们接下来看一个使用的例子:

首先我们导入相关依赖包:


cglib
cglib
2.2.2

1
2
3
4
5
我们的委托方还是前面的Person类对象张三,代码如下:

public class CglibTry {
public static void main(String[] args) {
final People zs = new People("张三");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(People.class);
enhancer.setCallback(new MethodInterceptor() {

        /**
         * @param o: 代理对象
            * @param method: 被代理方法
         * @param params: 方法入参
         * @param methodProxy: CGLIB方法
         **/
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            if (method.getName().equals("sing")) {
                System.out.println("这里是cglib对sing方法的动态增强");
            }
            return method.invoke(zs,objects);
        }
    });
    People cglibProxy = (People) enhancer.create();
    System.out.println(cglibProxy.sing());
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
结果:

当然cglib也可以代理接口,我们尝试一下:

public class CglibProxyInterfaceTry {
public static void main(String[] args) {
final People zs = new People("张三");
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(new Class[]{ProxyInterface.class});
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("sing")) {
System.out.println("这里是cglib对sing方法的动态增强");
}
return method.invoke(zs,objects);
}
});
ProxyInterface proxyInterface = (ProxyInterface) enhancer.create();
System.out.println(proxyInterface.sing());
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
区别在于:

最后使用enhancer.create()创建的对象是代理接口类型的,而不是委托方的类型
接口代理使用的是setInterfaces方法而不是setSuperclass方法(当然这个不绝对,我们可以看看下面的代码)
public class CglibProxyInterfaceTry {
public static void main(String[] args) {
// final People zs = new People("张三");
// Enhancer enhancer = new Enhancer();
// enhancer.setInterfaces(new Class[]{ProxyInterface.class});
// enhancer.setCallback(new MethodInterceptor() {
// public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// if (method.getName().equals("sing")) {
// System.out.println("这里是cglib对sing方法的动态增强");
// }
// return method.invoke(zs,objects);
// }
// });
// ProxyInterface proxyInterface = (ProxyInterface) enhancer.create();
// System.out.println(proxyInterface.sing());

    final People zs = new People("张三");
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(ProxyInterface.class);
    enhancer.setCallback(new MethodInterceptor() {
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            if (method.getName().equals("sing")) {
                System.out.println("这里是cglib对sing方法的动态增强");
            }
            return method.invoke(zs,objects);
        }
    });
    ProxyInterface proxyInterface = (ProxyInterface) enhancer.create();
    System.out.println(proxyInterface.sing());
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
也就是说setSuperclass代理接口也是可以的。

JDK动态代理源码分析
我们直接来看newProxyInstance方法:

此处得到安全管理器非重点,继续往下看,我们可以看到一个getProxyClass0方法:

这个方法非常重要,就是通过它来生成的代理类。

拿到这个代理类之后,通过这个代理类的构造方法并传入InvocationHandler对象来得到一个代理对象。

接着我们来看看代理类的产生方法getProxyClass0:

我们可以看到这个地方使用了缓存。如果由实现给定接口的给定加载器定义的代理类存在,则只返回缓存副本;否则,它将通过ProxyClassFactory创建代理类
————————————————
版权声明:本文为CSDN博主「十八岁讨厌编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyb18507175502/article/details/129353385

目录
相关文章
|
24天前
|
Java
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
|
2月前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
31 0
[Java]静态代理与动态代理(基于JDK1.8)
|
2月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
91 1
|
2月前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
2月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
5月前
|
缓存 Java 测试技术
day27:Java零基础 - 动态代理
【7月更文挑战第27天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
43 2
day27:Java零基础 - 动态代理
|
4月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
275 0
|
4月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
201 0
|
5月前
|
开发框架 Java Android开发
Java中的类反射与动态代理详解
Java中的类反射与动态代理详解
|
5月前
|
Java 数据安全/隐私保护
Java中的动态代理机制详解
Java中的动态代理机制详解