Java学习笔记整理: 关于设计模式:代理模式 2024/7/10;

代理模式

自理解:通过带参构造器将需要扩展或使用的方法的类对象传入,重写并调用该对象方法(共同实现同一方法接口);

结构性模式

代理模式是什么

**代理模式的定义:**对其他对象提供一种代理以控制对这个对象的访问。

代理的作用

代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。

代理模式是一种设计模式,简单说即是在不改变源码 的情况下,实现对目标对象功能扩展

抽象角色(Subject):通过接口或抽象类声明真实角色实现的业务方法。

角色具有的行为放入该接口或抽象类总

代理角色(Proxy):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

真实角色(RealSubject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

静态代理

自理解:嵌套方法,通过一个被代理类和一个代理类,他们共同继承同一个接口,拥有共同的方法;代理类内声明私有变量接口,通过带参构造器将被代理类传入,重写接口方法并再调用代理类对象输出被代理类对象中的重写方法;

总结三种代理模式各有优缺点和相应的适用范围,主要看目标对象是否实现了接口。以Spring框架所选择的代理模式举例

在Spring的AOP编程中:如果加入容器的目标对象有实现接口,用JDK代理如果目标对象没有实现接口,用Cglib代理

其实这里做的事情无非就是,创建一个代理类SingerProxy,继承了ISinger接口并实现了其中的方法。只不过这种实现特意包含了目标对象的方法,正是这种特征使得看起来像是"扩展"了目标对象的方法。假使代理对象中只是简单地对sing方法做了另一种实现而没有包含目标对象的方法,也就不能算作代理模式了。所以这里的包含是关键。

**缺点:**这种实现方式很直观也很简单,但其缺点是代理对象必须提前写出,如果==接口层发生了变化==,代理对象的代码也要进行维护。如果能在运行时动态地写出代理对象,不但减少了一大批代理类的代码,也少了不断维护的烦恼,不过运行时的效率必定受到影响。这种方式就是接下来的动态代理。
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。

复制代码
 1  //声明共同实现的接口
     public interface ISinger {
     //接口方法
 2     void sing();
 3 }
 4 
 5 /**
 6  *  目标对象实现了某一接口
 7  */
 8 public class Singer implements ISinger{
     //被代理类对象 重写 接口 中的方法
 9     public void sing(){
10         System.out.println("唱一首歌");
11     }  
12 }
13 
14 /**
15  *  代理对象和目标对象实现相同的接口
16  */
17 public class SingerProxy implements ISinger{
18     // 接收目标对象,以便调用sing方法
19     private ISinger target;
        //带参构造器传入被代理对象
20     public UserDaoProxy(ISinger target){
21         this.target=target;
22     }
23     // 对目标对象的sing方法进行功能扩展
24     public void sing() {
25         System.out.println("向观众问好");
            //调用被代理对象方法
26         target.sing();
27         System.out.println("谢谢大家");
28     }
29 }
​
 1 /**
 2  * 测试类
 3  */
 4 public class Test {
 5     public static void main(String[] args) {
 6         //目标对象
 7         ISinger target = new Singer();
 8         //代理对象
 9         ISinger proxy = new SingerProxy(target);
10         //执行的是代理的方法
11         proxy.sing();
12     }
13 }

动态代理

自理解:通过JDK/Cglib拦截被代理类对象,并对其进行修改使代理类拥有所有被代理类实现的接口内方法;

JDK原理:被代理类需要接口,使用JDK自带方法Proxy获取代理对象 通过反射获取其方法;

Cglib原理:被代理类无须接口,引入Cglib通过指定方法获取代理对象.

动态代理是在程序运行时通过反射机制动态创建的。

JDK动态代理:

通过JDK自带的方法Proxy获取代理对象

涉及反射

调用Proxy类的静态方法newProxyInstance即可,该方法会返回代理类对象

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h ) 接收的三个参数依次为:

  • ClassLoader loader:指定当前目标对象使用类加载器,写法固定

  • Class<?>[] interfaces:目标对象实现的接口的类型,写法固定

  • InvocationHandler h:提供的执行被代理角色方法的方法。

