代理模式小记

01,什么是代理模式?

  • 代理(Proxy)是设计模式的一种,提供了间接对被代理象进行访问的方式,当一个类没有被代理时,我们访问的方式是直接访问,当被代理之后,我们可以通过代理对象间接访问被代理对象,这样做的好处是,可以在被代理对象实现的功能上通过代理对象增加额外的功能
  • 举个现实生活中的例子:假设我们买一台电脑,我们肯定不会直接去厂家买,而是去旗舰店或者体验店去卖,这里的工厂就相当于一个被代理的类,旗舰店或者体验店就相当于代理类
    在这里插入图片描述
  • 代理模式分为:静态代理模式和动态代理模式
  • 动态代理模式又可分为两种:基于接口的动态代理,基于子类代理的动态代理(Cglib代理)

02 ,静态代理

  • 在使用静态代理时,被代理对象与代理对象需要一起实现相同的接口或者是继承相同父类,因此要定义一个接口或抽象类
  • 定义一个生产者的接口
package com.aismall.staticProxy;

/*模拟生产的接口*/
public interface IProducer {
    /*销售*/
    public void saleProduct(float money);
}

  • 定义生产者的实现类
package com.aismall.staticProxy;

/*模拟生产厂家类,实现生产接口*/
public class Pruducer implements IProducer {
    /*销售*/
    public void saleProduct(float money){
        System.out.println("销售产品,并拿到钱"+money);
    }
}

  • 为了代理这个生产者的实现类,我们需要创建一个代理类,并且还要实现被代理类实现的接口
package com.aismall.staticProxy;

/*模拟代理类,实现生产接口*/
public class ProxyShop implements IProducer {
	//创建一个接口形式的变量,用来接收实现类的实例(多态的思想)
    private IProducer producer;
    public ProxyShop(){
        super();
    }
    //有参构造,要注意这个形参,也是接口类型的变量
    public  ProxyShop(IProducer producer) {
        super();
        //变量赋值
        this.producer=producer;
    }

    /*销售*/
    public void saleProduct(float money){
    	//方法增强的部分
    	enhanceMethod();
        //被代理的方法
        producer.saleProduct(1000*0.8f);
    }

    public void enhanceMethod(){
        System.out.println("产品被代理商售出,代理商扣除了20%的费用");
    }
}
  • 编写测试类
package com.aismall.staticProxy;

public class ststicProxyTest {
    public static void main(String[] args) {
    	//创建一个生产厂家的实例
        IProducer producer=new Pruducer();
        //创建一个代理商的实例,生厂家的实例作为参数传递
        ProxyShop proxyShop=new ProxyShop(producer);
        //调用销售方法(此方法内部调用了生产厂家的销售方法并对其进行了增强)
        proxyShop.saleProduct(1000);
    }
}

  • 运行结果
产品被代理商售出,代理商扣除了20%的费用
厂家拿到钱800.0
  • 分析
本来厂家一台电脑可以买1000元,由代理商代买之后,代理商扣除了20%
的费用,最后厂家得到了800,这个扣除费用的功能,就是对saleProduct
方法的增强
  • 静态代理总结:
    优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.
    缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,同时,一旦接口增加方法,目标对象与代理对象都要维护。
  • 而动态代理方式可以解决上面的问题

03,动态代理

  • 动态代理:
    特点:字节码随用随创建,随用随加载
    作用:不修改源码的基础上对方法进行增强
  • 分类:
    基于接口的动态代理:JDK动态代理
    基于子类的动态代理:CGLIB动态代理

3.1,基于接口的动态代理

  • 基于接口的动态代理
    涉及的类:Proxy
    提供者:JDK官方
  • 如何创建代理对象:
    使用Proxy类中的newProxyInstance方法
  • 创建代理对象的要求:
    被代理类至少实现一个接口,如果没有则不能使用,为什么有这条规则,我们可以通过查看newProxyInstance方法的源码得知:
 public static Object newProxyInstance(ClassLoader loader,
                                       Class<?>[] interfaces,
                                       InvocationHandler h)
        throws IllegalArgumentException
    {.....}
  • newProxyInstance方法的参数介绍:

    • ClassLoader:用于加载被代理对象的字节码,写的是被代理对象的类加载器或者和被代理对象使用相同的类加载器,固定写法
    • Class[]:字节码数组,用于加载被代理对象所实现的接口的字节码,目的是让代理对象和被代理对象拥有相同的方法(因为java类可以实现多个接口,所以这里使用字节码数组,固定写法就是因为这个参数被代理对象不实现接口就无法使用这种动态代理方式
    • InvocationHandler接口:可以在此接口的实现类书写用于增强的代码,它是写如何代理,这里通常情况下都是使用匿名内部类的方式
    • InvocationHandler接口中只有一个方法:invoke

      作用:执行被代理对象的任何接口方法都会经过该方法

    • public Object invoke(Object proxy, Method method, Object[] args)
    • invoke方法参数介绍:
      • proxy:代理对象的引用
      • method:当前执行的方法的对象的引用
      • args:当前执行的方法所需要的参数
      • return:和被代理对象相同的返回值
  • 定义一个生产者接口

package com.aismall.dynamicProxy;

/*模拟生产的接口*/
public interface IProducer {
    /*销售*/
    public void saleProduct(float money);
}

  • 定义一个生产者接口的实现类
package com.aismall.dynamicProxy;
/*模拟生产厂家*/
public class Pruducer implements IProducer {
    /*销售*/
    public void saleProduct(float money){
        System.out.println("销售产品,并拿到钱"+money);
    }
}

  • 定义代理类并实现销售功能
package com.aismall.dynamicProxy;

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

public class proxyShop {
    public static void main(String[] args) {
     //创建一个生产工厂的实例
     final Pruducer pruducer=new Pruducer();
     // 调用Proxy类中的静态方法:newProxyInstance
     IProducer proxyProducer=(IProducer) Proxy.newProxyInstance(pruducer.getClass().getClassLoader(),
                pruducer.getClass().getInterfaces(), new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*在此提供增强的代码*/
                        //创建一个用于返回的对象变量
                        Object returnValue=null;
                        //1.获取方法执行的参数
                        Float money=(Float)args[0];
                        //2.判断方法是不是销售方法
                        if("saleProduct".equals(method.getName())){
                            returnValue=method.invoke(pruducer,0.8f*money);
                        }
                        return returnValue;
                    }
                });
        proxyProducer.saleProduct(1000);
    }
}

  • 运行结果
