Featured image of post Java代理模式

Java代理模式

Java代理模式相关学习

代理模式

代理模式是常用的java设计模式。

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

实现分为静态代理和动态代理两种实现方式

静态代理

简单来说代理模式就是将被代理类包装起来然后重新实现相同的方法,并且调用原来方法的同时可以在方法前后添加一个新的处理。而这种包装可以使用继承或者组合来使用。当我们调用的时候需要使用的是代理类的对象来调用而不是原来的被代理对象。

通过继承实现

通过继承被代理对象,重写被代理方法,可以对其进行代理。

优点:被代理类无需实现接口

缺点:只能代理这个类,要想代理其他类,要想代理其他类需要写新的代理方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package org.dm.vulhub.Ser;

public class Tank{
    public void move() {
        System.out.println("Tank moving cla....");
    }

    public static void main(String[] args) {
        new ProxyTank().move();
    }
}

class ProxyTank extends Tank{
    @Override
    public void move() {
        System.out.println("方法执行前...");
        super.move();
        System.out.println("方法执行后...");
    }
}

通过组合实现

定义一个 Movable 接口被代理类代理类都需要实现该接口。代理类需要将该接口作为属性,实例化时需要传入该接口的对象,这样该代理类就可以实现代理所有实现Movable的类了。

接口在这里的目的就是起一个规范作用保证被代理类和代理类都实现了move()方法。

优点:可以代理所有实现接口的类

缺点:被代理的类必须实现接口。

 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
package org.dm.vulhub.Ser;

public class Tank implements Movable{
    @Override
    public void move() {
        System.out.println("Tank moving cla....");
    }

    public static void main(String[] args) {
        Tank tank = new Tank();
        new LogProxy(tank).move();
    }
}

class LogProxy implements Movable{
    private Movable movable;
    public LogProxy(Movable movable) {
        this.movable = movable;
    }
    @Override
    public void move() {
        System.out.println("方法执行前....");
        movable.move();
        System.out.println("方法执行后....");
    }
}

interface Movable {
    void move();
}

动态代理

动态代理其实本质还是将被代理类包装一层,生成一个具有新的相同功能的代理类。但是与静态代理不同的是,这个代理类我们自己定义的。而动态代理这个代理类是根据我们的提示动态生成的。

相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

实现动态代理有三种方案

JDK动态代理

通过java提供的Proxy类帮我们创建代理对象

优点:可以生成所有实现了口的代理对象,并且最重要的是,可以对被代理类的所有方法做拦截处理

缺点:JDK反射生成代理必须面向接口, 这是由Proxy的原理决定的。生成代理的方法中你必须指定实现类的接口,它根据这个接口来实现代理类生成的所实现的接口。

 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
32
33
34
35
36
37
38
39
40
41
package org.dm.vulhub.Ser;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Tank implements Movable{
    @Override
    public void move() {
        System.out.println("Tank moving cla....");
    }

    public static void main(String[] args) {
        Tank tank = new Tank();
        Movable o = (Movable)Proxy.newProxyInstance(
            Tank.class.getClassLoader()
            ,new Class[]{Movable.class}
            ,new LogProxy(tank));
        o.move();
    }
}

class LogProxy implements InvocationHandler {
    private Movable movable;

    public LogProxy(Movable movable) {
        this.movable = movable;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法:"+method.getName()+"()执行前");
        Object invoke = method.invoke(movable, args);  // 此处相当于 movable.move()
        System.out.println("方法:"+method.getName()+"()执行后");
        return invoke;
    }
}

interface Movable {
    void move();
}

Proxy.newProxyInstance的传参是实现的关键

newProxyInstance(): 创建代理对象

参数一: 被代理类对象

参数二:接口类对象 被代理对象所实现的接口

参数三:调用处理器。 被调用对象方法被调用后该如何处理

JDK实现原理

我们可以在main函数的最开始添加这段代码

1
2
import sun.misc.ProxyGenerator;
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

main函数最后添加这段代码

1
2
3
4
5
        try {
            Thread.sleep(new Random().nextInt(90000));
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

然后直接执行,可以看到项目中出现了一些中间文件

image-20240803001417337

这个自动生成的class文件源码如下

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.dm.vulhub.Ser;

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

final class $Proxy0 extends Proxy implements Movable {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("org.dm.vulhub.Ser.Movable").getMethod("move");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void move() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

}

可以看到14-17行和每个函数的具体实现,是使用静态代理的组合方式进行的具体实现。

这也就解释了为什么需要被代理对象必须实现接口,如果没有实现接口,代理类就不能确定需要实现哪些方法进行方法层面的代理。

CGLIB动态代理

CGLib(Code Generate Library) 与JDK动态代理不同的是,cglib生成代理是被代理对象的子类。

因此它拥有继承方法实现静态代理的

优点:不需要被代理对象实现某个接口。

缺点:不能给final类生成代理,因为final类无法拥有子类。

使用cglib生成代理类也很简单,只要指定父类和回调方法即可

首先需要引入依赖

1
2
3
4
5
<dependency>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>3.2.12</version>
 </dependency>

然后完整代码

 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
32
package org.dm.vulhub.Ser;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

class TimeMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("生成的类名"+o.getClass().getName());
        System.out.println("生成的类的父类"+o.getClass().getSuperclass().getName());
        System.out.println("方法执行前,被代理的方法"+method.getName());
        Object result = null;
        result = methodProxy.invokeSuper(o, objects);
        System.out.println("方法执行后,被代理的方法"+method.getName());
        return result;
    }
}
class Tank{
    public void move(){
        System.out.println("Tank moving clacla....");
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer(); // 增强者
        enhancer.setSuperclass(Tank.class); // 指定父类
        enhancer.setCallback(new TimeMethodInterceptor()); // 当被代理对象的方法调用的时候会调用 该对象的intercept
        Tank tank = (Tank)enhancer.create();  // 动态代理的生成
        tank.move();  // 生成之后会调用
    }
}

SpringAop

待补充!!!!

Dan❤Anan
Built with Hugo
主题 StackJimmy 设计