Spring:代理模式之静态代理&动态代理

前言

其实之前写过类似一篇了,重新具体的总结一下

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP 和 SpringMVC】面试必定

代理模式的分欸:

  • 静态代理
  • 动态代理

代理的原型:

静态代理

角色分析:

  • 抽象角色:一般会用接口或者抽象类来解决(这里指的是外卖平台和餐厅的共同目标"赚钱")
  • 真实角色:被代理的角色(厨师、餐厅)
  • 代理角色:代理真实角色,代理真实角色后,通常会进行一些附属操作...(外卖平台)
  • 客户:访问代理对象的人!(这里指的是要吃饭的人)

代理模式的好处:

  • 可以使真实角色的操作更加纯粹!,不用去关注一些公共的业务
  • 公共业务就交给了代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!

缺点:

  • 一个真实角色就会产生一个代理角色,代码量翻倍~开发效率会变低
  • 缺点解决的办法就是动态代理

代码步骤

  1. 接口
java 复制代码
// 卖吃的(赚钱)
public interface Sell {
    public  void sell();
}
  1. 真实角色
java 复制代码
// 厨师:真实角色
public class Chef implements Sell{


    @Override
    public void sell() {
        System.out.println("cike_y真实厨师:快来我这里点吃的吧");
    }
}
  1. 代理角色
java 复制代码
// 少用继承(局限性很大)、多用组合的方式
public class proxy implements Sell{
    @Override
    public void sell() {
        System.out.println("外卖平台开始代理厨师的话:");
        chef.sell();//帮真实角色(厨师)代理他说的话
        // 下面是外卖平台(中介代理)的附属操作
        System.out.println("外卖平台(中介代理)开始附属以下操作了:\n");
        SeeRider();
        SendOrder();
        GiveMoney();
        TakeCommission();
        TakeRiderFee();
    }


    // 组合
    private Chef chef;

    public proxy() {
    }

    public proxy(Chef chef) {
        this.chef = chef;
    }

    // setProxy(){..} 没必要set注入

    // 代理可以做一些附属的事情
    public void SeeRider(){// 找骑手
        System.out.println("帮你寻找骑手中......");
    }

    // 收取佣金
    public void TakeCommission(){
        System.out.println("外卖平台收取佣金");
    }

    // 骑手费用
    public void TakeRiderFee(){
        System.out.println("给骑手小费");
    }

    // 发送客户订单
    public void SendOrder(){
        System.out.println("发送客户订单");
    }

    // 给餐厅钱
    public void GiveMoney(){
        System.out.println("给餐厅钱");
    }
}
  1. 客户端访问代理角色
java 复制代码
public class Client {
    public static void main(String[] args) {
        // 想点这家餐厅做的菜
        Chef chef = new Chef();
        // 外卖平台,帮你通知厨师做菜,但是外卖平台(代理)会做一些附属操作
        proxy proxy = new proxy(chef);

        // 你不用面对厨师、直接找外卖平台即可!
        proxy.sell();
    }
}

加深理解

在一个增删改查的业务

  1. 接口类(公共调用的方法)
java 复制代码
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
  1. 真实角色(业务实现类)
java 复制代码
public class UserServiceImpl {
    public void add() {
        System.out.println("添加用户");
    }
    public void delete() {
        System.out.println("删除用户");
    }
    public void update() {
        System.out.println("修改用户");
    }
    public void query() {
        System.out.println("查询用户");
    }
}
  1. 代理角色(代理真实角色的类&横向扩展附属操作)
java 复制代码
public class UserServiceProxy implements UserService{
    public UserServiceProxy(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }

    public void setUserServiceImpl(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }

    private UserServiceImpl userServiceImpl;
    @Override
    public void add() {
        debug("add");
        userServiceImpl.add();
    }

    @Override
    public void delete() {
        debug("delete");
        userServiceImpl.delete();
    }

    @Override
    public void update() {
        debug("update");
        userServiceImpl.update();
    }

    @Override
    public void query() {
        debug("query");
        userServiceImpl.query();
    }

    // 日志方法:横向扩展增增删改查业务
    public void debug(String msg){
        System.out.println("debug:"+msg+"方法执行了");
    }
}
  1. 客户端访问代理角色(客户端想实现真实角色的类,只能通过访问代理角色)
java 复制代码
public class Client {
    public static void main(String[] args) {
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy(userServiceImpl);
        userServiceProxy.add();
        userServiceProxy.delete();
        userServiceProxy.update();
        userServiceProxy.query();
    }
}

聊聊AOP:

动态代理

动态代理的底层都是反射

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口的---JDK的动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javasist

需要了解的两个类:Proxy:代理,InvocationHandler:调用处理程序

Proxy

  • Proxy提供了创建动态代理类和实例的静态方法。

InvocationHandler

  • 代理实例的_调用处理程序_实现的_接口_

