代理模式(静态代理和动态代理原理分析)

本文深入探讨代理模式,包括控制对象访问权限、静态与动态代理的原理和区别,以及JDK和CGLIB动态代理的实现。通过实例分析,展示了如何在不修改原有代码的情况下增加额外功能,如日志和缓存。同时,对比了代理模式与装饰者模式的差异,指出代理模式在运行时动态添加逻辑的特点。
摘要由CSDN通过智能技术生成

控制对象访问权限

  • 概念
  • 类图
  • 代码实例
  • 和装饰者区别

概念

为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象(委托类)中介作用。 代理类和委托类都有同样接口。

好处:可以不用动原来类的逻辑,再次增加一些功能,符合开闭原则。真正的业务还是交给被代理对象处理的,因此在其委托被代理对象处理业务前后实现一些公共逻辑服务,例如加入缓存或日志等功能,无须修改原来的类就可以使用代理进行实现。

类图

在这里插入图片描述

Subject: 代理类和被代理类实现同样的接口

Proxy:代理类,里面有被代理类,具体逻辑委托被代理类进行处理

RealSubject:被代理类,可以在其内做一些访问权限控制,额外的业务处理

Client:看到的是代理类,并不知道具体处理业务逻辑的类,降低耦合性

代码实例

UserDAO 代理和被代理的公共的接口(Subject)

/**
 * @author duanyimiao
 * @create 2018-09-22 10:47 AM
 * @description 用户数据持久化逻辑接口
 **/
public interface UserDAO {
    

    boolean insert(String name,int age);
}

UserDAOImpl 被代理类(RealSubject)

/**
 * @author duanyimiao
 * @create 2018-09-22 10:47 AM
 * @description 用户数据持久具体实现
 **/
public class UserDAOImpl implements UserDAO {
    

    @Override
    public boolean insert(String name, int age) {
    
        System.out.println("insert to database name="+name +" age="+age);
        return true;
    }
}

UserDAOProxyByInterface 代理类,通过实现接口方式实现代理方式(Proxy)

/**
 * @author duanyimiao
 * @create 2018-09-22 10:50 AM
 * @description 用户持久数据的代理类(和被代理类实现同个接口方式)
 **/
public class UserDAOProxyByInterface implements UserDAO {
    
    private UserDAOImpl conreteUserDAO;

    public UserDAOProxyByInterface() {
    

    }
    public UserDAOProxyByInterface(UserDAOImpl conreteUserDAO) {
    
        this.conreteUserDAO = conreteUserDAO;
    }
    @Override
    public boolean insert(String name, int age) {
    
        System.out.println("before insert handle some logic by interface");
        return conreteUserDAO.insert(name, age);
    }
}

UserDAOProxyByExtend 代理类,通过继承被代理类实现的代理方式(Proxy)

/**
 * @author duanyimiao
 * @create 2018-09-22 10:50 AM
 * @description 用户持久数据的代理类(继承被代理类方式)
 **/
public class UserDAOProxyByExtend extends UserDAOImpl {
    
    private UserDAOImpl conreteUserDAO;

    public UserDAOProxyByExtend() {
    

    }
    public UserDAOProxyByExtend(UserDAOImpl conreteUserDAO) {
    
        this.conreteUserDAO = conreteUserDAO;
    }
    @Override
    public boolean insert(String name, int age) {
    
        System.out.println("before insert handle some logic by extend");
        return conreteUserDAO.insert(name, age);
    }
}

Client 获取代理对象客户端(Client)

/**
 * @author duanyimiao
 * @create 2018-09-22 10:52 AM
 * @description
 **/
public class Client {
    
    public static void main(String[] args) {
    
        UserDAOImpl conreteUserDAO = new UserDAOImpl();
        UserDAOProxyByInterface userDAOProxyByInterface = new UserDAOProxyByInterface(conreteUserDAO);
        //和被代理类实现同个接口方式进行代理
        userDAOProxyByInterface.insert("dynamo", 18);

        //通过继承被代理类方式进行代理
        UserDAOProxyByExtend userDAOProxyByExtend = new UserDAOProxyByExtend(conreteUserDAO);
        userDAOProxyByExtend.insert("dynamo", 18);

    }
}

