首页 > 其他分享 >软件设计模式白话文系列(六)代理模式

软件设计模式白话文系列(六)代理模式

时间:2022-11-11 17:34:19浏览次数:53  
标签:11 软件设计 代理 模式 public Long new 白话文 id

1、描述

代理模式属于结构型模式中的一种,通过对代理对象的调用来达到对原对象的增强、减弱作用。通过代理类的生成时机,我们将编译期就生成代理类的情况称之为静态代理模式,而在 Java 运行期动态生成代理类的场景称为动态代理模式。动态代理又基于接口继承两种实现方式分别分为 JDK 动态代理和 CGLib 动态代理两种。

2、适用性

  • 当访问对象不方便直接引用时(如原对象授权太过宽泛、需要对不同用户级别提供不同权限)。
  • 原对象功能需要增加,可以通过代理模式在不影响原始类的基础上实现目的。

3、实现逻辑

  1. 抽取真实主题类需要代理的接口获取抽象主题类。
  2. 代理类和主题类共同实现抽象主题类。
  3. 代理类控制真实主题类生命周期。代理在完成⼀些任务后 应将⼯作委派给服真实主题类的对象。
  • 真实主题类:原始类、需要被代理的类。

  • 抽象主题类:定义真实主题类中需要被代理类代理的接口。保证代理对象和原始对象的可交互性。

  • 代理类:实现抽象主题类,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

4、实战代码

4.1 静态代理

存在一个 Member 操作的业务层代码。现仅提供新增和查询功能的代理类,并需记录查询日志。

/**
 * 用户类
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:00:46
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Member {
    private Long id;
    private String name;
}

/**
 * 原始主题类接口(模拟实际开发,代理模式中这个类没有实际用途)
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:17:12
 */
public interface MemberService {

    Member getMember(Long id);

    List<Member> listMember();

    void saveMember(Long id, String name);

    void updateMember(Long id, String name);

    void deleteMember(Long id);
}

/**
 * 抽象主题类
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:00:14
 */
public class MemberServiceImpl implements MemberService, ProxyService {
    /**
     * 模拟 DB 初始化点数据
     */
    public static final Map<Long, Member> db = new HashMap<>();

    static {
        db.put(1L, new Member(1L, "张三"));
        db.put(2L, new Member(2L, "李四"));
    }

    @Override
    public Member getMember(Long id) {
        Member member = db.get(id);
        System.out.println(member);
        return member;
    }

    @Override
    public List<Member> listMember() {
        ArrayList<Member> members = new ArrayList<>(db.values());
        System.out.println(members);
        return members;
    }

    @Override
    public void saveMember(Long id, String name) {
        db.put(id, new Member(id, name));
    }

    @Override
    public void updateMember(Long id, String name) {
        db.put(id, new Member(id, name));
    }

    @Override
    public void deleteMember(Long id) {
        db.remove(id);
    }
}

/**
 * 抽象主题类
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:17:12
 */
public interface ProxyService {

    Member getMember(Long id);

    List<Member> listMember();

    void saveMember(Long id, String name);

}

/**
 * 代理类
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:16:26
 */
public class ProxyServerImpl implements ProxyService {

    private MemberService memberService = new MemberServiceImpl();

    @Override
    public Member getMember(Long id) {
        System.out.println("代理类查询 member 详情信息");
        return memberService.getMember(id);
    }

    @Override
    public List<Member> listMember() {
        System.out.println("代理类查询 member 列表信息");
        return memberService.listMember();
    }

    @Override
    public void saveMember(Long id, String name) {
        System.out.println("代理类新增 member 信息");
        memberService.saveMember(id, name);
    }
}

/**
 * 测试类
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:28:01
 */
public class App {
    public static void main(String[] args) {
        ProxyService proxyServer = new ProxyServerImpl();
        proxyServer.saveMember(3L, "王五");
        System.out.println("--------------");
        proxyServer.getMember(1L);
        System.out.println("--------------");
        proxyServer.listMember();
    }
}

执行结果:

通过代理模式,我实现了对原始对象的访问控制,客户端只能调用新增和查询接口,同时也实现对原始对象接口的增强作用,新增了日志打印功能。

4.2 JDK 动态代理

Java 中提供了一个动态代理类 Proxy,Proxy 不是上述中的代理类,而是通过其提供的静态方法(newProxyInstance 方法)来动态创建代理对象。

/**
 * 动态代理工厂
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 13:47:40
 */
public class ProxyFactory {
    private static ProxyService proxyService = new MemberServiceImpl();

    public static ProxyService getProxyService() {
        return (ProxyService) Proxy.newProxyInstance(
                proxyService.getClass().getClassLoader(),
                proxyService.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("代理类操作 member 功能:" + method.getName());
                    return method.invoke(proxyService, args);
                });
    }
}

/**
 * 测试类
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:28:01
 */
public class App {
    public static void main(String[] args) {
        ProxyService proxyServer = ProxyFactory.getProxyService();
        proxyServer.saveMember(3L, "王五");
        System.out.println("--------------");
        proxyServer.getMember(1L);
        System.out.println("--------------");
        proxyServer.listMember();
    }
}

执行结果:

动态代理相对于静态代理,可以把多个方法集中控制,在需代理的方法数量多或者会出现改动的时候,动态代理效率远高于静态代理。

4.3 CGLib 动态代理

