Skip to content

代理模式是什么?

代理模式也称为委托者模式,属于结构型设计模式,代理模式通常是提供一个中间人,用于解决两类问题:

  • 控制对目标对象的访问
  • 在访问目标对象时增加额外功能

‼️本质是通常是提供一个中间人,以控制目标对象的访问。

什么是静态代理?

静态代理是指代理关系在编译期确定的代理模式。使用静态代理时,通常的做法是为每个业务类抽象一个接口,对应地创建一个代理类。

java
1、定义基础接口
public interface HttpApi {
    String get(String url);
}

2、网络请求的真正实现
public class RealModule implements HttpApi {
     @Override
     public String get(String url) {
         return "result";
     }
}

3、代理类
public class Proxy implements HttpApi {
    private HttpApi target;

    Proxy(HttpApi target) {
        this.target = target;
    }

    @Override
    public String get(String url) {
        // 扩展的功能
        Log.i("http-statistic", url);
        // 访问基础对象
        return target.get(url);
    }
}

可以看出,当需要频繁使用代理模式时,我们需要创建很多代理类,需要创建很多重复的代码。

java 动态代理

java 的动态代理是 jdk 1.3 中引入的特性,“动态”是相对于“静态”代理而言,代理类不是在编译期生成的,而是在运行时生成。 java 动态代理的核心 API 是 Proxy 类和InvocationHandler 接口。

  • **InvocationHandler** :创建动态代理示例必须提供代理处理器代理处理器是实现了InvocationHandler 接口的类的实例,这个接口只有一个invoke方法,调用代理对象的方法实际上会传递给 invoke方法,方法签名如下:Object invoke(Object proxy, Method method, Object[] args)
  • **Proxy**: 创建代理对象需要使用 Proxy 类的newProxyInstance方法,方法接受参数如下:
    • ClassLoader loader:类加载器通常就是接口类的ClassLoader
    • Class<?>[] interfaces:代理类实现的接口数组,
    • InvocationHandler h:调用处理器实例

我们来看一个简单的例子:

java
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 接口
interface hello {
    void hi();
}

//  原对象
class helloOrigin implements hello {
    @Override
    public void hi() {
        System.out.println("hi!");
    }
}

// 调用处理器
class proxyHandler implements InvocationHandler {
    hello target;

    public proxyHandler(hello target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy !");
        return method.invoke(target, args);
    }
}


public class DynimicProxyTest {

    @Test
    public void test() {
        // 创建原对象类
        hello originBean = new helloOrigin();
        originBean.hi();

        // 创建代理类
        hello proxyBean = (hello) Proxy.newProxyInstance(hello.class.getClassLoader(),
                                                         new Class[]{hello.class},
                                                         new proxyHandler(originBean));
        proxyBean.hi();
    }
}

💡注意:只有接口,没有实现类的情况下, java 动态代理也可以直接在运行期创建接口对象:

java
public class Main {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
}

并不存在可以直接实例化接口的黑魔法,动态代理其实就是JVM帮我们自动编写了一个上述类(不需要源码,可以直接生成字节码),把上面的动态代理改写为静态实现类大概长这样:

java
public class HelloDynamicProxy implements Hello {
    InvocationHandler handler;
    public HelloDynamicProxy(InvocationHandler handler) {
        this.handler = handler;
    }
    public void morning(String name) {
        handler.invoke(
           this,
           Hello.class.getMethod("morning", String.class),
           new Object[] { name });
    }
}

CGLIB 动态代理

java 动态代理要求代理类必须实现某个接口,无法代理没有实现接口的类,存在一定的局限。 CGLIB是一个基于字节码的代码生成库,可以用来生成动态代理对象,与Java标准库中的动态代理相比,CGLIB动态代理所生成的代理对象是基于继承的方式,而不是基于接口的,所以也叫子类代理。很多流行的框架如HiberateSpring 等很多框架都使用CGLIB库。 maven 中引入CGLIB:

xml
<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.2.4</version>
</dependency>

首先先创建一个被代理的目标类:(注意目标代理类和目标代理方法不能是被 final 修饰的,因为 CGLIB动态代理是通过生成目标类的子类来实现代理,而 final 修饰的方法无法被重写)。

java
public class PersonService {
    public String sayHello(String name) {
        return "Hello " + name;
    }

    public Integer lengthOfName(String name) {
        return name.length();
    }
}

接着我们使用cglibEnhancer类创建一个代理:

java

Enhancer enhancer = new Enhancer();
// 设置目标类,可以是类或接口
enhancer.setSuperclass(PersonService.class);
// 设置拦截方法
enhancer.setCallback((FixedValue) () -> "Hello Tom!");
// 创建代理类
PersonService personService = (PersonService) enhancer.create();
System.out.println(personService.sayHello("lm"));

// 打印
// "Hello Tom!"

使用setSuperclass方法指定被代理类; 使用setCallback指定回调方法,当调用代理类的目标方法时会交由回调方法执行,上边代码例子使用FixedValue进行强制类型转换,表示回调方法直接返回一个值。 我们还可以使用setCallback指定更复杂的回调方法,比如说根据不同的被拦截的方法,采取不同的处理策略:

java
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    if (method.getName().equals("sayHello")) {
        return "Hello Tom!";
    } else {
        return proxy.invokeSuper(obj, args);
    }
});

上边例子中,我们拦截了目标类PersonServicesayHello方法, 当调用sayHello方法时直接返回"Hello Tom!"。注意MethodInterceptor是用于进行类型转换的。