gof23——代理模式详解

一、代理模式和结构型设计模式

代理模式(Proxy Pattern)是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标之前起到了中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一是保护目标对象,而是增强目标对象。

要弄清楚代理模式,首先我们需要了解什么是结构型设计模式。结构型设计模式存在的目的主要是:在解决了对象的创建问题之后,对象的组成和对象之间的依赖关系就成了开发人员关注的焦点。因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等等。因此,结构型设计模式最主要涉及和关心的是如何组合类和对象来获得更大的结构。结构型设计模式采用继承机制来组合接口火实现(也称为类结构型模式),或者通过组合一些对象从而实现一些新的功能(也称为对象结构型模式)。

二、静态代理

1.静态代理使用的前提:
  • 1.代理类和被代理类继承同一个接口
  • 2.代理类中持有被代理类对象
  • 3.由代理类来控制被代理类的行为(即类的方法)
2.代码示例

以买票的行为为例,现在我们买票可以直接在12306上进行购票,也可以通过第三方平台如智行等进行购票

购票接口:

public interface BuyTicket {
    /**
     * 购票
     */
    void buyTicket();
}

被代理对象:

public class User implements BuyTicket{
    /**
     * 购票
     */
    @Override
    public void buyTicket() {
        System.out.println("用户:请求购票");
    }

    public void offerInfo(){
        System.out.println("用户:提供购票需要的个人信息");
    }
}

代理对象:

public class BuyTicketProxy implements BuyTicket{
    private User user;
    public BuyTicketProxy(User u){
        user=u;
    }
    /**
     * 购票
     */
    @Override
    public void buyTicket() {
        user.buyTicket();
        System.out.println("代理平台:请求获取用户信息");
        user.offerInfo();
        System.out.println("代理平台:购票完成!!!");
    }
}

客户端:

public class Client {
    public static void main(String[] args) {
        BuyTicketProxy buyTicketProxy=new BuyTicketProxy(new User());
        buyTicketProxy.buyTicket();
    }
}

从以上代码可以看出,代理平台在进行购票时,实际上仍然是调用用户的功能逻辑,用户自身并不需要关心自己的方法何时使用,交由代理平台控制。

三、静态代理在业务上的应用

在分布式业务中 ,通常会对数据库进行分库分表操作,分库分表之后使用Java操作时就可能需要配置多个数据源,我们通过设置数据源路由来动态切换数据源。

创建订单类:

@Getter
@Setter
@ToString
public class Order {
    private String id;
    private Object orderInfo;
    private Long createTime;

}

创建OrderDao持久层操作

public class OrderDao {
    public int insert(Order order) {
        System.out.println("OrderDao 创建Order成功");
        return 1;
    }
}

创建IOrderService接口

public interface IOrderSerivce {
    /**
     * 创建Order
     * @param order order
     * @return int
     */
    public int createOrder(Order order);
}

创建OrderService实现类

public class OrderService implements IOrderSerivce{
    private OrderDao orderDao;

    public OrderService(){
        orderDao=new OrderDao();
    }


    @Override
    public int createOrder(Order order){
        System.out.println("OrderService调用OrderDao创建订单");
        return orderDao.insert(order);
    }
}

创建数据源路由对象,是与哦给你ThreadLocal的单例对象实现DynamicDataSourceEntry类

public class DynamicDataSourceEntry {
    /**
     * 默认数据源
     */
    public final static String DEFAULT_SOURCE=null;

    private final static ThreadLocal<String> LOCAL=new ThreadLocal<>();

    private DynamicDataSourceEntry(){}

    /**
     * 清空数据源
     */
    public static void clear(){
        LOCAL.remove();
    }

    /**
     * 获取数据源
     * @return 数据源
     */
    public static String get(){
        return LOCAL.get();
    }

    /**
     * 还原当前切换的数据源
     */
    public static void restore(){
        LOCAL.set(DEFAULT_SOURCE);
    }

    /**
     * 设置数据源
     * @param dataSource 数据源
     */
    public static void set(String dataSource){
        LOCAL.set(dataSource);
    }

    /**
     * 根据年份动态设置数据源
     * @param year
     */
    public static void set(int year){
        LOCAL.set("DB_"+year);
    }

}

创建OrderService的静态代理类,主要完成的功能时根据订单创建时间自动按年份选择数据库。

public class OrderServiceStaticProxy implements IOrderSerivce{
    private IOrderSerivce orderService;

    private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy");

    public OrderServiceStaticProxy(IOrderSerivce orderService){
        this.orderService=orderService;
    }
    /**
     * 创建Order
     *
     * @param order order
     * @return int
     */
    @Override
    public int createOrder(Order order) {
        System.out.println("Proxy before method");
        Long time=order.getCreateTime();
        int dbRouter= Integer.parseInt(yearFormat.format(new Date(time)));
        System.out.println("静态代理类自动分配到DB_"+dbRouter+"数据源处理数据");
        DynamicDataSourceEntry.set(dbRouter);
        int res=orderService.createOrder(order);
        System.out.println("Proxy after method");
        return res;
    }
}

