spring学习日记-day5-代理模式

一、学习目标

        代理模式是一种常用的设计模式,它主要用于在不修改原有对象代码的情况下,通过引入一个代理对象来控制对原有对象的访问,从而增强原有对象的功能。代理模式主要分为两种:静态代理和动态代理。尽管Spring框架本身更多地利用了动态代理来实现其功能,但理解静态代理是理解Spring代理机制的基础

  代理模式(Proxy Pattern)是程序设计中的一种重要设计模式,属于结构型设计模式。它主要通过为其他对象提供一个代理以控制对这个对象的访问,从而在客户端和目标对象之间起到中介的作用。  

        代理模式主要包含以下三个角色:

  1. 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  2. 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  3. 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用

        

        代理模式主要分为以下几种类型:

  1. 静态代理:在代码编译阶段就已经生成了代理类,代理类需要实现与目标对象相同的接口。静态代理在不修改目标对象的前提下对目标对象的方法进行增强,但需要为每个目标对象创建一个代理类,导致系统中类的数量增加,维护成本较高。
  2. 动态代理:在程序运行时,通过反射机制动态生成的代理类。Java中可以使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现动态代理。动态代理可以为多个目标对象创建一个代理类,减少了类的数量,并且更加灵活地处理不同的情况。但由于使用了反射机制,性能相对静态代理略低。
  3. CGLIB代理:CGLIB代理是通过继承目标对象的方式来创建代理类。它是第三方库(如Spring AOP)常用的一种代理方式,适用于没有实现接口的类。CGLIB代理不需要目标对象实现接口,可以代理任何类,但由于采用了继承的方式,目标类不能是final类,代理类也不能是final方法。相对于JDK动态代理,CGLIB代理的性能较高,但由于使用了字节码生成技术,可能会增加复杂性。

         优点

  1. 职责清晰:真实角色只关注实际的业务逻辑,而代理对象负责非业务逻辑的操作,使得编程更加简洁清晰。
  2. 降低耦合度:代理模式在一定程度上降低了系统的耦合度,提高了系统的扩展性。
  3. 保护作用:代理对象在客户端和目标对象之间起到中介的作用,可以保护目标对象不被直接访问。
  4. 增强功能:代理模式可以在不改变原始对象的情况下,为其增加额外的功能,如日志记录、权限校验等。

二、静态代理

1.定义一个接口,目标类和代理类都将实现这个接口

public interface Travel {
    //实现旅游功能
    public void travel();
}

2.目标类实现上述接口,定义业务逻辑

public class Spot implements Travel{
    //spot为旅游目的地,实现旅游功能
    public void travel() {
        System.out.println("接待游客");
    }
}

3代理类实现上述接口,并持有目标类的一个实例

public class Proxy implements Travel{
    private Spot spot;

    public Proxy(Spot spot) {
        this.spot = spot;
    }

    public void travel() {
        plan();
        ticket();
        spot.travel();
    }
    
    public void plan(){
        System.out.println("做攻略");
    }
    public void ticket(){
        System.out.println("订票");
    }
}

4.客户通过代理类来调用目标类

public class Client {
    public static void main(String[] args) {
        Spot spot=new Spot();
        Proxy proxy=new Proxy(spot);
        proxy.travel();
    }

}

5.优缺点

优点

1.实现简单,易于理解。

2.可以在不修改目标对象的前提下扩展目标对象的功能。

缺点

1.如果目标对象很多,需要为每一个目标对象创建一个代理类,这会导致类爆炸,增加系统的复杂度和维护成本。

2.如果接口增加方法,则目标对象和代理类都需要修改,这违反了开闭原则

三、动态代理

1.定义一个接口。

public interface Travel {
    //实现旅游功能
    public void travel();
}

2.创建一个实现了该接口的目标类。

public class Spot implements Travel{
    public void travel() {
        System.out.println("接待游客");
    }
}

3.创建一个实现了InvocationHandler接口的代理类,在invoke方法中编写增强逻辑。

// 定义一个实现了InvocationHandler接口的类,用于创建动态代理对象  
public class ProxyInvocationHandler implements InvocationHandler {  
    // 持有被代理对象的引用  
    private Travel travel;  
  
    // 设置被代理对象的方法  
    public void setTravel(Travel travel) {  
        this.travel = travel;  
    }  
  
    // 获取动态代理对象的方法  
    // 这里需要注意,获取代理对象的类加载器应该是被代理对象的类加载器,而不是ProxyInvocationHandler的类加载器  
    public Object getProxy(){  
        // 使用Proxy类的newProxyInstance方法创建代理对象  
        // 第一个参数:类加载器,加载代理类的ClassLoader  
        // 第二个参数:被代理对象实现的接口数组,代理类需要实现这些接口  
        // 第三个参数:实现了InvocationHandler接口的处理器对象  
        return Proxy.newProxyInstance(  
            this.travel.getClass().getClassLoader(), // 使用被代理对象的类加载器  
            travel.getClass().getInterfaces(), // 获取被代理对象实现的接口数组  
            this // 将当前对象作为InvocationHandler  
        );  
    }  
  
    // 实现InvocationHandler接口的invoke方法  
    // 该方法会在代理对象调用方法时被自动调用  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        // 在调用被代理对象的方法之前,执行一些前置操作,如做攻略和订票  
        plan();  
        ticket();  
  
        // 调用被代理对象的方法,使用反射机制  
        // method.invoke(被代理对象, 参数数组)  
        Object result = method.invoke(travel, args);  
  
        // 返回被代理对象方法的执行结果  
        return result;  
    }  
  
    // 前置操作:做攻略  
    public void plan(){  
        System.out.println("做攻略");  
    }  
  
    // 前置操作:订票  
    public void ticket(){  
        System.out.println("订票");  
    }  
}

4.使用Proxy.newProxyInstance方法创建代理对象。

 public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                travel.getClass().getInterfaces(), this);
    }

5.通过代理对象调用目标方法,自动执行增强逻辑。

public class Client {
    public static void main(String[] args) {
        Spot spot=new Spot();
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTravel(spot);
        Travel proxy=(Travel)pih.getProxy() ;
        proxy.travel();
    }

}

6.优缺点

优点

解耦:将切面逻辑与业务逻辑分离,降低了系统的耦合度。

灵活:可以在不修改目标对象代码的情况下,为目标对象添加额外的功能或逻辑。

易于维护:切面逻辑可以集中管理,便于维护和升级。

缺点

性能开销:动态代理的创建和调用过程中存在反射或字节码生成等开销,可能会影响系统性能。

限制:JDK动态代理只能代理实现了接口的类;CGLIB动态代理不能代理final修饰的类和方法。

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值