Java代理模式汇编

本文详细介绍了Java代理模式的实现方式,包括静态代理、基于JDK和CGlib的动态代理。静态代理在编译时创建代理类,而动态代理则在运行时动态生成。JDK动态代理依赖于Proxy类和InvocationHandler接口,适用于实现了接口的类,而CGlib基于类继承实现。此外,文章还讨论了AOP在动态代理中的应用和各种代理的使用场景。
摘要由CSDN通过智能技术生成

文章目录

Java 代理模式实现方式,主流如下五种方法

  • 静态代理,工程师编辑代理类代码,实现代理模式;在编译期就生成了代理类。

  • 基于 JDK 实现动态代理,通过jdk提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并持有InvocationHandler接口引用 )字节码文件并实例化对象返回。(jdk动态代理是由java内部的反射机制来实例化代理对象,并代理的调用委托类方法)

  • 基于CGlib 动态代理模式 基于继承被代理类生成代理子类,不用实现接口。只需要被代理类是非final 类即可。(cglib动态代理底层是借助asm字节码技术

  • 基于 Aspectj 实现动态代理(修改目标类的字节,织入代理的字节,在程序编译的时候 插入动态代理的字节码,不会生成全新的Class )

  • 基于 instrumentation 实现动态代理(修改目标类的字节码、类装载的时候动态拦截去修改,基于javaagent) -javaagent:spring-instrument-4.3.8.RELEASE.jar (类装载的时候 插入动态代理的字节码,不会生成全新的Class )

Notes

  • 委托类 即指的是代理模式中的被代理对象
  • 代理类 指的是生成的代表委托类的一个角色

静态代理实现

静态代理是代理类在编译期间就创建好了,不是编译器生成的代理类,而是手动创建的类。在编译时就已经将接口,被代理类,代理类等确定下来。,软件设计中所指的代理一般是指静态代理,也就是在代码中显式指定的代理。

实现步骤

  • 委托类和代理类之间的约束接口Cat
  • 约束接口实现类 Lion,实现 Cat 接口,委托角色
  • 代理类实现 FeederProxy,实现Cat 接口,并含有一个 Cat接口引用属性。 代理角色,代理 cat接口属性引用实例的行为并可以新增公共逻辑
Cat接口
package org.vincent.proxy.staticproxy;

/**
 * @author PengRong
 * @package org.vincent.proxy.staticproxy
 * @date 2018/12/15 - 17:12
 * @ProjectName JavaAopLearning
 * @Description: 静态代理类接口, 委托类和代理类都需要实现的接口规范。
 * 定义了一个猫科动物的两个行为接口,吃东西,奔跑。
 * 作为代理类 和委托类之间的约束接口
 */
public interface Cat {
   
    public String eatFood(String foodName);

    public boolean running();
}

委托类 Lion
package org.vincent.proxy.staticproxy;


/**
 * @author PengRong
 * @package org.vincent.proxy.staticproxy
 * @date 2018/12/15 - 17:15
 * @ProjectName JavaAopLearning
 * @Description: 狮子 实现了猫科动物接口Cat, 并实现了具体的行为。作为委托类实现
 */
public class Lion implements Cat {
   
    private String name;
    private int runningSpeed;

    public String getName() {
   
        return name;
    }

    public void setName(String name) {
   
        this.name = name;
    }

    public int getRunningSpeed() {
   
        return runningSpeed;
    }

    public void setRunningSpeed(int runningSpeed) {
   
        this.runningSpeed = runningSpeed;
    }

    public Lion() {
   
    }

    @Override
    public String eatFood(String foodName) {
   
        String eat = this.name + " Lion eat food. foodName = " + foodName;
        System.out.println(eat);
        return eat;
    }

    @Override
    public boolean running() {
   
        System.out.println(this.name + " Lion is running . Speed :" + this.runningSpeed);
        return false;
    }
}

代理类角色(FeederProxy)
package org.vincent.proxy.staticproxy;

/**
 * @author PengRong
 * @package org.vincent.proxy.staticproxy
 * @date 2018/12/15 - 17:19
 * @ProjectName JavaAopLearning
 * @Description: 饲养员 实现Cat接口,作为静态代理类实现。代理狮子的行为。
 * 代理类中可以新增一些其他行为,在实践中主要做的是参数校验的功能。
 */
public class FeederProxy implements Cat {
   
    private Cat cat;

    public FeederProxy(){
   }
    public FeederProxy(Cat cat) {
   
        if (cat instanceof Cat) {
   
            this.cat = cat;
        }
    }

    public void setCat(Cat cat) {
   
        if (cat instanceof Cat) {
   
            this.cat = cat;
        }
    }

    @Override
    public String eatFood(String foodName) {
   
        System.out.println("proxy Lion exec eatFood ");
        return cat.eatFood(foodName);
    }

    @Override
    public boolean running() {
   
        System.out.println("proxy Lion exec running.");
        return cat.running();
    }
}


静态代理类测试
package org.vincent.proxy;

import org.vincent.proxy.staticproxy.Cat;
import org.vincent.proxy.staticproxy.FeederProxy;
import org.vincent.proxy.staticproxy.Lion;

/**
 * @author PengRong
 * @package org.vincent.proxy
 * @date 2018/12/15 - 18:31
 * @ProjectName JavaAopLearning
 * @Description: 静态代理类测试
 */
public class staticProxyTest {
   
    public static void main(String[] args) {
   
        Lion lion = new Lion();
        lion.setName("狮子 小王");
        lion.setRunningSpeed(100);
        /**
         * new 静态代理类,静态代理类在编译前已经创建好了,和动态代理的最大区别点
         */
        Cat proxy = new FeederProxy(lion);

        System.out.println(Thread.currentThread().getName()+" -- " + proxy.eatFood("水牛"));
        proxy.running();
    }
}


静态代理很好的诠释了代理设计模式,代理模式最主要的就是有一个公共接口(Cat),一个委托类(Lion),一个代理类(FeederProxy),代理类持有委托类的实例,代为执行具体类实例方法。
上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指客户端不直接调用实际对象的方法,客户端依赖公共接口并使用代理类。
那么我们在代理过程中就可以加上一些其他用途。 就这个例子来说在 eatFood方法调用中,代理类在调用具体实现类之前添加System.out.println("proxy Lion exec eatFood ");语句
就是添加间接性带来的收益。代理类存在的意义是为了增加一些公共的逻辑代码。

动态代理类(基于接口实现)

静态代理是代理类在代码运行前已经创建好,并生成class文件;动态代理类 是代理类在程序运行时创建的代理模式。

动态代理类的代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
想想你有100个静态代理类,现在有一个需求,每个代理类都需要新增一个处理逻辑,你需要打开100个代理类在每个代理方法里面新增处理逻辑吗? 有或者代理类有5个方法,每个方法都需要新增一个处理逻辑,
你需要在每个方法都手动新增处理逻辑吗? 想想就挺无趣的。动态代理类帮你一键搞定。

动态代理类涉及角色

  • 委托类和代理类实现的公共接口(Person.java)
  • 实现公共接口的具体委托类(SoftwareEngineer.java)
  • InvocationHandler接口被Proxy类回调处理,一般实现 InvocationHandler 接口的类具有委托类引用,接口方法 invoke 中添加公共代码并调用委托类的接口方法。(PersonInvocationHandler.java)
  • JDK提供生成动态代理类的核心类Proxy ( JDK 提供的Proxy.java)

基于JDK技术 动态代理类技术核心 Proxy类和一个 InvocationHandler 接口

java的java.lang.reflect包下提供了Proxy类和一个 InvocationHandler 接口,这个类Proxy定义了生成JDK动态代理类的方法 getProxyClass(ClassLoader loader,Class<?>... interfaces)生成动态代理类,返回class实例代表一个class文件。可以保存该 class 文件查看jdk生成的代理类文件长什么样

该生成的动态代理类继承Proxy类,(重要特性) ,并实现公共接口。

InvocationHandler这个接口 是被动态代理类回调的接口,我们所有需要增加的针对委托类的统一处理逻辑都增加到invoke 方法里面在调用委托类接口方法之前或之后 结束战斗。

案例
公共接口
package org.vincent.proxy.dynamicproxy;

/**
 * Created by PengRong on 2018/12/25.
 * 创建Person 接口 用于定义 委托类和代理类之间的约束行为
 */
public interface Person
{
   
    /**
     *
     * @param name 人名
     * @param dst 工作目的地
     */
    void goWorking(String name, String dst);

    /**
     * 获取名称
     * @return
     */
    String getName( );

    /**
     * 设置名称
     * @param name
     */
    void  setName(String name);
}

具体实现类,等下被委托,被代理的类 SoftwareEngineer.java
package org.vincent.proxy.dynamicproxy;

/**
 * Created by PengRong on 2018/12/25.
 * 动态代理委托类实现, 实现接口 Person。 被动态生成的代理类代理
 */
public class SoftwareEngineer implements Person{
   
    public  SoftwareEngineer(){
   }
    public  SoftwareEngineer(String name){
   
        this.name=name;
    }
    private  String name;

    public String getName() {
   
        return name;
    }

    public void setName(String name) {
   
        this.name = name;
    }

    @Override
    public void goWorking(String name, String dst) {
   
        System.out.println("name ="+name+" , 去 "+dst +" 工作");
    }
}

InvocationHandler 接口实现 PersonInvocationHandler.java
package org.vincent.proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * Created by PengRong on 2018/12/25.
 * PersonInvocationHandler 类 实现InvocationHandler接口,这个类中持有一个被代理对象(委托类)的实例target。该类别JDK Proxy类回调
 * InvocationHandler 接口中有一个invoke方法,当一个代理实例的方法被调用时,代理方法将被编码并分发到 InvocationHandler接口的invoke方法执行。
 */
public class PersonInvocationHandler<T> implements InvocationHandler {
   
    /**
     * 被代理对象引用,invoke 方法里面method 需要使用这个 被代理对象
     */
    T target;

    public PersonInvocationHandler(T target) {
   
        this.target = target;
    }


    /**
     * 在
     * @param proxy  代表动态生成的 动态代理 对象实例
     * @param method 代表被调用委托类的接口方法,和生成的代理类实例调用的接口方法是一致的,它对应的Method 实例
     * @param args   代表调用接口方法对应的Object参数数组,如果接口是无参,则为null; 对于原始数据类型返回的他的包装类型。
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
        /**
         * 在转调具体目标对象之前,可以执行一些功能处理
         */
        System.out.println("被动态代理类回调执行, 代理类 proxyClass ="+proxy.getClass()+" 方法名: " + method.getName() + "方法. 方法返回类型:"+method.getReturnType()
        +" 接口方法入参数组: "+(args ==null ? "null" : Arrays.toString(args)));
        /**
         * 代理过程中插入监测方法,计算该方法耗时
         */
        MonitorUtil.start();
        Thread.sleep(1);
        /** 调用呗代理对象的真实方法,*/
        Object result = method.invoke(target, args);
        MonitorUtil.finish(method.getName());
        return result;
    }
}

PersonInvocationHandler invoke 方法中添加的公共代码,这里简单以统计方法执行时间为逻辑
package org.vincent.proxy.dynamicproxy;

/**
 * Created by PengRong on 2018/12/25.
 * 方法用时监控类
 */
public class MonitorUtil {
   
    private static ThreadLocal<Long> tl = new ThreadLocal<>(
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值