静态代理和动态代理

静态代理和动态代理

此处观看更佳

1.什么是代理?

代理是指设计模式中的代理模式,如下是网上对代理模式的定义:

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

代理模式的主要优点有:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

其主要缺点是:

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

其实用人话来讲就是代理模式就是 消费者A要通过 中间商B才能去访问 提供者C,A通过B去访问C时,B还可以做一些其他的操作(像极了中间商赚差价),除此之外,A是通过B去访问C,这样屏蔽了对目标的真实对象的直接访问,可以对C起一个保护作用。

2.静态代理

静态代理就是对每个目标对象的代理都是程序员手动完成的,在程序编译运行之前就已经将代理类,真实对象等生成了class文件,且一一对应。

这样的坏处就是我们需要对每一个类创建一个代理类,实现增强功能,工作量大麻烦不说,维护起来也是相当麻烦的事情。

静态代理实现的步骤:

  1. 创建一个功能接口
package cuit.epoch.pymjl.proxydemo.staticproxy;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 15:39
 **/
public interface SmsService {
    /**
     * 发送短信
     * @param phone 手机号
     * @param msg 短信内容
     */
    void sendSms(String phone, String msg);
}
  1. 创建一个真实对象,去实现这个功能接口,这个真实对象的角色就是上文提到的 提供者C
	package cuit.epoch.pymjl.proxydemo.staticproxy.impl;

import cuit.epoch.pymjl.proxydemo.staticproxy.SmsService;
import lombok.extern.log4j.Log4j2;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 15:40
 **/
@Log4j2
public class SmsServiceImpl implements SmsService {
    @Override
    public void sendSms(String phone, String msg) {
        log.info("This is a target Object");
        log.info("Start send SMS:[{}],to:[{}]", msg, phone);
    }
}
  1. 再创建一个代理类,也实现这个接口,它的角色就是上文提到的 中间商B,这个中间商可以对其做增强功能,除了访问本来的服务以外,还可以去做一些其他的事情。
package cuit.epoch.pymjl.proxydemo.staticproxy.impl;

import cuit.epoch.pymjl.proxydemo.staticproxy.SmsService;
import lombok.extern.log4j.Log4j2;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 15:42
 **/
@Log4j2
public class ProxySmsServiceImpl implements SmsService {
    private final SmsService smsService;

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

    @Override
    public void sendSms(String phone, String msg) {
        log.info("This is a proxy Object,Before calling the method to send SMS, we can do something else");
        log.info("Proxy call the method to Send SMS to [" + phone + " ] with msg [" + msg + "]");
        smsService.sendSms(phone, msg);
        log.info("After calling the method to send SMS, we can do something else");
    }

    public SmsService getSmsService() {
        return smsService;
    }
}
  1. 编写主启动类,通过B去访问C,他就是 消费者A
package cuit.epoch.pymjl.proxydemo.staticproxy;

import cuit.epoch.pymjl.proxydemo.staticproxy.impl.ProxySmsServiceImpl;
import cuit.epoch.pymjl.proxydemo.staticproxy.impl.SmsServiceImpl;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 15:46
 **/
public class Main {
    public static void main(String[] args) {
        //创建Sms服务对象
        SmsService smsService = new SmsServiceImpl();
        // 创建一个代理对象
        SmsService proxy = new ProxySmsServiceImpl(smsService);
        //调用代理对象的方法
        proxy.sendSms("18888888888", "Hello i am a proxy");

    }
}
  1. 运行测试

image-20220421211900320

3.动态代理

相比于静态代理,动态代理更加灵活,我们不需要针对每一个目标就创建一个代理类,也不需要我们必须实现接口,我们可以直接实现代理类(cglib)。动态代理是在程序运行时生成字节码,并加载到JVM中的

Spring,RPC底层都用到了动态代理,而动态代理又依赖于反射实现。

对于Java而言,动态代理一般是有两种方式:一个是jdk的动态代理,另一个是cglib,二者各有差别

3.1.jdk动态代理

jdk的动态代理只能针对实现了接口的类进行代理,其核心就是需要实现 InvocationHandler接口,并重写 invoke 方法。invoke方法就是我们自定义的增强功能,我们可以通过 newProxyInstance()方法来创建一个代理类,然后这个代理类都会调用handler中的invoke方法,实现功能的增强。

jdk动态代理的实现步骤:

  1. 自定义一个handler,实现 InvocationHandler接口,重写 invoke方法。
package cuit.epoch.pymjl.proxydemo.dynamicproxy.jdk;

import lombok.extern.log4j.Log4j2;

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

/**
 * 自定义一个InvocationHandler,实现InvocationHandler接口,并实现invoke方法
 *
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 16:04
 **/
@Log4j2
public class MyInvocationHandler implements InvocationHandler {
    /**
     * This is the target object that we are invoking all methods on.
     */
    private final Object target;

    /**
     * Constructor for MyInvocationHandler.
     *
     * @param target the target object that we are invoking all methods on.
     */
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Before method invocation, we can do some pre-processing.
        log.info("Before invoking " + method.getName());
        //Invoke the method on the target object.
        Object result = method.invoke(target, args);
        //After method invocation, we can do some post-processing.
        log.info("After invoking " + method.getName());
        return result;
    }
}

