首页 > 编程语言 >Java 设计模式:代理模式

Java 设计模式:代理模式

时间:2022-11-24 15:33:54浏览次数:39  
标签:Java Target 对象 代理 proxy 设计模式 方法 public

目录

代理模式(Proxy Pattern)

概述

所属:结构性模式,提供了对目标对象另外的访问方式。

适用时机:控制对象的访问,或增加访问对象的功能。

优点:

  • 隐藏了目标对象,使用者根本就不会感知到目标对象的存在。
  • 不改变目标对象结构。

实现

Java 代理模式实现可分为两类:静态代理,动态代理。

静态代理

特点:代理关系在编译期确定。

要求:目标对象继承某个目标接口。

实现方式:代理对象实现目标接口的所有方法代理对象内含目标对象实例,实现方法时可调用目标对象的方法并在自己实现方法的过程中添加相应的操作,以达到增强目标对象的方法的目的。

弊端:静态代理虽然实现了对目标对象的功能扩展,但由于它要实现目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。具有重复性脆弱性

示例

目标接口 interface ITarget,与其实现 class Target:

public interface ITarget {
    void call();
}

public class Target implements ITarget {

    @Override
    public void call() {
        System.out.println("I'm real target.");
    }

}

代理类:

public class Proxy implements ITarget{

    private Target target;
    
    AProxy() {
        this.target = new Target();
    }

    @Override
    public void call() {
        // 如果需要扩展方法,在这里实现。
        aTaget.call();
        System.out.println("I'm proxy A.");
    }
    
    // 你也可以添加其它接口
    public void extraMethod() {
        System.out.println("I'm extra method.");
    }
    
}

我们使用时,即使用对应的代理类:

public class ProxyPatternDemo {

    public static void main(String[] args) {
		Proxy proxy = new Proxy();
        
        // 调用代理方法
        proxy.call();
        // 调用额外方法
        proxy.extraMethod();

    }
}

动态代理

特点:代理关系在运行时确定。

JVM 根据传进来的业务实现类对象及方法名,在运行时生成代理类的字节码,即 class 文件,随后被字节码引擎执行,然后通过该代理类对象进行方法调用。

动态代理的实现方式有很多,这里主要讲述 JDK 动态代理与 CGLib 动态代理。

JDK 动态代理

核心 API:java.lang.reflect.Proxy、java.lang.reflect.InvocationHandler

特点/实现原理:

  • 利用反射机制
  • 基于接口。

缺点:

  • JDK 动态代理有一个最致命的问题是它只能代理实现了某个接口的实现类,并且代理类也只能代理接口中实现的方法,要是实现类中有自己私有的方法,而接口中没有的话,该方法不能进行代理调用。
示例
public class JDKProxyDemo {

    public static void main(String[] args) {

        // 目标对象
        final Target target = new Target();
        
        // 生成代理对象
        Class<? extends Target> targetClass = target.getClass();
        ITarget proxy = (ITarget) Proxy.newProxyInstance(targetClass.getClassLoader(), targetClass.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                
                // 这里拓展原来的方法
                System.out.println("extra method.");
                Object invoke = method.invoke(target, args);
                return invoke;
            }
        });

        // 调用代理对象方法
        proxy.call();
    }
}
源码分析

java.lang.reflect.Proxy 的 newProxyInstance 方法源码中,最重要的是以下三条 statements:

这三条语句的作用:获得代理类的 class 对象、获得构造器、返回实例。

其中,InvocationHandler 最终被作为 java.lang.reflect.Constructor 中 sun.reflect.ConstructorAccessor constructorAccessor 的 newInstance 方法参数:

而 constructorAccessor 来自于 sun.reflect.ReflectionFactory 的 newConstructorAccessor 。所以 newInstance 最终调用的是以下几个实例的 newInstance:

CGLib 动态代理

核心 API:net.sf.cglib.proxy.Enhancer、net.sf.cglib.proxy.MethodInterceptor

特点/实现原理:

  • 基于ASM

  • 代理类去继承目标类,然后重写其中目标类的方法,同时设置拦截器,调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑。

注意:需要导入依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>{cglib.version}</version>
</dependency>
示例
public class CGLibProxyDemo {

    public static void main(String[] args) {

        // 目标对象
        final Target target = new Target();
        
        // 生成代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Target.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            // 这里拓展原来的方法
            System.out.println("extra method.");
            Object invoke = method.invoke(target, objects);
            return invoke;
        });
        Target proxy = (Target) enhancer.create();

        // 调用代理对象方法
        proxy.call();
    }
}
源码分析

// TODO 24/11/2022 meyok: CGLib 源码分析

业界实践

Spring 默认使用 JDK 动态代理实现 AOP,同时也可以使用 CGLib 动态代理实现 AOP。

Spring 本身 AOP 不如 AspectJ 强大,Spring 融入 AspectJ 可添加以下依赖:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

标签:Java,Target,对象,代理,proxy,设计模式,方法,public
From: https://www.cnblogs.com/meyok/p/16922018.html

相关文章

  • Java篇—实现快递鸟API查询接口签名代码大全!
    本文章为Java与常用的API查询接口签名代码大全,复制代码可直接“食用"。具体实操可以搜抖音(快递鸟)查看视频教程。此文章供各位程序员学习参考,后续我将会继续分享各语言的快递......
  • java 去除多余逗号方法
    java去除多余逗号方法//测试数据Stringdata=",6G+128G,标准版,,时光静紫,"//将组装好的数据分割String[]fmtSplit=data.split(",");//利用stream流过滤到......
  • java多线程(一)
    初始化线程的四种方式1、继承Threadpublicstaticvoidmain(String[]args){System.out.println("main....statt");newThread01().start();Sy......
  • 使用selenium定时爬取网页内容-java版本
    使用场景某些网页有反扒机制,使用jsoup和httpclient不能满足要求,使用selenium可以。环境配置​​https://registry.npmmirror.com/binary.html?path=chromedriver/​​下载解......
  • java LinkedList , ArrayDeque, ArrayList区别
    linkedlist  既实现了 list接口,又实现了 queue,deque接口, 底层用链表数据结构,便于增删元素和顺序迭代arraydeque 实现了 queue和deque接口,底层用数组实......
  • 新建java项目及案例 练习
    1.打开IDEA软件2.在工具栏中点击File,下拉框中找到New-》Project  3.点击Maven-》NEXT 4.显示以下界面,修改名称,点击Finish完成 5.显示以下界面,选择pom.xml文......
  • java proguard混淆通配符
    类名通配符如下:|通配符|含义||---||?|匹配单个字符,包名分隔符(.)除外||*|匹配除(.)外的任意字符||**|匹配任意字符(包含.),如com.rush.**匹配com.rush包下......
  • 【Java Servlet 开发系列之一】在mac系统安装Apache Tomcat的详细步骤
    本站文章均为​​ 李华明Himi ​​​原创,转载务必在明显处注明:对于ApacheTomcat估计很多童鞋都会,那么今天就简单说下在mac上进行tomcat的安装;  第一步:下载Tomcat ......
  • java 基础——数组
    什么是数组?官方定义:数组(Array)是有序的元素序列。  简单来说:可以把数组想象成一个线性数据结构,用来装东西的,每个东西有自己的编号,并且编号是从0开始(重点) 直接来看......
  • 匆匆那年之Java程序员之最近两周的面试总结:
    匆匆那年之Java程序员之最近两周的面试总结:(一):匆匆那年之来帝都之初:还记得那是2011年的冬天,我们一行20多个同学一起来到了这个一直向往的城市首都,刚到北京是凌晨4点30,......