0%

JAVA代理学习

接口

基本格式

[可见度] interface 接口名称 [extends 其他的接口名] {
        // 声明变量
        // 抽象方法
}

//接口类的实现
...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...

例子

package com.xxxx.helloworld;
//import java.lang.*;

//调用接口
public class Hello {
    public static void main(String[] args) {
        Helloimplements m = new Helloimplements();
        m.sayHello("aaa");
    }
}

//定义接口
interface IHello {

    void sayHello(String name);

    void sayGoogBye(String name);

}
//定义类
class Helloimplements implements IHello {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name);
    }
    @Override
    public void sayGoogBye(String name) {
        System.out.println(name+" GoodBye!");
    }
}

实际用途引入

  • 假设我们需要在每次问候他人的时候做一个日志记录,这时候我们只需要在Helloimplements加一个写日志的方法即可,但是如果这个Helloimplements是在封装好的JAR里面呢,我们没法修改代码呢?

静态代理

  • 为了实现上述需求我们可以使用静态代理,创建一个新的类作为代理类,具体实现如下,虽然有点像继承,但是本质上还是有所区别,代理的是接口而非类。也就是将一个接口实例化成对象后传入代理类中,代理类相对应的(名称一致)的方法上进行调用对应方法,然后添加我们想增加的功能。这就是所谓的静态代理。
package com.xxxx.helloworld;
//import java.lang.*;


public class Hello {
    public static void main(String[] args) {
        Helloimplements m = new Helloimplements();
        StaicProxy proxy = new StaicProxy();
        proxy.setImpl(m);
        proxy.sayHello("黄莉荃");
    }
}


interface IHello {

    void sayHello(String name);

    void sayGoodBye(String name);

}

class Helloimplements implements IHello {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name);
    }
    @Override
    public void sayGoodBye(String name) {
        System.out.println(name+" GoodBye!");
    }
}

class StaicProxy implements IHello{
    private IHello iHello;
    public  void setImpl(IHello impl){
        this.iHello=impl;
    }
    @Override
    public void sayHello(String name) {
        System.out.println("logs:SayHello");
        iHello.sayHello(name);
    }

    @Override
    public void sayGoodBye(String name) {
        System.out.println("logs:SayGoodBye");
        iHello.sayHello(name);
    }
}

动态代理

  • 动态代理(dynamic proxy)同样需要一个代理类来代理接口,其中比较关键的是newProxyInstance这个方法来实现代理:
newProxyInstance
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
       throws IllegalArgumentException
  • 参数:

loader: 用哪个类加载器去加载代理对象

interfaces:动态代理类需要实现的接口

h:动态代理方法在执行时,会调用h里面的invoke方法去执行

  • 实例:

    • 在代理类中使用一个bind方法来调用newProxyInstance,因为传入的是接口实例化后的对象,所以形参也是Object类型,然后我们将newProxyInstance返回的对象return.
       public Object bind(Object delegate){
            this.delegate = delegate;
            return  Proxy.newProxyInstance(
              this.delegate.getClass().getClassLoader(),this.delegate.getClass().getInterfaces(),this);
        }
InvocationHandler
  • 和前面静态代理一样我们需要定义一个代理类,这个类需要implementsInvocationHandler,我们在使用newProxyInstance返回的对象调用相应的方法时候,InvocationHandler会自动调用invoke方法。所以在代理类里面需要定义一个invoke方法。
invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("记录日志");
        Object result = method.invoke(vehical, args);
        return result;
    }
  • 参数

proxy:就是代理对象,newProxyInstance方法的返回对象

method:调用的方法

args: 方法中的参数

实例
package com.xxxx.helloworld;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;



public class Hello {
    public static void main(String[] args) {
        Helloimplements m = new Helloimplements();
        DynaProxy proxy = new DynaProxy();
        IHello ihello = (IHello) proxy.bind(m);
        ihello.sayHello("黄莉荃");
    }
}


interface IHello {
    void sayHello(String name);
    void sayGoodBye(String name);
}

class Helloimplements implements IHello {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name);
    }
    @Override
    public void sayGoodBye(String name) {
        System.out.println(name+" GoodBye!");
    }
}
class DynaProxy implements InvocationHandler{
    private Object delegate;
    public Object bind(Object delegate){
        this.delegate = delegate;
        return  Proxy.newProxyInstance(
          this.delegate.getClass().getClassLoader(),this.delegate.getClass().getInterfaces(),this);
    }
    public Object invoke(Object proxy,Method method,Object[] args ) throws  Throwable{
        Object result =null;
        try{
            String originalName = method.getName();
            if(originalName.equals("sayHello")){
                System.out.println("logs:sayHello");
            }else if(originalName.equals("sayGoodbye")){
                System.out.println("logs:sayGoodbye");
            }

            result = method.invoke(this.delegate,args);
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

}
分析
  • 我们在bind处进行断点,可以看到Helloimplements对象传入。并赋值给delegate

WX20201114-133749@2x

  • 跳到下一步可以看到调用了Proxy.newProxyInstance.

WX20201114-143013@2x

  • 我们断点在ihello.sayHello("黄莉荃");可以看到虽然ihello的静态类型是IHello但是实际的数据类型是$Proxy0.说明这个变量是被JVM加工过的。

WX20201114-144902@2x

  • 当这个被JVM加工过的变量的sayHello或者sayGoodbye方法被调用时候,将由JVM自动转交到invoke下去。

WX20201114-145321@2x