Java静态代理、动态代理

一、代理模式

代理模式是通过创建代理类(Proxy)的方式来访问服务,代理类通常会有一个委托类对象,代理类不会自己实现真正服务,而是通过委托类对象的相关方法来提供服务。可以用来实现被代理类的功能增强,而不入侵被代理类的源代码。

二、静态代理

对目标对象的每个方法的增强都是手动完成的,每个目标对象对应一个代理类,这样会产生过多的代理类;并且当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。

代码展示:

  1. 定义发送短信的接口
public interface SmsService {
    String send(String message);
}
  1. 实现发送短信的接口
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println(" send message : " + message);
        return message;
    }
}
  1. 创建代理类并同样实现发送短信的接口
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 message;
    }
}
  1. 实际使用
public class Test {
    public static void main(String[] args) {
        // 静态代理
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

执行结果:

静态代理 before method send()
 send message : java
静态代理 after method send()

三、动态代理

代理类在程序运行时创建的代理方式被成为动态代理。动态代理相较于静态代理的优势在于可以很方便的对代理类的所有函数进行统一管理,不需要针对每个目标类都单独创建一个代理类。

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

3.1 JDK动态代理

3.1.1 介绍

在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。

Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。

	/**
    * 作用:主要用来生成一个代理对象
    * 
    * @param loader  类加载器,用于加载代理对象
    * @param interfaces  被代理类实现的一些接口
    * @param h  实现了 InvocationHandler 接口的对象
    **/
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        .......
    }

当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

public interface InvocationHandler {
    /**
    * 作用:主要用来生成一个代理对象
    * 
    * @param proxy   动态生成的代理类
    * @param method   与代理类对象调用的方法相对应(反射得到的方法)
    * @param args   当前 method 方法的参数
    **/
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

总结:通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。 你可以在 invoke() 方法中自定义处理逻辑,比如在方法执行前后做什么事情。

3.1.2 使用步骤

  • 定义一个接口及其实现类;
  • 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  • 通过 Proxy.newProxyInstance(ClassLoader loader,Class\<?\>[] interfaces,InvocationHandler h) 方法创建代理对象;

3.1.3 代码展示:

  1. 定义发送短信的接口
public interface SmsService {
    String send(String message);
}
  1. 实现发送短信的接口
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("真实的 send message : " + message);
        return message;
    }
}
  1. 定义一个JDK动态代理类
public class MyInvocationHandler implements InvocationHandler {
    // 代理类中的真实对象
    private final Object target;
 
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
 
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException,IllegalAccessException {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("JDK动态代理 before method " + method.getName());
        Object result = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("JDK动态代理 after method " + method.getName());
        return result;
    }
}
  1. 实际使用
public class TestJDkProxy {

    public static void main(String[] args) {

        SmsServiceImpl si = new SmsServiceImpl();
        Object obj = Proxy.newProxyInstance(
                si.getClass().getClassLoader(), // 目标类的类加载
                si.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
                new MyInvocationHandler(si)   // 代理对象对应的自定义 InvocationHandler
        );
        SmsService smsService1 = (SmsService) obj;
        smsService1.send("Java!");

    }

}

执行结果:

JDK动态代理 before method send
真实的 send message : Java!
JDK动态代理 after method send

3.2 CGLib动态代理

3.2.1 介绍

在 CGLIB 动态代理机制中 MethodInterceptor 接口和 Enhancer 类是核心。

JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。

需要自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法。

public interface MethodInterceptor extends Callback{

    /**
    * 拦截被代理类中的方法
    *
    * @param obj:被代理的对象(需要增强的对象)
    * @param method:被拦截的方法(需要增强的方法)
    * @param args:方法入参
    * @param proxy:用于调用原始方法
    **/
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}

3.2.2 使用步骤

  • 定义一个类;
  • 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  • 通过 Enhancer 类的 create()创建代理类;

3.2.3 代码展示

  1. 实现一个发送短信的类
public class SmsService {

    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
  1. 自定义 MethodInterceptor(方法拦截器)
public class MyMethodInterceptor  implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Object object = methodProxy.invokeSuper(o, objects);
        // 调用invoke会死循环
//        Object object = methodProxy.invoke(o, objects);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return object;
    }
  1. 实际使用
public class TestCGLibProxy {

    public static void main(String[] args) {

        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(SmsService.class.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(SmsService.class);
        // 设置方法拦截器
        enhancer.setCallback(new MyMethodInterceptor());
        // 创建代理类
        Object  proxyObject = enhancer.create();
        // 执行方法
        SmsService smsService = (SmsService)  proxyObject;
        smsService.send("CGLibProxy");

    }

}

执行结果

before method send
send message:CGLibProxy
after method send

四、AOP

Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×