设计模式之代理模式

代理模式

1. 抛出问题

在Java的开发中有分层的思想,分为Controller层,Service层还有DAO层。每个层面都有自己的工作,分别解决不同的问题,Controller层负责业务流程的控制,指挥Service层的接口来完成业务的处理,在Service层之上;Service层是一个中间层,用于实现业务的设计和逻辑实现,在DAO层之上;DAO层主要做数据持久层,一般用于对数据库的具体操作。

在三个层次中实际上Service层要处理的任务是最多的,所以Service层也是我们处理业务的核心逻辑层,那么Service层中包含了那些代码呢?

可以大致分为核心功能和额外功能两类:

  1. 核心功能
    1. 业务运算
    2. 对DAO层的调用
  2. 额外功能
    1. 不属于业务
    2. 可有可无
    3. 代码量很小
    4. 如:事务,日志,性能监控…

核心功能是Service层中必须要处理的,那么额外功能在Service层中实现是否合适呢?

  • 从Service层的调度者(Controller层)的角度:应该在Service层中实现额外要实现的功能
  • 从软件设计者的角度:Service不应该编写额外功能

现实生活中的举例:

假如一个人有很多房子,先要出租出去,这个人也就是房东就需要完成两个事情:1.带租客看房交钱入住,2.宣传自己出租房屋的广告。但是从房东的角度来讲,自己的核心任务就是带租客看房交钱入住,而宣传自己出租房屋的广告是要额外做的事情,所以就有了中介。中介要做的任务就是宣传房东的租房广告,也就是房东要实现的额外任务。

从上面的例子可以看出,中介代理(Proxy)了房东完成额外的任务,那么,从软件设计的角度,Service层如果有了这样的代理来完成额外的任务,Service层就可以专心处理核心业务,利于Service层的代码维护,于是我们引入了代理模式。

补充:建议大家在学习过程中不要在意静态代理还是动态代理,只要记住他们的本质都是代理模式而已。

2. 静态代理

2.1 概念

通过代理类,为原始类(目标类)增加额外的功能。好处是有利与原始类代码的维护。

2.2 名词解释

  1. 原始类(目标类)

    指定是业务类,也就是被代理类

  2. 原始方法(目标方法)

    原始类中的方法,就是原始方法。

  3. 代理类

    实现附加功能的类(如事务,日志,性能监控…)

2.3 静态代理开发的核心要素

原始类的特征抽象出一个接口,实际实现由其实现类来完成。代理类需要与原始类实现相同的接口,同时需要在代理方法中调用目标方法。

代理类 = 原始类 + 额外功能 + 实现与原始类相同的接口

2.4 编码

UserService接口:

public interface UserService {
    boolean login();

    void register();
}

实现类UserServiceImpl

public class UserServiceImpl implements UserService {

    @Override
    public boolean login() {
        System.out.println("UserServiceImpl.login");
        return false;
    }

    @Override
    public void register() {
        System.out.println("UserServiceImpl.register");
    }
}

代理类UserServiceProxy:

public class UserServiceProxy implements UserService {
    private UserService userService = new UserServiceImpl();

    @Override
    public boolean login() {
        System.out.println("----额外功能----");
        return userService.login();
    }

    @Override
    public void register() {
        System.out.println("----额外功能----");
        userService.register();
    }
}

2.5 静态代理存在的问题

  1. 静态类文件多,不利于项目的管理

    有多少个类需要被代理,就需要创建多少个代理类

  2. 代码维护性差

    代理类很多,导致额外功能维护性差

3. 动态代理

动态代理基于JVM的动态字节码技术,在程序运行时,动态的去生成代理类、代理类的对象,这样就可以不需要进行硬编码编写多个类来实现代理功能。

3.1 JDK动态代理

JDK动态代理的原理和静态代理类似,都是需要与原始类实现相同的接口,这样可以约束代理类的行为。但是JDK动态代理与上面提到的静态代理不同,JDK动态代理的代理类对象是在程序运行时根据代理类动态进行创建。

所以怎么进行创建呢,创建的过程中需要什么“原料”吗?

  1. JVM中的类都是由类加载器来进行加载的,所以需要提供一个类加载器。
  2. 需要获取原始类实现的接口信息,也就是需要提供接口。
  3. 动态执行增强逻辑的方法。

JDK为我们提供了相关的API:java.lang.reflect包下的Proxy类。

Proxy中有一个静态方法:

Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces, InvocationHandler h);

方法参数解析:

  • ClassLoader loader:需要提供的类加载器。
  • Class<?>[] interfaces:原始类所实现的接口。
  • InvocationHandler h:是一个InvocationHandler接口的实例,需要通过实现InvocationHandler,从而实现增强逻辑的方法。

比较复杂的是实现InvocationHandler接口:

InvocationHandler invocationHandler = new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
};

方法参数说明:

  • Object proxy:创建的代理类对象的引用。
    • 可以使用反射获取代理对象的信息(也就是proxy.getClass().getName())。
    • 可以将代理对象返回以进行连续调用,这就是proxy存在的目的,因为this并不是代理对象。
  • Method method:原始方法,可以调用Method中的invoke()方法来执行。
  • Object[] args:原始方法的参数。
  • 返回值:是原始方法的返回值。

代码:

在invoke方法中我们可以实现很多额外的功能。

public class TestProxy {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("---前置处理---");
                Object ret = method.invoke(userService, args);
                System.out.println("---后置处理---");
                return proxy;
            }
        };

        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestProxy.class.getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
        boolean ret = userServiceProxy.login("Tom", "123456");
        System.out.println("ret = " + ret);
        userServiceProxy.register(new User("Jerry", "123123"));
    }
}

3.2 CGlib动态代理

CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法⼀致,同时在代理类中提供新的实现(额外功能+原始方法)

在这里插入图片描述

CGlib实现动态代理的相关API在org.springframework.cglib.proxy包下,需要使用包中的Enhancer类。

代码:

package com.jc.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class TestCGlib {
    public static void main(String[] args) {
        UserService userService = new UserService();

        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(TestCGlib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());

        MethodInterceptor methodInterceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("---CGlib 前置处理---");
                Object ret = method.invoke(userService, objects);
                System.out.println("---CGlib 后置处理---");
                return ret;
            }
        };

        enhancer.setCallback(methodInterceptor);
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("Tom", "123456");
        userServiceProxy.register(new User("Jerry", "123123"));
    }
}

参数解释:

  • Object o:代理类对象。
  • Method method:原始方法对象。
  • Object[] objects:原始方法中的参数。
  • MethodProxy methodProxy:代理方法对象。

3.3 动态代理总结

  1. JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理的实现类(JDK自带API)
  2. Cglib动态代理 Enhancer 通过继承原始类创建的代理类 (Spring提供的API)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神烦狗闯入了你的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值