代理类写法

  1. 继承InvocationHandler接口
java 复制代码
public class ProxyInvocationHandler implements InvocationHandler
  1. 引用真实角色的接口
java 复制代码
private Sell sell;
  1. 获取真实角色的共同继承的接口
java 复制代码
public Object getProxy(){
    // 获取被代理接口的所有方法
    return Proxy.newProxyInstance(this.getClass().getClassLoader(),
            sell.getClass().getInterfaces(), this);
}
  1. 处理代理实例,并且执行方法返回结果
java 复制代码
    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(sell, args); // 执行一个方法,执行完毕之后会获取一个结果
        return result;
    }

代码步骤

真实角色(厨师)

java 复制代码
// 厨师:真实角色
public class Chef implements Sell {


    @Override
    public void sell() {
        System.out.println("cike_y真实厨师:这里是Demo04案例,快来我这里点吃的吧");
    }
}

外卖平台和厨师都要赚钱(共同接口)

java 复制代码
public interface Sell {
    public  void sell();
}

外卖平台(动态代理类)

java 复制代码
//等我们会用这个类自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler{
    // 被代理的接口,解耦性强
    private Sell sell;

    public void setSell(Sell sell) {
        this.sell = sell;
    }

    //    Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
//            new Class<?>[] { Foo.class },
//            handler);

    // 获取真实角色和代理角色公共的接口
    public Object getProxy(){
        // 获取被代理接口的所有方法
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                sell.getClass().getInterfaces(), this);
    }

    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在执行目标对象的方法之前,可以进行一些自定义操作
        Start();
        Object result = method.invoke(sell, args); // 执行一个方法,执行完毕之后会获取一个结果
        // 在执行目标对象的方法之后,可以进行一些自定义操作
        SeeRider();
        SendOrder();
        GiveMoney();
        TakeCommission();
        TakeRiderFee();
        return result;
    }

// 代理的附属操作
    public void Start(){
        System.out.println("这里是外卖平台,开始代理:");
    }

    // 代理可以做一些附属的事情
    public void SeeRider(){// 找骑手
        System.out.println("帮你寻找骑手中......");
    }

    // 收取佣金
    public void TakeCommission(){
        System.out.println("外卖平台收取佣金");
    }

    // 骑手费用
    public void TakeRiderFee(){
        System.out.println("给骑手小费");
    }

    // 发送客户订单
    public void SendOrder(){
        System.out.println("发送客户订单");
    }

    // 给餐厅钱
    public void GiveMoney(){
        System.out.println("给餐厅钱");
    }

}

用户(要吃饭的人:面对外卖平台)

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 真实角色
        Chef chef = new Chef();

        // 代理角色
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();

        // 要代理的对象:指的是真实角色,设置代理的真实角色对象
        proxyInvocationHandler.setTarget(chef);

        // 代理角色(已经代理了真实角色了)获取接口
        Sell proxy =  (Sell) proxyInvocationHandler.getProxy();

        // 直接面对外卖平台,不用找厨师
        proxy.sell();
    }

万能的写法

只需要改变动态代理的对象为Object对象就可以了,因为Java 中Object 类是所有类的父类,也就是说Java 的所有类都继承了Object,子类可以使用Object 的所有方法。

java 复制代码
public class ProxyInvocationHandler implements InvocationHandler {
    //  要代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

//  官方写法:
//    Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
//            new Class<?>[] { Foo.class },
//            handler);

// 获取真实角色的公共接口
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);

    }

    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args); // 执行方法,返回结果
        return result;
    }
}

其余的:真实角色(厨师)、接口(外卖平台和厨师的共同目标)、用户(吃饭的人)写法都不需要变。

动态代理的好处:

  • 可以使真实角色的操作更加纯粹!,不用去关注一些公共的业务
  • 公共业务就交给了代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

参考文档:

plain 复制代码
JDK-api-1.8_google
相关推荐
青岛少儿编程-王老师2 小时前
CCF编程能力等级认证GESP—C++8级—20251227
java·开发语言·c++
任子菲阳2 小时前
学JavaWeb第六天——JDBC & Mybatis
java·数据库·mybatis
计算机毕设指导62 小时前
基于微信小程序的个性化漫画阅读推荐系统【源码文末联系】
java·python·微信小程序·小程序·tomcat·maven·intellij-idea
Love Song残响2 小时前
高效自动化清理临时文件方案
java·开发语言·spring
技术小泽2 小时前
java转go语言入门基础篇(二)
java·golang
古城小栈2 小时前
Rust 中符号语法 一文全晓
开发语言·后端·rust
我不会写代码njdjnssj2 小时前
基于SpringBoot+SSM的外卖平台Day1-6
java·spring boot·后端
崎岖Qiu2 小时前
【设计模式笔记26】:深入浅出「观察者模式」
java·笔记·观察者模式·设计模式