invoke方法的参数介绍:

    /**
     * 当使用代理对象调用方法时实际上会调用到这个方法
     *
     * @param proxy  动态生成的代理类
     * @param method 代理类调用的目标对象的方法
     * @param args   当前方法的入参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    }
  1. 创建一个代理类工厂,生成代理对象
package cuit.epoch.pymjl.proxydemo.dynamicproxy.jdk;


import java.lang.reflect.Proxy;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 16:10
 **/
public class JdkProxyFactory {
    public static Object getProxy(Object target) {
                //1.Get the classloader of the proxy class
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                //2.Get the interface of the proxy class, you can specify multiple interface
                target.getClass().getInterfaces(),
                //3.Get the InvocationHandler of the proxy class
                new MyInvocationHandler(target));
    }
}

在这个工厂类中间我们使用 Proxy.newProxyInstance()方法生成代理类,这样通过代理类调用方法都会被转发到我们自定义的handler中的 invoke方法中。

newProxyInstance()三个参数的意义如下:

    /**
     * @param loader  指定代理对象的类加载器
     * @param interfaces 代理对象需要实现的接口,可以同时指定多个接口
     * @param h   handler,方法调用的实际处理者,代理对象的方法调用都会转发到这里。
     */
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  1. 编写主启动类测试
package cuit.epoch.pymjl.proxydemo.dynamicproxy.jdk;

import cuit.epoch.pymjl.proxydemo.staticproxy.SmsService;
import cuit.epoch.pymjl.proxydemo.staticproxy.impl.SmsServiceImpl;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 16:15
 **/
public class Main {
    public static void main(String[] args) {
        SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
        smsService.sendSms("12345678901", "hello");
    }
}

这里我们使用的上面的接口和类

  1. 测试结果

image-20220421214903166

3.2.cglib

cglib可以使我们不必实现某一个接口,直接代理某一个类,利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。jdk的动态代理是面向接口的,而cglib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。

如果要被代理的对象是个实现类(实现了某个接口),那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制)

如果要被代理的对象不是个实现类,那么Spring会强制使用CGLib来实现动态代理。

在cglib动态代理机制中 MethodInterceptor Enhancer类是核心

我们需要自己写一个实现类,实现 MethodInterceptor接口,并重写 intercept来拦截增强被代理的方法

  1. 先写一个服务类
package cuit.epoch.pymjl.proxydemo.dynamicproxy.cglib;

import lombok.extern.log4j.Log4j2;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 16:25
 **/
@Log4j2
public class AliSmsService {
    public String sendMessage(String phone, String message) {
        log.info("This is a target Object: AliSmsService");
        log.info("AliSmsService start send Message to [" + phone + "]: [" + message + "]");
        return "AliSmsService send Message to [" + phone + "]: [" + message + "] Success";
    }
}

  1. 再自定义一个实现类,实现 MethodInterceptor接口
package cuit.epoch.pymjl.proxydemo.dynamicproxy.cglib;

import lombok.extern.log4j.Log4j2;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 16:29
 **/
@Log4j2
public class MyMethodInterceptor implements MethodInterceptor {
    /**
     * @param o           proxied object
     * @param method      methods of the proxied object
     * @param args        Arguments to the methods of the proxied object
     * @param methodProxy A proxy for the method of the proxy object, used to call the original method
     * @return the result of the method of the proxied object
     * @throws Throwable if an exception is thrown by the method of the proxied object
     */

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        log.info("before method");
        Object result = methodProxy.invokeSuper(o, args);
        log.info("after method");
        return result;
    }
}
  1. 再通过 Enhancer类动态获取代理类
package cuit.epoch.pymjl.proxydemo.dynamicproxy.cglib;

import net.sf.cglib.proxy.Enhancer;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 16:36
 **/
public class CglibProxyFactory {
    public static Object getProxy(Class<?> clazz) {
        // 1.Create enhancers for creating dynamic proxy classes
        Enhancer enhancer = new Enhancer();
        // 2.set classloader
        enhancer.setClassLoader(clazz.getClassLoader());
        // 3.set superclass
        enhancer.setSuperclass(clazz);
        //4.Set up method interceptors
        enhancer.setCallback(new MyMethodInterceptor());
        //5.Create a proxy class object
        return enhancer.create();
    }
}
  1. 编写主启动类,进行测试
package cuit.epoch.pymjl.proxydemo.dynamicproxy.cglib;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 16:40
 **/
public class Main {
    public static void main(String[] args) {
        AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
        aliSmsService.sendMessage("123456789", "hello");
    }
}
  1. 测试结果

进行测试

package cuit.epoch.pymjl.proxydemo.dynamicproxy.cglib;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/4/19 16:40
 **/
public class Main {
    public static void main(String[] args) {
        AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
        aliSmsService.sendMessage("123456789", "hello");
    }
}
  1. 测试结果

image-20220421215958162

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pymj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值