代理模式笔记

image-20200531213054908

学习资源来自于哔哩哔哩UP遇见狂神说,一个宝藏UP大家快去关注吧!记得三连加分享,不要做白嫖党.

由于使用的是国外的imgur图床,图片无法显示请魔法上网 !

由于使用的是国外的imgur图床,图片无法显示请魔法上网 !

由于使用的是国外的imgur图床,图片无法显示请魔法上网 !

10. 代理模式

为什么要学代理模式 ? 因为这就是SpringAOP的底层 ! [面试必问: SpringAOP 和 SpringMVC]

代理模式的分类 :

  • 静态代理
  • 动态代理

Imgur

10.1 静态代理

角色分析 :

  • 抽象角色 : 共同完成的事情(租房) , 一般会用接口或者抽象类来解决
  • 真实角色 : 被代理的角色(房东)
  • 代理角色 : 代理真实角色(中介) , 代理真实角色后,我们一般会做一些附属操作
  • 客户 : 访问代理对象的人(你)

Imgur

代码步骤 :

  1. 接口

    //租房
    public interface Rent {
        public void rent();
    }
    
  2. 真实角色

    //房东
    public class Landlord implements Rent {
        public void rent() {
            System.out.println("房东出租了房屋!");
        }
    }
    
  3. 代理角色

    //代理角色
    public class Proxy implements Rent{
    
        private Landlord landlord;
    
        public Proxy() {
        }
    
        public Proxy(Landlord landlord) {
            this.landlord = landlord;
        }
    
        public void rent() {
            landlord.rent();
        }
    
        //看房
        public void seeHouse() {
            System.out.println("中介带你看房");
        }
    
        //收中介费
        public void fare() {
            System.out.println("收中介费");
        }
    
        //签合同
        public void hetong() {
            System.out.println("签租赁合同");
        }
    }
    
  4. 客户端访问代理角色

    public class Client {
        public static void main(String[] args) {
            //房东要租房子
            Proxy proxy = new Proxy(new Landlord());
            //代理,中介帮房东租房子,但是代理一般会有一些附属操作!
            proxy.hetong();
            proxy.fare();
            proxy.seeHouse();
    
            //你不用面对房东,直接找中介租房即可!
            proxy.rent();
        }
    }
    

代理模式和好处 :

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

缺点 :

  • 一个真实角色就会产生一个代理角色 , 代码量会翻倍 , 开发效率变低 .

10.2 加深理解

代码 : 1.以业务层添加日志为例子 :

Imgur

  1. 增删改查接口 :

Imgur

  1. 业务层实现

Imgur

  1. 添加一个代理类,并增加一个日志方法,这样可以不改动原有代码 .

Imgur

  1. 测试 :

横向开发导图 :

Imgur

10.3 动态代理

  • 动态代理和静态代理一样
  • 动态代理是动态生成的,不是我们写好的
  • 动态代理可分为两大类 : 基于接口的动态代理 , 基于类的动态代理
    • 基于接口 : JDK 动态代理 [例子中使用这个]
    • 基于类 : cglib
    • java字节码实现 : JAVAssist

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

  • Proxy提供了创建动态代理类的静态方法
  • InvocationHandler 是由代理实例的湖用处理程实现的接口。
    每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

动态代理代码总览 :

Imgur

动态代理的核心 --->工具类: ProxyInvocationHandler.java

//等下我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

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

    //生成得到被代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }


    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制实现
        log(method.getName());
        return method.invoke(target, args);
    }

    public void log(String msg) {
        System.out.println("执行了" + msg + "方法");
    }
}

10.4 自己的理解

静态代理模式代码思路 :

接口 ---> 接口实现类 ---> 代理类 ---> 测试类

  • 接口中写要实现的方法

  • 接口实现类的作用十分纯粹 , 就是实现接口中的方法

  • 在代理类中将接口实现类作为一个私有属性,并通过set方法传参,实现代理

    private UserServiceImpl userService;
    
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }
    
  • 在测试类中new出代理类,代理类通过set方法,将new出来的接口实现类对象作为参数传参,就可以开始调用方法了

    UserServiceProxy proxy = new UserServiceProxy();
    proxy.setUserService(new UserServiceImpl());
    

动态代理模式实现思路 :

  • 重点部分就是将将接口实现类作为一个私有属性,并通过set方法传参 , 动态代理将这个set方法传的具体参数类变为了一个可变的参数类. Object target代表一个未知的参数类(接口实现类).
//被代理的接口
private Object target;

public void setTarget(Object target) {
    this.target = target;
}
  • 既然这个参数类未知,那么我就得将它生成出来,通过反射来实现

    //生成得到被代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
    

    这是方法是一个固定写法,三个参数分别为:
    1.得到类加载器 2.得到代理的类的接口 3. this代表InvocationHandler .
    因为动态代理的工具类需要实现InvocationHandler接口,所以直接写 this

  • 现在已经通过反射获取了代理的接口,剩下的就是一个执行方法

    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制实现
        return method.invoke(target, args);
    }
    

    这是继承InvocationHandler接口所需要实现的方法, 它的作用就是通过反射自动运行接口方法(它的实现是通过反射找到接口的接口实现类,并运行接口实现类的方法).

  • 如果要拓展方法就可以直接在工具类中加.

动态代理的完整工具类 :

//等下我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

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

    //生成得到被代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }


    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制实现
        log(method.getName());
        return method.invoke(target, args);
    }

    //添加拓展方法
    public void log(String msg) {
        System.out.println("执行了" + msg + "方法");
    }
}

测试类:

public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //被代理角色,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        pih.setTarget(userService); //设置要代理的对象
        
        //动态生成代理类
        UserService proxy = (UserService) pih.getProxy();

        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}

简化一下:

public class Client {
    public static void main(String[] args) {

        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        proxyInvocationHandler.setTarget(new UserServiceImpl());
        UserService proxy = (UserService) proxyInvocationHandler.getProxy();
        
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}

实例化真实角色(接口实现类) ---> 实例化接口实现类 ---> 用接口实现类的set方法注入(代理)真实角色 ---> 用接口实现类的getProxy方法生成代理类 ---> 运行代理类的方法和拓展方法 .

原文地址:https://www.cnblogs.com/tanshishi/p/13149850.html