测试类

public class Client {
    public static void main(String[] args) throws ParseException {
        Order order=new Order();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
        Date date=sdf.parse("2020/05/03");
        order.setCreateTime(date.getTime());

        IOrderSerivce orderService=new OrderServiceStaticProxy(new OrderService());
        orderService.createOrder(order);
    }
}

image-20200506143346904

四、JDK的动态代理

image-20200515110802142

上述的静态代理需要代理对象和目标对象实现相同的接口,这意味着随着代理对象和目标对象的不断添加,一旦接口增加了方法,目标对象和代理对象都需维护,代码会变得相当繁琐和冗余。

为此我们需要一种适应性更强的代理模式,这里JDK为我们提供了一种基于接口的动态代理。使用JDK的动态代理需要我门使用Proxy类以及InvocationHandler这个接口并实现invoke方法,调用Proxy类的静态方法newProxyInstance()。我们先查看下该方法的源码需要的参数

image-20200506144406609

  • ClassLoader loader

指定目标对象使用的类加载器,可以使用Object.getClass().getClassLoader()获取

  • Class<?>[ ] interfaces

目标对象实现的接口的类型,可用通过Object.getClass().getInterfaces() 获取

  • InvocationHandler h

事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行的目标对象的方法作为参数传入

现在我们再回顾下刚才的业务,将静态的数据源路由改成动态路由的业务。

创建动态代理类

public class OrderServiceDynamicProxy implements InvocationHandler {
    private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy");
    private Object target;

    public OrderServiceDynamicProxy(Object target){
        this.target=target;
    }
    public Object getInstance(){
        Class<?> clazz=target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(args[0]);
        Object object=method.invoke(target,args);
        System.out.println("Proxy after method");
        return object;
    }
    private void before(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        System.out.println("Proxy before method");
        Long time= (Long) obj.getClass().getDeclaredMethod("getCreateTime").invoke(obj);
        int dbRouter= Integer.parseInt(yearFormat.format(new Date(time)));
        System.out.println("静态代理类自动分配到DB_"+dbRouter+"数据源处理数据");
        DynamicDataSourceEntry.set(dbRouter);
    }
}

测试类

public class Client {
    public static void main(String[] args) throws ParseException {
        Order order=new Order();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
        Date date=sdf.parse("2020/05/03");
        order.setCreateTime(date.getTime());

        IOrderSerivce orderService= (IOrderSerivce) new OrderServiceDynamicProxy(new OrderService()).getInstance();
        orderService.createOrder(order);
    }
}

五、动态代理源码探索

六、CGLib动态代理

image-20200515110733902

基于子类的动态代理技术,要求被代理类不可被final修饰,使用的是CGLib库提供的Enhance类,调用其create方法创建代理对象。create方法的传入参数为:

  • Class type

指定被代理对象的字节码文件

  • callback

回调接口,这里一般使用MethodInterceptor的实现类

继续对之前提到的业务类进行改造

public class OrderServiceCGLibProxy implements MethodInterceptor {
    private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy");
    private Object target;

    public OrderServiceCGLibProxy(Object target){
        this.target=target;
    }
    public Object getInstance(){
        return Enhancer.create(target.getClass(),this);
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before(objects[0]);
        Object object=methodProxy.invokeSuper(o,objects);
        System.out.println("Proxy after method");
        return object;
    }

    private void before(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        System.out.println("Proxy before method");
        Long time= (Long) obj.getClass().getDeclaredMethod("getCreateTime").invoke(obj);
        int dbRouter= Integer.parseInt(yearFormat.format(new Date(time)));
        System.out.println("静态代理类自动分配到DB_"+dbRouter+"数据源处理数据");
        DynamicDataSourceEntry.set(dbRouter);
    }
}

七、CGLib的FastClass机制

  • cglib代理对象实现后会产生三个.class文件, 这三个文件分别是被代理类的class文件、被代代理类的FastClass文件和代理类的FastClass文件。
  • 代理类会获得所有在父类继承来的方法, 并且会有MethodProxy与之对应
  • 调用过程:以createOrder()方法为例。代理对象调用this.createOrder()方法->调用拦截器->methodProxy.invokeSuper->CGLIB$createOrder()$0->被代理对象createOrder()方法
  • cglib动态代理执行代理方法效率之所以比JDK的高是因为cglib采用了FastClass机制, 原理简单来说就是:为代理类和被代理类各生成一个Class, 这个Class会为代理类或被代理类的方法分配一个index(int类型) 。这个index当做一个入参, Fast Class就可以直接定位要调用的方法直接进行调用, 这样省去了反射调用, 所以调用效率比JDK动态代理通过反射调用高
  • FastClass并不是跟代理类一块生成的,而是在第一次执行MethodProxy invoke/invoke Super方法时生成的并放在了缓存中。Method Proxy invoke/invoke Super都调用了 init()方法,init()方法中获取index。