缺点:
  • 可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,如果没有,则可以使用Cglib代理。
复制代码
 public interface ISinger {
     void sing();
     void sings();
 }
 
 /**
  *  目标对象实现了某一接口
  */
 public class Singer implements ISinger{
     public void sing(){
         System.out.println("唱一首歌");
     }  
     public void sings(){
         System.out.println("唱二首歌");
     }     
 }
​
public class Test{
     public static void main(String[] args) {
   //new被代理类对象
    Singer target = new Singer();
   //通过lambda创建代理类对象proxy
      ISinger proxy  = (ISinger) Proxy.newProxyInstance( target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {
         @Override
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println("向观众问好");
              //执行目标对象方法
              Object returnValue = method.invoke(target, args);
              System.out.println("谢谢大家");
              return returnValue;
              }
        });
   //输出方法,可直接调用被代理类对象中的方法并且经过代理类拦截修改后输出
        proxy.sing();
        proxy.sings();
     }
 }
//若不适应lambda则可以创建代理类--------------------------------------
//代理类
public class Behavior {
    //代理对象接口
    private SellHours sellHours;
    //带参构造器
    Behavior(SellHours sellHours){
        this.sellHours = sellHours;
    }
    //JDK内proxy方法:
    public Object getSellHours(){
        //指定当前目标对象使用类加载器,写法固定
        ClassLoader classLoader = sellHours.getClass().getClassLoader();
        //目标对象实现的接口的类型,写法固定
        Class<?>[] interfaces = sellHours.getClass().getInterfaces();
        //提供的执行被代理角色方法的方法
        InvocationHandler invocationHandler = new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //调用执行部分:
                System.out.println("进入动态代理:::");
                //通过反射获取对象方法
                Object invoke = method.invoke(sellHours);
                //返回值
                return invoke;
            }
        };
        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    };
}
//测试
public class Test {
    public static void main(String[] args) {
        //创建代理对象并赋值被代理类对象
        Behavior behavior = new Behavior(new Landlord());
        //使用代理对象内方法获取被代理类方法
        SellHours sellHours = (SellHours) behavior.getSellHours();
        //执行
        sellHours.getName();
    }
}

Cglib代理:

前提条件:
  1. 需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar

  2. 目标类不能为final

  3. 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

复制代码
/**
 * 目标对象,没有实现任何接口
 */
public class Singer{
​
    public void sing() {
        System.out.println("唱一首歌");
    }
}
/**
 * Cglib子类代理工厂:这里的代码也非常固定,只有部分是需要自己写出---------
 * 创建代理类并实现Cglib中的MethodInterceptor接口
 */
public class ProxyFactory implements MethodInterceptor{
    // 维护目标对象
    private Object target;
​
    public ProxyFactory(Object target) {
        this.target = target;
    }
​
    // 给目标对象创建一个代理对象
    public Object getProxyInstance(){,
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置被代理对象的父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建代理对象
        return en.create();
    }
    //重写Cglib中的方法
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        //调用执行部分:--------------------
        System.out.println("向观众问好");
        //通过反射执行目标对象的方法
        Object returnValue = method.invoke(target, args);
        System.out.println("谢谢大家");
        //返值
        return returnValue;
    }
}
/**
 * 测试类
 */
public class Test{
    public static void main(String[] args){
        //目标对象
        Singer target = new Singer();
        //代理对象
        Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();
        //执行代理对象的方法
        proxy.sing();
    }
}  
        
相关推荐
喵叔哟1 分钟前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生7 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
hopetomorrow20 分钟前
学习路之压力测试--jmeter安装教程
学习·jmeter·压力测试
hopetomorrow21 分钟前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
不是二师兄的八戒31 分钟前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
爱编程的小生43 分钟前
Easyexcel(2-文件读取)
java·excel
/**书香门第*/1 小时前
Cocos creator 3.8 支持的动画 7
学习·游戏·游戏引擎·游戏程序·cocos2d
带多刺的玫瑰1 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
美式小田1 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
猫爪笔记1 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html