销售产品,并拿到钱800.0
  • 总结:
    代理对象不需要实现接口,但是被代理对象一定要实现接口,否则不能使用这种动态代理方式,因此这也算是这种方式的缺陷。

3.2,基于子类的动态代理(cglib代理)

  • 基于子类的动态代理:
    涉及的类:Enhancer
    提供者:第三方cglib库
<packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_3</version>
        </dependency>
    </dependencies>
  • 如何创建代理对象:

    • 使用Enhancer类中的create方法
    • 源码如下:
    public static Object create(Class type, Callback callback) {
            Enhancer e = new Enhancer();
            e.setSuperclass(type);
            e.setCallback(callback);
            return e.create();
        }
    
  • 创建代理对象的要求:

    • 被代理类不能是最终类(也就是没有被final修饰的类)
  • create方法的参数介绍:

    • Class:字节码,用于指定被代理对象的字节码,固定写法
    • Callback接口:在此接口的实现类中书写用于增强的代码,我们一般使用它的子接口MethodInterceptor接口
    • MethodInterceptor接口中只有一个方法intercept
    • public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
    • intercept方法:

      作用:执行被代理对象的任何接口方法都会经过该方法

    • intercept方法参数介绍:
      • o:代理对象的引用
      • method:当前执行的方法的对象的引用
      • objects:当前执行的方法所需要的参数
      • methodProxy:当前执行方法的代理对象
      • return:和被代理对象相同的返回值
  • 定义一个生产者类

package com.aismall.dynamicProxy;
/*模拟生产厂家*/
public class Pruducer {
    /*销售*/
    public void saleProduct(float money){
        System.out.println("销售产品,并拿到钱"+money);
    }
}

  • 定义代理类并实现销售功能
package com.aismall.cglibProxy;

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

public class proxyShop {
    public static void main(String[] args) {
       final Pruducer pruducer=new Pruducer();
        Pruducer cglibProducer= (Pruducer) Enhancer.create(pruducer.getClass(), new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //在此书提供增强的代码
                Object returnValue=null;
                //1.获取方法执行的参数
                Float money=(Float)objects[0];
                //2.判断方法是不是销售方法
                if("saleProduct".equals(method.getName())){
                    returnValue=method.invoke(pruducer,0.8f*money);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(1000);
    }
}

  • 运行结果
销售产品,并拿到钱800.0
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Protobuf是一种高效的序列化协议,可以用于数据交换和数据存储。它的主要优势是大小小,速度快,可扩展性强。下面是使用Protobuf的一些小记: 1. 定义消息格式 首先,需要定义消息格式,以便Protobuf可以将数据序列化和反序列化。消息格式定义在.proto文件中,使用protobuf语言编写。例如,下面是一个简单的消息格式定义: ``` syntax = "proto3"; message Person { string name = 1; int32 age = 2; } ``` 这个消息格式定义了一个名为Person的消息,包含两个字段:name和age。 2. 生成代码 一旦消息格式定义好,就可以使用Protobuf编译器生成代码。编译器将根据消息格式定义生成相应的代码,包括消息类、序列化和反序列化方法等。可以使用以下命令生成代码: ``` protoc --java_out=. message.proto ``` 这将生成一个名为message.pb.java的Java类,该类包含Person消息的定义以及相关方法。 3. 序列化和反序列化 一旦生成了代码,就可以使用Protobuf序列化和反序列化数据。例如,下面是一个示例代码,将一个Person对象序列化为字节数组,并将其反序列化为另一个Person对象: ``` Person person = Person.newBuilder() .setName("Alice") .setAge(25) .build(); byte[] bytes = person.toByteArray(); Person deserializedPerson = Person.parseFrom(bytes); ``` 这个示例代码创建了一个Person对象,将其序列化为字节数组,然后将其反序列化为另一个Person对象。在这个过程中,Protobuf使用生成的代码执行序列化和反序列化操作。 以上是使用Protobuf的一些基本步骤和注意事项,希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

彤彤的小跟班

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

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

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

打赏作者

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

抵扣说明:

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

余额充值