八、JDK动态代理和CGib动态代理对比

1.JDK动态代理实现类被代理类的接口,CGLib代理继承了被代理对象。

2.JDK动态代理和CGLib代理都在运行期生成了字节码文件,JDK动态代理直接生成Class字节码文件,而CGLib代理使用ASM框架生成Class字节码文件,CGLib代理实现更加复杂,生成代理类的效率比通过JDK动态代理生成的效率低,即JDK动态代理字节码文件的生成效率高。

3.JDK动态代理调用代理方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用方法的,所以CGLib代理的执行效率高。

九、静态代理和动态代理的区别

1.静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,那么代理类也需要同步增加方法,这一点上违背了开闭原则

2.动态代理采用在运行时动态生成代码的方式,取消了对被代理类扩展的限制,遵循开闭原则

3.动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。

十、代理类的优缺点

优点:

  • 代理模式能够将代理对象与真实被调用对象分离
  • 在一定程度上降低了系统的耦合性,扩展性好
  • 可以起到保护目标对象的作用
  • 可以增强目标对象的功能

缺点:

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加了一个代理对象,会导致请求的速度变慢
  • 增加了系统的复杂度
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 设计模式是软件开发中常用的一种解决方案,它们是一些经过实践验证的可复用设计思想。设计模式允许开发人员在类和对象的结构上灵活地更改,并提供了一种优雅的解决方案来应对各种软件开发问题。 GOF(Gang of Four)是指Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四位软件工程师,他们在《设计模式:可复用面向对象软件的基础》一书中总结了23种常见的设计模式,这本书因此而获得了“设计模式圣经”的称号。 这本书以案例为基础,深入浅出地讲解了每个设计模式的原理和应用场景,并提供了C++实现源码。 其中,创建型设计模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。这些模式都提供了一种方式来创建对象,使得程序在实例化对象时更加灵活和可扩展。 结构型设计模式包括适配器模式、装饰器模式、代理模式、组合模式、享元模式和外观模式。这些模式关注如何通过类和对象的组合来创建更大的结构,并提供了一种优雅的方式来简化系统的复杂性。 行为型设计模式包括策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式和中介者模式。这些模式关注对象之间的通信和交互,它们提供了一种优雅的方式来实现松耦合和可维护的代码。 总之,设计模式是软件开发中非常重要的一部分,它们提供了一种通用的解决方案来处理常见的设计问题。通过学习和应用设计模式,开发人员可以提高代码的可复用性、可扩展性和可维护性,并加快开发进度。 ### 回答2: 设计模式是软件开发中常用的解决问题的一种思维方式或者说是一种已被证实有效的解决问题的方法。GOF 23种设计模式是由四位著名的软件工程师提出并总结出的一套经典的设计模式。 GOF 23种设计模式分别是创建型模式、结构型模式和行为型模式。创建型模式包括简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。结构型模式包括适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式和代理模式。行为型模式包括策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式和解释器模式。 GOF 23种设计模式具有不同的应用场景和优势。通过学习和理解这些设计模式,开发者可以更加灵活地应对各种软件开发中的问题。同时,掌握这些设计模式也有助于提高代码的可读性、可维护性和可扩展性。 附带C语言实现源码是一种更加具体的学习和理解设计模式的方法。通过查看实现源码,可以更加直观地看到设计模式在实践中的应用。这些源码可以作为学习的参考,帮助开发者更好地理解设计模式的思想和使用方式。 总之,设计模式是软件开发中非常重要的一部分,通过学习GOF 23种设计模式并理解其应用场景和优势,可以提高软件开发的效率和质量。附带C语言实现源码能够更加具体地帮助开发者理解设计模式的实际应用。 ### 回答3: 设计模式是软件工程中常用的一种设计思想或模板,可以用于解决特定的问题和提供可重用的解决方案。GOF(Gang of Four)提出了23种设计模式,并在书籍《设计模式:可复用面向对象软件的基础》中进行了详细的解析和说明。 这本书详细地介绍了23种设计模式,包括创建型模式、结构型模式和行为型模式。通过阅读这本书,读者可以了解每种设计模式的特点、适用场景和实现方法。另外,书中还通过示例代码的方式演示了每种设计模式的具体实现,并提供了附带的C语言实现源码。 这本书对于理解设计模式的概念和思想非常有帮助。它不仅提供了23种设计模式的名字和简介,还详细解释了每种模式的适用场景和应用案例。读者可以通过学习这些设计模式,了解如何将它们应用于自己的软件开发工作中,提高代码的可重用性和可维护性。 书中的C语言实现源码是帮助读者理解和实践设计模式的重要资源。通过阅读源码,读者可以更加深入地理解每种设计模式的具体实现细节,并进一步提升自己的编程能力。 总之,通过学习《设计模式:可复用面向对象软件的基础》这本书,读者可以全面了解设计模式的概念、分类和实现方法,并通过阅读附带的C语言实现源码来加深对设计模式的理解和应用。这将对提升软件设计和开发的能力和水平非常有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值