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
相关推荐
向上的车轮11 小时前
为什么.NET(C#)转 Java 开发时常常在“吐槽”Java:checked exception
java·c#·.net
Dragon Wu11 小时前
Spring Security Oauth2.1 授权码模式实现前后端分离的方案
java·spring boot·后端·spring cloud·springboot·springcloud
跳动的梦想家h12 小时前
环境配置 + AI 提效双管齐下
java·vue.js·spring
坚持就完事了12 小时前
Java中的集合
java·开发语言
wjhx12 小时前
QT中对蓝牙权限的申请,整理一下
java·数据库·qt
一个有梦有戏的人12 小时前
Python3基础:进阶基础,筑牢编程底层能力
后端·python
YCY^v^12 小时前
JeecgBoot 项目运行指南
java·学习
人间打气筒(Ada)12 小时前
jenkins基于Pipeline发布项目
java·pipeline·jenkins·流水线·ci·cd·cicd
爬山算法12 小时前
Hibernate(88)如何在负载测试中使用Hibernate?
java·后端·hibernate
自不量力的A同学12 小时前
Solon AI v3.9 正式发布:全能 Skill 爆发
java·网络·人工智能