JDK动态代理和实现原理初步

文章由B站动力节点相关课程视频整理而成。

代理模式

为其它对象提供一个代理以控制对这个对象的访问。在某些情况下,一个对象不适合或不能直接访问另一个对象,而代理可以在客户类与目标对象之间起到中介的作用。

具有这种访问关系呈现出来的模式称之为代理模式。

不能直接访问
客户类A
目标对象C
代理B

B是代理类,能够访问到C,也能增加新的业务功能,增强对C的访问。

代理的作用:

  • 功能增强:在原有功能的基础上增加了额外的功能。
  • 控制访问:代理类不让客户能直接访问目标对象。

代理的实现:

  • 静态代理(如下图所示):
    代理类是手工创建的一个java类,所要代理的目标是确定的。代理类包含了目标对象,在对目标对象的方法进行调用时可以进行功能增强。
    优点:实现简单,容易理解
    缺点:当项目中的目标类与代理类数量增多时,修改接口中的方法会使工作量增大。
实现
实现
实现
实现
客户类A
淘宝代理类B
拼多多代理类C
金士顿U盘
厂家D
闪迪U盘
厂家E
接口F:
出售U盘

代理类B、C和目标类D、E均实现了接口F中抽象方法出售U盘的功能。但厂家D、E不向个人客户A直接出售,只向代理商家B、C。某一天,F新增出售移动硬盘的功能,类B、C、D、E均要修改。

  • 动态代理
    当在接口中修改方法时,不用修改代理类

动态代理

在程序执行过程中,JDK利用反射机制创建对象能力创建代理对象,不用创建代理类文件。代理目标不是确定的,而是可活动的,变化的。
动态代理的实现:

  • JDK动态代理:使用java反射包中的类和接口实现动态代理的功能。
    反射包:java.lang.reflect
    类:InvocationHandler(接口)、Method、Proxy
  • cgLib代理:第三方的开源项目,高效的code生成类库,可以在运行期间扩展java类,实现java接口,被广泛的AOP框架使用

使用JDL动态代理,要求目标类与代理类实现相同的接口。若目标类无接口,则不能使用该方法实现
此时可以考虑使用cgLib。cgLib代理的生成原理使生成目标类的子类,子类对象即代理对象。因此目标类不能是final的。

JDK动态代理的实现

package com.yang.service;
//目标接口,定义目标类实现的功能
//JDK动态代理要求代理目标必须实现接口
public interface ISomeService {

    void doService ();
}
package com.yang.service;

public class SomeService implements ISomeService {
    @Override
    public void doService() {
        System.out.println("SomeServie类的doService方法执行," +
                "此方法满足了一定的业务需要。");
    }
}
package com.yang.proxyHandler;

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

//创建InvocationHandler的实现类,在invoke方法中实现代理功能

public class myInvocationHandler implements InvocationHandler {

    //动态代理的目标对象
    private Object target = null;

    //动态代理的目标对象通过构造方法传入,可以根据需要传入不同的对象,
    //而不是唯一确定的,这就是“动态”的含义
    public myInvocationHandler(Object target) {
        this.target = target;
    }

    /*java.lang.reflect.InvocationHandler中的方法
            此方法实现了对原有代理对象的业务方法的调用,
            即SomeServie类的doService方法的调用
            并在此基础上可以选择进行一定的功能增强
            此方法的参数均为JDK管理,不需要人工传入
            Object proxy:目标类的代理对象
            Method method:目标类的业务方法对象
            Object[] args:目标类的业务方法所需要的参数
                (例如由于本例doService没有参数,args为空)
         */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.调用目标方法
         method.invoke(target,args);

         //2.实现功能增强
        System.out.println("myInvocationHandler类的invoke方法执行," +
                "在原有业务方法结束后进行功能增强");

        //如果代理的目标对象有返回值,那就要在“1.调用目标方法”中接收返回值
        //在有必要的情况下进行强值类型转换,并在“2.实现功能增强”中修改
        //最后在此处返回。
        //本例中目标方法(doservice)无返回值
        return null;
    }
}
package com.yang;

import com.yang.proxyHandler.myInvocationHandler;
import com.yang.service.ISomeService;
import com.yang.service.SomeService;

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

/**
 * Hello world!
 *  */
public class App 
{
    public static void main( String[] args )
    {

        //1.创建目标对象
        ISomeService target = new SomeService();

        //2.创建InvocationHandler对象,
        //构造器参数决定了代理目标,传谁代理谁
        InvocationHandler handler = new myInvocationHandler(target);

        //3.获取代理对象,并将返回值转为接口类型
        //强转成功的原因在于目标对象target实现了ISomeService接口
        ISomeService proxy = (ISomeService) Proxy.newProxyInstance(
                //第一个参数是目标类的类加载器,可看作固定写法
                target.getClass().getClassLoader(),
                //第二个参数是目标类的接口对象,可看作固定写法
                //正是因为这个参数决定了JDK动态代理必须实现接口
                target.getClass().getInterfaces(),
                //第三个参数是包含目标类的InvocationHandler对象
                handler);

        //4.实现代理的业务功能
        proxy.doService();

    }
}

动态代理的好处

在实际开发中,将日志、事务等与也业务方法无关、但很多业务方法都要执行的代码放在InvocationHandler类invoke方法的功能增强中,这样一来:

  • 解耦合,使业务代码与非业务代码分离,业务类专注于业务的实现。
  • 减少代码复用。
  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值