lengjingbuliaoyidian
文章15
标签7
分类7

结构型模式

  • 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者则采用组合或聚合来组合对象。

由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。

  • 结构型模式分为一下7种:
  1. 代理模式
  2. 适配器模式
  3. 装饰者模式
  4. 桥接模式
  5. 外观模式
  6. 组合模式
  7. 享元模式

代理模式(中介商模式)

  • 概述:
    代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

Java中的代理按照代理类生成实际不同又分为静态代理和动态代理。静态代理类在编译时期就生成,而动态代理类则是在Java运行时动态生成,动态代理又有JDK代理和CGLib代理。

  • 结构:
    代理模式分为三种角色:
  1. 抽象主题类(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题类(RealSubject):实现抽象主题类中的具体业务,是代理对象所代表的真是对象,是最终要引用的对象。
  3. 代理类(Proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
  • 优缺点:

    1. 优点:
      • 代理模式在客户端与目标对象之间起到了一个中介作用和保护目标对象的作用
      • 代理对象可以扩展目标对象的功能
      • 代理模式能将客户端与目标对象分离,在一定程度上减低了系统的耦合性
    2. 缺点:
      • 代理模式会导致系统中类的数量增加,在一定程度上增加了系统的复杂度
      • 代理模式的性能可能会受到影响,因为代理对象需要额外的处理来转发请求
  • 使用场景:

  1. 远程代理
    2.防火墙代理
  2. 保护代理

静态代理

  • 实例:火车站售票
    • 场景:
      • 火车站售票系统中,售票窗口是客户端,而售票系统是服务器端。为了保护售票系统的安全,我们可以使用代理模式来实现。
    • 代码:
      • 抽象主题类(Subject):
        public interface SellTickets {
        void sell();
        }
      • 真实主题类(RealSubject):
        public class TrainStation implements SellTickets {
        @Override
        public void sell() {
        System.out.println("火车站售票");
        }
        }
      • 代理类(Proxy):
        public class TrainStationProxy implements SellTickets {
        //先声明对象
        private TraninStation traninStation = new TraninStation();
        @Override
        public void sell() {
        System.out.println("代理点收取一些服务费用");
        traninStation.sell();
        }
        }
      • 客户端:
        public class Client {

        //代理模式
        public static void main(String[] args) {
        ProxyPoint proxyPoint = new ProxyPoint();
        proxyPoint.sell();
        }
        }


        //结果:代理点收取一些服务费用 火车站卖票

动态代理——JDK代理模式

  • Java中提供了一个动态代理类Proxy,Proxy并不是上面我们上述所说的代理对象类,而是提供了一个创建代理对象的静态方法(newProxyInstance方法),来获取代理对象。

  • 实例:同样是上面的卖票案例,这次我们使用JDK动态代理模式来实现

  • 抽象主题类(Subject):

    public interface SellTickets {
    void sell();
    }

  • 真实主题类(RealSubject):

public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站售票");
}
}
  • 代理主题类(ProxyFactory):
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//获取代理对象的工厂类——代理类也实现了对应的接口
public class ProxyFactory {
//先声明一个目标对象
private TraninStation station = new TraninStation();

public SellTickets getProxyObject(){
//通过newProxyInstance返回代理对象(创建对象是接口形式的需要转化一下)
/**
* ClassLoader loader,类加载器,用于加载代理类,可以通过目标对象获取类加载器
*Class<?>[] interfaces,代理类实现的接口的字节码对象
*InvocationHandler h),代理对象的调用处理程序
*/
SellTickets proxyObject= (SellTickets) Proxy.newProxyInstance(
station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/**
* Object proxy,代理对象,和proxyObject对象是同一个对象,在invoke方法中基本不需要使用
* Method method,对接口中的方法进行封装的method对象
* Object[] args,调用方法的实际参数
*/

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("invoke方法执行了");
System.out.println("代售点收取手续费");
//执行对象的方法
Object obj= method.invoke(station, args);
return obj;
}

}
);
return proxyObject;
}
}
  • 测试类:

public class Client {
public static void main(String[] args) {
//获取代理对象
//1.创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//2.通过代理工厂对象获取代理对象
SellTickets proxyObject = factory.getProxyObject();
//3.调用方法
proxyObject.sell();
}
}
  • 注意:在’ProxyFactory’类中,getProxyObject()方法返回的是SellTickets接口,而不是TrainStation类。
    这是因为JDK动态代理模式是基于接口的代理,而不是基于类的代理。
    所以,代理对象只能实现与目标对象相同的接口,而不能继承目标对象的类。
    同时代理类是程序在运行过程中动态的在内存中生成的类。

  • 上述的执行流程:

    1. 在测试类中通过代理对象调用sell()方法
    2. 根据多态的特性,执行的是代理类($Proxy0)中的sell()方法,而不是TrainStation类中的sell()方法。
    3. 在$Proxy0类中,sell()方法的实现是通过InvocationHandler接口的子实现类对象的invoke()方法来完成的。
    4. 在invoke()方法中,通过反射机制调用执行真实所属类(TraninStation)目标对象中的sell()方法。

动态代理——CGLIB代理模式

  • CGLIB(Code Generation Library)是一个基于ASM的字节码生成类库(代码生成包,是第三方提供的包,所以需要时引入jar包),用于在运行时动态生成一个类。
  • 在pom.xml文件中引入CGLIB的依赖:
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
    public class TraninStation {

public void sell() {
System.out.println("火车站卖票");
}
}
    import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* @Description: 代理对象工厂,用来获取代理对象(目标对象所属的子类)
* @
* @
*/
public class ProxyFactory implements MethodInterceptor {
//声明火车站对象
private TraninStation traninStation = new TraninStation();
public TraninStation getProxyObject(){
//创建Enhancer对象,类似于JDK动态代理中的Proxy类
Enhancer enhancer = new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(TraninStation.class);
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TraninStation proxyObject = (TraninStation) enhancer.create();
return proxyObject;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//System.out.println("代理对象拦截方法");
System.out.println("代理对象拦截方法,执行目标对象的方法之前");
System.out.println("代售点收取一定的服务费用CGLIB");
//执行目标对象的方法
Object invoke = method.invoke(traninStation, objects);
return invoke;
}
}
    public class Client {
public static void main(String[] args) {
//创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//获取代理对象
TraninStation proxyObject =factory.getProxyObject();
//通过代理对象调用方法
proxyObject.sell();
}
//结果是:
//代理对象拦截方法,执行目标对象的方法之前
//代售点收取一定的服务费用CGLIB
//火车站卖票
}

总结:

jdk动态代理基于反射实现,cglib基于继承实现,所以cglib动态代理类是目标类的的子类,而jdk动态代理类是目标对象实现的接口的子类。

本文作者:lengjingbuliaoyidian
本文链接:http://example.com/2025/08/12/Java23%E7%A7%8D%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可