JDK 动态代理和静态代理的必要前提是代理类和原始类都需要创建⼀个接⼝来实现代理和服务对象的可交换性。 如果没有现成的服务接⼝,从服务类中抽取接⼝并⾮总是可⾏的, 因为我们需要对服务的所有客户端进⾏修改, 让它们使⽤接⼝。所以我们的备选方案将代理作为服务类的⼦类, 这样代理就能继承服务的所有接⼝了。

这里我们需要导入 CGLib 依赖,CGLib 可以为没有提供接口的类实现代理。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
/**
 * 抽象主题类 不用实现接口
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:00:14
 */
public class MemberServiceImpl {
    /**
     * 模拟 DB 初始化点数据
     */
    public static final Map<Long, Member> db = new HashMap<>();

    static {
        db.put(1L, new Member(1L, "张三"));
        db.put(2L, new Member(2L, "李四"));
    }

    public Member getMember(Long id) {
        Member member = db.get(id);
        System.out.println(member);
        return member;
    }

    public List<Member> listMember() {
        ArrayList<Member> members = new ArrayList<>(db.values());
        System.out.println(members);
        return members;
    }

    public void saveMember(Long id, String name) {
        db.put(id, new Member(id, name));
    }

    public void updateMember(Long id, String name) {
        db.put(id, new Member(id, name));
    }

    public void deleteMember(Long id) {
        db.remove(id);
    }
}

/**
 * 动态代理工厂
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 13:47:40
 */
public class ProxyFactory implements MethodInterceptor {

    public MemberServiceImpl getProxyObject() {
        Enhancer enhancer = new Enhancer();
        //设置父类的字节码对象
        enhancer.setSuperclass(MemberServiceImpl.class);
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        return (MemberServiceImpl) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理类操作 member 功能:" + method.getName());
        return methodProxy.invokeSuper(o, objects);
    }
}

/**
 * 测试类
 *
 * @author Eajur.Wen
 * @version 1.0
 * @date 2022-11-11 06:28:01
 */
public class App {
    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory();
        MemberServiceImpl proxyObject = proxyFactory.getProxyObject();
        proxyObject.saveMember(3L, "王五");
        System.out.println("--------------");
        proxyObject.getMember(1L);
        System.out.println("--------------");
        proxyObject.listMember();
        System.out.println("--------------");
        proxyObject.deleteMember(1L);
    }
}

执行结果:

这里需要注意,CGLib 生成代理类的本质是生成原始类的子类,因此无法代理被 final 修饰的类,或者被 final 修饰的方法,且会将原始类可被继承的方法完全暴露给客户端。

这里简单提一下,在 JDK 1.8 过后,JDK 动态代理效率高于 CGLib 动态代理,Spring AOP 中也是根据需代理类的方法有无接口来优先判断是否使用 JDK 动态代理。

标签:11,软件设计,代理,模式,public,Long,new,白话文,id
From: https://www.cnblogs.com/eajur/p/16881223.html

相关文章

  • Scala模式匹配
    1 switch与default等效的是捕获所有的case_模式。如果没有模式匹配,抛出MatchError,每个case中,不用break语句。可以在match中使用任何类型,而不仅仅是数字。varresult=0;......
  • 【Grpc(二)】两种stub, 四种模式(unary,客户端stream,服务端strea)示例
    protobuff定义:syntax="proto3";packagecom.liyao;optionjava_package="com.liyao.protobuf.test.service";optionjava_outer_classname="MyServiceProto";optionj......
  • 单例模式
    publicclassSingleton1{//饿汉式privateSingleton1(){};staticSingleton1singleton1=newSingleton1();publicstaticSingleton1getSingleton1()......
  • 设计模式学习(十一):组合模式
    设计模式学习(十一):组合模式作者:Grey原文地址:博客园:设计模式学习(十一):组合模式CSDN:设计模式学习(十一):组合模式组合模式组合模式是一种结构型模式。组合模式中,最常用的一......
  • 初识设计模式 - 解释器模式
    简介在某些情况下,为了更好地描述某一些特定类型的问题,我们可以创建一种新的语言,这种语言拥有自己的表达式和结构,即文法规则。解释器设计模式(InterpreterDesignPattern)......
  • VSCode如何让先前打开的文件不被自动关闭,一直保持在标签栏里(关闭预览模式)
    https://bbs.huaweicloud.com/blogs/320859 第一次接触VSCode-HuaweiIDE编辑器,每次打开一个新的代码文件,旧的代码文件都会被自动关闭(现在才知道是因为文件默认是以预览......
  • 设计模式学习(十):门面模式
    设计模式学习(十):门面模式作者:Grey原文地址:博客园:设计模式学习(十):门面模式CSDN:设计模式学习(十):门面模式门面模式门面模式是一种结构型模式。门面模式为子系统提供一组统......
  • 工厂模式案例解释
    1.1工厂模式​ 工厂模式一般分为简单工厂、工厂方法、抽象工厂,那么什么是简单工厂模式?工厂方法?抽象工厂模式?先看例子,再去看概念和UML图。举例:假设现在有一个项目要......
  • 14.严格模式
    严格模式JS运行代码的模式有两种:正常模式默认情况下代码都运行在正常模式下在正常模式,语法检查并不严格它的原则是,能不报错的地方尽量不报错这种处理方式导致代码......
  • [DPDK] 混杂模式
    [DPDK]混杂模式通常来讲,当一个网卡收到的包的目标MAC地址不是这个网卡的MAC地址时,网卡会无视这个包。如果想让网卡可以收到destMAC是任意地址的包,需要开启DPDK的混杂模......