Java代理模式

78 阅读5分钟

代理模式

我们使用代理对象来代替真实对象的访问,这样可以在不修改原目标对象的前提下,提供额外的功能,扩展目标对象的功能。

代理模式的主要作用是扩展目标对象的功能。比如说在目标对象的某个方法的执行前后增加自定义操作。

静态代理

静态代理中,我们对目标对象的每个方法的增强都是手动完成的,不灵活且麻烦。

实现步骤: 1、定义一个接口及其实现类;

public interface SmsService {
    String send(String message);
}

2、创建一个代理类同样实现这个接口

public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

3、将目标对象注入进代理类

public class SmsProxy implements SmsService {

    private final SmsService smsService;

    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method send()");
        smsService.send(message);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method send()");
        return null;
    }
}

实际使用:

public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

其中,java的代理类通过接受一个接口的实现类进一步封装体现了java的多态。

动态代理

相比静态代理来说,动态代理更加灵活。不需要每个目标类都创建一个代理类,也不需要必须实现接口,我们呢可以直接代理实现类(CGLIB动态代理机制)

从JVM角度说,动态代理是在运行时动态生成类字节码,并加载到JVM中。

Spring AOP、RPC框架都依赖了动态代理。

JAVA动态代理用两种: 一是JDK动态代理,通过反射机制实现; 优点:基于接口易于理解。 缺点:只能代理实现类接口的类。 二是CGLIB动态代理,实现方式是字节码技术生成子类。 优点:不需要代理类必须实现接口,更加灵活。 缺点:基于字节码,比JDK动态代理性能开销大。

相比来说,CGLIB比JDK动态代理有更高的初始化开销,调用开销相差不大。

JDK动态代理

在Java动态代理机制中InvocationHandler接口和Proxy类是核心 Proxy类中使用频率最高的方法是: newProxyInstance(),这个方法主要用来生成一个代理对象。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgmentException
{
    ...
}

该方法有三个参数 loader 类加载器,用于加载代理对象 interfaces 被代理类实现的一些接口 h 实现类InvocationHandler接口的对象

要实现动态代理,还必须实现InvocationHandler 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

public interface InvocationHandler {

    /**
     * 当你使用代理对象调用方法的时候实际会调用到这个方法
     */
    public Object invoke(Object proxy, Method method, Object[] args)
    {
        ...
    }
}

invoke()有三个参数: proxy:动态生成的代理类 method:与代理类对象调用方法相对应 args:当前method方法参数 通过Proxy类的newProxyInstance()创建的代理对象在调用方法的时候,是机会调用实现InvocationHandler接口的类的invoke()方法。可以在invoke()方法中自定义处理逻辑。

JDK动态代理类使用步骤 1、定义一个接口及其实现类;

public interface SmsService {
    String send(String message);
}

2、自定义InvocationHandler并重写invoke方法,在invoke方法中我们会调用原生方法并自定义一些处理逻辑;

public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

3、定义一个JDK动态代理类,通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象

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

/**
 * @author shuang.kou
 * @createTime 2020年05月11日 11:23:00
 */
public class DebugInvocationHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private final Object target;

    public DebugInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return result;
    }
}

public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 目标类的类加载器
                target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
                new DebugInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
        );
    }
}



实际使用:

SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");

CGLIB动态代理机制

JDK动态代理致命问题在于只能代理实现了接口的类。

CGLIB使用步骤 1、定义一个类

package github.javaguide.dynamicProxy.cglibDynamicProxy;

public class AliSmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

2、自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 自定义MethodInterceptor
 */
public class DebugMethodInterceptor implements MethodInterceptor {


    /**
     * @param o           被代理的对象(需要增强的对象)
     * @param method      被拦截的方法(需要增强的方法)
     * @param args        方法入参
     * @param methodProxy 用于调用原始方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Object object = methodProxy.invokeSuper(o, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return object;
    }

}

3、通过 Enhancer 类的 create()创建代理类;

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new DebugMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}

实际使用:

AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");