输出结果:

before insert handle some logic by interface
insert to database name=dynamo age=18
before insert handle some logic by extend
insert to database name=dynamo age=18

和装饰者模式区别

代理模式关注的是控制对对象的访问,一般具体的被代理对象对Client是不可见的,直接在创建代理对象时就创建了被代理对象,也就是编译时就确定了。 但是装饰者模式:更关注的是在一个对象方法上动态添加修饰逻辑,但是这些逻辑修饰在运行时才能确定的,通过构造方法传入具体的装饰者对象,执行时递归调用修饰对象的方法。

动态代理

静态代理和动态代理:

1、静态代理:代理类由程序员创建的然后编译成.class文件。但是其中缺点是,具有重复代码,灵活性不好,例如在执行接口A中所有方法之前加上日志逻辑,那么使用静态代理的话,在代理类中每个方法都得加,如果我想add* 开头方法加上一种逻辑,select* 开头方法加上另一种逻辑,那么就很难去实现和维护了,想解决以上困惑就要使用动态代理了。

2、动态代理:是在运行的时候,通过jvm中的反射进行动态创建对象,生成字节码对象(构造方法参数 InvocationHandler h类型),传入由我们实现InvocationHandler接口的对象,通过反射创建代理对象。 然后当调用代理对象的任何方法都会调用h中的 invoke(Object proxy,Method method,Object[] args)传入当前代理对象、当前调用的方法、方法参数值。

JDK动态代理实现原理

要求被代理类必须要实现接口,因为JDK动代理实现是通过实现接口方式来实现的。需要实现 InvocationHandler接口(在invoke方法中实现一些额外的逻辑,添加一些新功能),通过Proxy.newProxyInstance(ClassLoader classLoader,Class<?> interfaces,InvocationHandler h)。

实现步骤

1.定义一个接口ProxyObj

2.编写该接口I的实现类ProxyObjImpl

3.编写InvocationHandler接口的实现类InvokcationInvokeHandler,构造h类对象的时候可以把要代理的对象target传入,target完成实际的动作。在里面的invoke方法里编写自己想实现的逻辑,然后再调用实际要完成的动作就可以。

4.调用Proxy.newProxyInstance方法,传递的三个参数分别是代理类的类加载器(可以用Impl实例的getClass().getClassLoader())
、代理类要实现的接口列表(可以用Impl实例getClass().getInterfaces())、InvocationHandler实现类的实例。

这样就生成了 P r o x y 0 类 的 对 象 , 由 于 Proxy0类的对象,由于 Proxy0Proxy0类实现了ProxyObj接口,所以可以将对象强制转型成ProxyObj。

再说一下Proxy.newProxyInstance方法的实际过程:

1.使用传入的InvocationHandler实例参数将Proxy类的h实例初始化,注意,如果传入空对象的话,会抛出空指针错误,即h不能为空。

2.运行时生成代理Class,即$Proxy0

3.利用上面动态生成的$Proxy0类,构造出该类的对象,并返回。

实现代码

ProxyObj 代理类和被代理类实现的公共接口

public interface ProxyObj {
    

 public  void setName(String name);
}

ProxyObjImpl 被代理类

public class ProxyObjImpl implements  ProxyObj,ProxyObj1{
    

  private String name;

  public String getName() {
    
    return name;
  }

  public void setName(String name) {
    
    System.out.println("set name="+name);
    this.name = name;
  }

  @Override
  public void setAge(Integer age) {
    
    System.out.println("set age="+age);
  }
}

InvokcationInvokeHandler 实现InvocationHandler接口

public class InvokcationInvokeHandler implements InvocationHandler {
    
    //真实的对象
    private ProxyObj proxyPbj;

    public InvokcationInvokeHandler(ProxyObj proxy) {
    
        this.proxyPbj = proxy;
    }

    /**
     *
     * @param proxy jvm生成的动态代理对象 $Proxy0
     * @param method 当前代理对象调用的方法对象,是代理对象实现接口中的方法
     * @param args 调用对象传入的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
        System.out.println("before call set proxy" + proxy.getClass
  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值