栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Spring框架】7.代理模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Spring框架】7.代理模式

代理模式是SpringAOP的底层,在了解AOP之前,需要先了解代理模式是个什么样的东西。

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式分为以下两种:

  • 静态代理
  • 动态代理

下面先从静态代理入手,了解学习。

1. 静态代理 1.1 简 述


前面关于代理模式的定义,如果没有一个例子进行阐述,理解起来,有些抽象。这里用租房的例子,来详细说说这个代理模式是个什么玩意。

毕业后进入社会参加工作,第一件事要考虑的,就是住哪里?这个时候,就有租房的需求了。

要租房子,最简单的方法莫过于找房东直接租,但房东又岂是那么容易找到的!往往都是神龙见首不见尾。

就目前的租房市场而言,能直接找到房东租房的,少得可怜,往往都是中介人员在打理,这也就意味着我们去租房所遇到的人,十之八九都是中介人员。只有中介人员才可能直接联系到房东,并且中介人员在房屋出租之前,并不会给你房东的联系方式。

结合上面的图和描述,我们可以知道,在整个租房过程中,中介其实充当的就是房东的代理人,目的就是代表房东将房子出租出去,那房东自己干嘛去了?当然是找地方度假快活去了。所以租房子就是代理模式的一个体现了。

1.2 静态代理实现

角色分析

  • 抽象角色:一般会使用接口或者抽象类来解决(如上面的共同目标)
  • 真实角色:被代理的角色(如上面的房东)
  • 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作(如上面的中介)
  • 客户:访问代理角色的人(如上面的租房子的人)

代码实现

  1. 新建一个接口 Rent ,作为抽象角色,代表出租房子
package com.yuhuofei.proxy;


public interface Rent {

    void rent();

}

  1. 新建一个 Host.java 类,作为真实角色,代表房东
package com.yuhuofei.proxy;


public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

  1. 新建一个 HostProxy.java 类,作为代理角色,表示中介
package com.yuhuofei.proxy;


public class HostProxy implements Rent{

    private Host host;

    public HostProxy(Host host){
        this.host=host;
    }

    //帮房东出租房子
    @Override
    public void rent() {
        host.rent();
    }

    //看各个地方的房子
    public void seeHouse(){
        System.out.println("中介带你看房");
    }

    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }

    //签租房合同
    public void signContract(){
        System.out.println("签租房合同");
    }
}

  1. 新建一个 Client.java 类,作为客户,代表要租房子的人
package com.yuhuofei.proxy;


public class Client {

    public static void main(String[] args) {

        //房东要出租房子
        Host host = new Host();
        //代理,代理一般会有其它的附加操作
        HostProxy hostProxy = new HostProxy(host);
        //直接找中介租房
        hostProxy.rent();
    }
}

静态代理优缺点:

  • 可以使得真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务交给代理角色处理,实现业务分工
  • 公共业务发生扩展的时候,方便集中管理
  • 一个真实角色就要产生一个代理角色,代码里会翻倍,开发效率会变低
2. 动态代理

动态代理和静态代理的角色分类一样,但不同的是动态代理动态生成的代理角色,所代理的是接口,而不再是某一个具体实现类。

动态代理分类:

  • 基于接口的动态代理
  • 基于类的动态代理

基于接口实现动态代理,一般是使用 JDK 动态代理

基于类实现动态代理,一般是使用 cglib

还有一种用得比较多的动态代理实现方式是基于 java 字节码实现,使用 javasist 这个类库包。

在进行代码实现动态代理前,需要了解一个类和一个接口,一个类是 Proxy (代理)类,一个接口是 InvocationHandler (调用处理程序)接口,同时还要了解反射的知识,因为动态代理本质是通过反射实现的

2.1 示例1——动态地代理指定的接口
  1. 新建一个 Rent 接口,表示出租房子这个目标,即抽象角色
package com.yuhuofei.dynamic;


public interface Rent {

    void rent();

}

  1. 新建一个 Host.java 类,代表真实角色
package com.yuhuofei.dynamic;


public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

  1. 新建一个调用处理程序的类,ProxyInvocationHandler.java,用于动态生成代理角色
package com.yuhuofei.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

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

    //处理代理类实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //动态代理本质是使用反射机制实现的
        seeHouse();
        Object result = method.invoke(rent, args);
        return result;
    }

    public void seeHouse() {
        System.out.println("中介带着看房子!!!");
    }
}

  1. 新建一个 Client.java 类,用于代表客户
package com.yuhuofei.dynamic;


public class Client {

    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //使用ProxyInvocationHandler,用于生成代理角色
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        //设置要代理的真实角色
        proxyInvocationHandler.setRent(host);
        //生成代理角色,这里要注意,动态代理代理的是接口,不是具体实现类
        Rent proxy = (Rent) proxyInvocationHandler.getProxy();
        proxy.rent();

    }
}

  1. 项目结构即测试结果如下

2.2 示例2——动态地代理自定义的接口
  1. 新建一个接口 UserService ,代表抽象角色
package com.yuhuofei.demo;


public interface UserService {

    void add();

    void delete();

    void query();

    void update();
}

  1. 新建一个 UserServiceImpl.java 类,作为真实角色
package com.yuhuofei.demo;


public class UserServiceImpl implements UserService {

    @Override
    public void add() {
        System.out.println("新增用户信息");
    }

    @Override
    public void delete() {
        System.out.println("删除用户信息");
    }

    @Override
    public void query() {
        System.out.println("查询用户信息");
    }

    @Override
    public void update() {
        System.out.println("更新用户信息");
    }
}

  1. 新建一个泛型类 UserProxyInvocationHandler,用于动态地生成代理角色
package com.yuhuofei.demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class UserProxyInvocationHandler implements InvocationHandler {

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

    //构造方法
    public UserProxyInvocationHandler(T target) {
        this.target = target;
    }

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

    //处理代理类实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //动态代理本质是使用反射机制实现的
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String name) {
        System.out.println("调用了 " + name + " 方法");
    }
}

  1. 新建一个 Client.java 类,代表客户
package com.yuhuofei.demo;



public class Client {

    public static void main(String[] args) {
        //真实角色
        UserServiceImpl user = new UserServiceImpl();
        //UserProxyInvocationHandler,用于生成代理角色
        UserProxyInvocationHandler handler = new UserProxyInvocationHandler<>(user);
        //生成代理角色,这里要注意,动态代理所代理的是接口,不再是具体实现类
        UserService proxy = (UserService) handler.getProxy(user);
        //调用接口中的方法
        proxy.delete();
    }
}

  1. 测试结果及项目结构

  1. 方法执行顺序说明:

    proxy.delete()——>Object invoke(Object proxy, Method method, Object[] args)——>log(method.getName())——>输出结果——>method.invoke(target, args)——>void delete()——输出结果;对于这种不能一眼看出执行顺序的程序,可以打断点执行,一步一步跟踪代码运行步骤,找到正确逻辑。

动态代理相比于静态代理的好处:动态代理,它代理的是接口,因此也就可以代理实现了这个接口的所有类;而静态代理只能代理指定的一个类。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1003956.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号