动态代理方法解析

金莲想happy

package proxy;
public class Jinlian implements IHappy{
    @Override
    public void happy() {
        System.out.println("金莲正在happy...");
    }
}

阎婆惜也想happy

package proxy;
 class Yanpoxi implements IHappy{
    @Override
    public void happy() {
        System.out.println("阎婆惜正在happy...");
    }
}

她们俩有共同的接口,定义了一个happy方法,分别实现

package proxy;
public interface IHappy {
    void happy();
}

但是她们只想happy,不想打扫房间,退房
那怎么办?找别人帮忙打扫,退房不就完事了么
找王婆好了

import org.junit.Test;
import proxy.IHappy;
import proxy.Jinlian;
import proxy.MyInvocationHandler;

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

public class Client {
    /**
     * 实现类完成动态代理
     */
    @Test
    public void test01() {
        //1.创建委托者对象
        Jinlian jinlian = new Jinlian();
        //2.获取委托者对象的字节码文件对象
        Class clazz = jinlian.getClass();
        //3.使用jdk的动态代理,创建代理者的对象
        //方法的三个参数分别是:
        //3.1. classLoader类加载器
        //3.2. 要代理的接口的字节码对象数组
        //3.3. InvocationHandler的实现类对象,真正定义代理的细节在这个实现类中定义,我们可以使用匿名内部类
        //      也可以使用实现类实现invocationHandler接口,传入委托者对象
        //代理对象的类型应该是被代理的接口类型拿,到了代理对象,需要强转为接口类型。
        IHappy proxyInstance =
                (IHappy) Proxy.newProxyInstance(clazz.getClassLoader(),
                        clazz.getInterfaces(),
                        new MyInvocationHandler(jinlian));
        
        //我们的目标是:代理金莲的happy方法
        //我们调用的应该是代理者的happy()方法
        proxyInstance.happy();
    }
    /**
     * 匿名内部类完成动态代理
     */
    @Test
    public void test02(){
        //1.获取委托者对象
        Jinlian jinlian = new Jinlian();
        //2.拿到委托者对象的字节码文件对象
        Class clazz = jinlian.getClass();
        
        /*3.Proxy的newProxyInstance方法
            参数1.委托者对象类加载器
            参数2.委托者对象字节码数组对象
            参数3.匿名内部类invocationHandler
        */
        IHappy proxyInstance = (IHappy) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                clazz.getInterfaces(),
                new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //4.1用method.getName()判断方法名字是否是需要的happy方法
                if(method.getName().equals("happy")){
                    //5.1前置加强
                    System.out.println("王婆帮忙开好房间");
                    //5.2代理者调用委托者原本方法
                    method.invoke(jinlian,args);
                    //5.3后置加强
                    System.out.println("王婆帮忙打扫房间退房");
                    return null;
                }else{
                    return method.invoke(jinlian,args);
                }
                
            }
        });
        //上面只是拿到了加强以后的代理对象,
        //还需要调用happy方法告诉加强的代理对象该执行任务并加强了
        proxyInstance.happy();
    }
}

自定义的增强类

package proxy;

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

public class MyInvocationHandler implements InvocationHandler{
    //4.然后invocationHandler的实现类有一个成员变量,也就是调用者(委托者)
    //  成员变量为接口类型,由构造方法传入调用者的接口也可以说是父类对象women
    //  因为不确定是哪种类型,但知道都继承了接口IHappy,所以用多态的方式接收委托者
    private IHappy women;
    //构造方法,用于接收委托者对象,传入的是jinlian,拿到的是IHappy接口的women对象
    public MyInvocationHandler(IHappy women) {
        this.women = women;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /*1. invoke方法什么时候执行: 代理者调用任何方法都会执行,所以我们在invoke方法中编写具体的代理规则
        2. invoke方法的三个参数是什么意思:
              2.1.Object proxy,也就是拿到的代理者对象本身,不要去使用他,会发生递归
              2.2. Method method,拿到的代理者对象将要调用的委托者的方法,
              2.3. args 表示代理对象调用委托者的方法时候传入的参数
        3.此时我们要对happy()方法进行代理,其它方法不代理(调用委托者原本的方法)
              3.1 判断该方法是不是happy()方法
              method.getName()拿到代理者要代理的委托者的方法的名字,判断与happry是否相同
              因为我们这里只增强happy方法,而传进来的委托者可能有多个方法。
        
        */
        if (method.getName().equals("happy")) {
            //如果是happy方法则进行增强
            //3.2前置增强
            System.out.println("开好房间准备happy...");
            //3.3调用委托者的该方法
            method.invoke(women,args);
            //3.4后置增强
            System.out.println("打扫房间...");
            //return null表示方法结束,不会执行后面的代码。因为已经执行了method.invoke(women,args);所以这里不需要返回任何东西
            return null;
        }
        //方法不需要被代理,那么就执行委托者原本的方法
        return method.invoke(women,args);
        //可以把method.invoke(women,args)简单的理解为直接调用该方法。参数和普通调用一样。
    }
}

解析

一.实现对象

首先创建委托者的class对象
Class clazz = jinlian.getClass();
然后用Proxy类的newProxyInstance方法
传入三个参数

  1. 类加载器ClassLoader loader,
    clazz.getClassLoader()
  2. 类实现?Class<?>[] interfaces,
    clazz.getInterfaces()
  3. 匿名内部类InvocationHandler h
    或实现类
    new MyInvocationHandler(jinlian)

强转为委托人和代理人共同的接口类型
IHappy proxyInstance = (IHappy)Proxy.newProxyInstance(参数1,参数2,参数3);
proxyInstance 代理对象执行方法。

二.实现类

上述 参数3-invocationHandler的实现类有一个成员变量,也就是调用者(委托者)
接口类型,由构造方法传入调用者的接口也可以说是父类对象women
,因为不确定是哪种类型,但知道都继承了接口IHappy,所以用多态的方式接收委托者

private IHappy women;
//构造方法
public MyInvocationHandler(IHappy women) {
    this.women = women;

//invocationHandler接口的默认方法invoke(参数1,参数2,参数3)
public Object invoke(Object proxy, Method method, Object[] args) {}
//1. invoke方法什么时候执行: 代理者调用任何方法都会执行,所以我们在invoke方法中编写具体的代理规则
    参数1 Object proxy,也就是拿到的代理者对象本身,不要去使用他,会发生递归
    参数2 Method method,拿到的代理者对象将要调用的委托者的方法,
    参数3 args 表示代理对象调用委托者的方法时候传入的参数

//method.getName()拿到代理者要代理的委托者的方法的名字,判断与happry是否相同
因为我们这里只增强happy方法,而传进来的委托者可能有多个方法。
if (method.getName().equals("happy")) {
            //如果是happy方法则进行增强
            //前置工作
            System.out.println("开好房间准备happy...");
           
            //调用委托者的该方法,传入参数,
1,委托者对象,决定要调用哪个委托者的方法
2.调用该方法时需要的参数列表,这些都在代理的时候通过反射拿到了。
            method.invoke(women,args);
            
            //后置工作
            System.out.println("打扫房间...");
            return null;
}
//方法不需要被代理,那么就执行委托者原本的方法
        return method.invoke(women,args);
}

可以把method.invoke(women,args)简单的理解为直接调用该方法。参数和普通调用一样。

或匿名内部类里有

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值