当前位置: 代码迷 >> 综合 >> Kotlin-8-动态代理
  详细解决方案

Kotlin-8-动态代理

热度:25   发布时间:2023-10-09 05:50:00.0

目录

1、kotlin的动态代理实现

2、什么是代理?

3、什么情况下使用动态代理?

4、Java中动态代理实现的两种方式

5、静态代理和动态代理

6、静态代理的实现

7、JDK动态代理的实现


1、kotlin的动态代理实现

/*** 被代理类和代理类的共同接口*/
interface Animal {fun pick(voice: String): String}/*** 被代理类(target类)*/
class Dog : com.yobo.yobo_kotlin.test8_DaiLi.Animal {override fun pick(voice: String): String {println("被代理对象的叫声:$voice")val result = "$voice+2"return result}
}/*** 代理类的工具类,它的作用:当代理对象调用target类对象的方法的时候,会被分发到这个工具类的invoke()方法中,* 然后你在这里对target类的内部方法进行进一步的封装。*/
class ProxyAnimal_dong(private val mDog: Dog) : InvocationHandler {/*** @param proxy 调用该方法的代理实例(proxyDog就是代理实例)* @param method 代理实例(proxyDog)调用的方法,可通过method.getName()获取到方法的名称* @param args 代理实例(proxyDog)调用方法传入的参数数组。* @return 代理实例(proxyDog)调用的方法的返回值。*/@Throws(Throwable::class)override fun invoke(proxy: Any, method: Method, args: Array<Any>): Any? {//通过method我们就能知道代理实例调用的是哪个方法,然后进一步对其进行封装。//这里我们实现和上面静态代理一样的封装,给voice参数+1,给返回值+2+3if (method.name == "pick") {println("代理对象,调用了pick(),参数args=" + args[0])var voice = args[0] as Stringvoice = "$voice+1"var result = mDog.pick(voice)println("被代理对象,调用它的内部方法的返回值,result=$result")result = "$result+3"return result}return null}}fun main() {val dog = Dog()val dong = ProxyAnimal_dong(dog)/*** 获取到代理对象* @param   loader 类加载器,这里可以传 被代理对象的类加载器* @param   interfaces 被代理对象实现的接口,这里可以传你只想拦截的那个方法的接口* @param   h 代理对象的工具类* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)**/val proxyDog =Proxy.newProxyInstance(dong.javaClass.classLoader, dog.javaClass.interfaces, dong) as Animalval result = proxyDog.pick("Wang")println("代理对象调用pick()返回值,result=$result")
}

 


2、什么是代理?

大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 A---->B—>C 这种模式。A可以不通过直接与C对话的情况下,而通过B与其产生间接对话。

具体了解动态代理可以看这里

3、什么情况下使用动态代理?

        1、需要对较难修改的类方法进行功能增加。
        2、RPC即远程过程调用,通过动态代理的建立一个中间人进行通信。
        3、实现切面编程(AOP)可以采用动态代理的机制来实现。

4、Java中动态代理实现的两种方式

jdk动态代理和cglib动态代理。

两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

原理:jdk动态代理是jdk原生就支持的一种代理方式,它的实现原理,就是通过让target类和代理类实现同一接口,代理类持有target对象,来达到方法拦截的作用,这样通过接口的方式有两个弊端,一个是必须保证target类有接口,第二个是如果想要对target类的方法进行代理拦截,那么就要保证这些方法都要在接口中声明,实现上略微有点限制。  

5、静态代理和动态代理

        根据加载被代理类的时机不同,将代理分为静态代理和动态代理。编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;运行时才确定被代理的类是哪个,那么可以使用类动态代理。

6、静态代理的实现

         1、JDK的实现的动态代理的限制条件之一就是需要目标类实现了接口,那么静态代理的target类也需要实现接口。

//统一接口
public interface Animal {String pick(String voice);
}

   2、被代理类(target类)的实现。

public class Dog implements Animal {@Overridepublic String pick(String voice) {System.out.println("被代理对象的叫声:"+voice);voice = voice + "+2";return voice;}
}

3、静态代理类的实现,我们使用代理类的一个目的就是对target(被代理)类的内部方法进行一些功能的增加,这里我对参数和返回值进行了功能增加

public class ProxyAnimal implements Animal {private Dog dog;public ProxyAnimal(Dog dog) {this.dog = dog;}@Overridepublic String pick(String voice) {System.out.println("代理层叫声:"+voice);String newVoice = voice + "+1";String result=dog.pick(newVoice);result = result + "+3";return result;}
}

4、输出结果,可以看出,通过代理的形式,我们可以对target类的内部方法进行参数或者返回值的进一步封装,从而实现代码的复用。

代理层叫声:Wang
被代理对象的叫声:Wang+1
总的返回结果:Wang+1+2+3

静态代理的实现方式比较暴力直接,需要将所有被代理类的所有方法都写一遍,并且一个个的手动转发过去。在维护被代理类的同时,作为java码工还需要同时维护代理类的相关代码,比较麻烦。因此就需要我们的动态代理登场了,通过使用动态代理,动态代理能够自动将代理类的相关方法转发到被代理类。

7、JDK动态代理的实现

       我们还实现上面静态代理实现的效果        

1、每个动态代理的代理对象都需要实现它的工具类。也就是实现一个继承InvocationHandler接口的类,它的作用:当代理对象调用target对象的方法的时候会被分发到这个工具类的invoke()方法中,然后你在这里对target类的内部方法进行进一步的封装。

public class ProxyAnimal_dong implements InvocationHandler {private Dog mDog;public ProxyAnimal_dong(Dog mDog) {this.mDog = mDog;}/*** @param proxy 调用该方法的代理实例(proxyDog就是代理实例)* @param method 代理实例(proxyDog)调用的方法,可通过method.getName()获取到方法的名称* @param args 代理实例(proxyDog)调用方法传入的参数数组。* @return 代理实例(proxyDog)调用的方法的返回值。*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//通过method我们就能知道代理实例调用的是哪个方法,然后进一步对其进行封装。//这里我们实现和上面静态代理一样的封装,给voice参数+1,给返回值+2+3if (method.getName().equals("pick")) {System.out.println("代理对象,调用了pick(),参数args=" + args[0]);String voice = (String) args[0];voice = voice + "+1";String result = mDog.pick(voice);System.out.println("被代理对象,调用它的内部方法的返回值,result="+result);result = result + "+3";return result;}return null;}}

2、当然还有target类和它的接口

//统一接口
public interface Animal {String pick(String voice);
}
public class Dog implements Animal {@Overridepublic String pick(String voice) {System.out.println("被代理对象的叫声:"+voice);voice = voice + "+2";return voice;}
}

3、获取代理对象,然后用代理对象调用target类的方法

    public static void main(String[] args) {Dog dog=new Dog();ProxyAnimal_dong dong=new ProxyAnimal_dong(dog);/*** 获取到代理对象* @param   loader 类加载器,这里可以传 被代理对象的类加载器* @param   interfaces 被代理对象实现的接口,这里可以传你只想拦截的那个方法的接口* @param   h 代理对象的工具类* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)*            */Animal proxyDog = (Animal) Proxy.newProxyInstance(dong.getClass().getClassLoader(), dog.getClass().getInterfaces(), dong);String result=proxyDog.pick("Wang");System.out.println("代理对象调用pick()返回值,result="+result);}
}

4、返回值

代理对象,调用了pick(),参数args=Wang被代理对象的叫声:Wang+1被代理对象,调用它的内部方法的返回值,result=Wang+1+2代理对象调用pick()返回值,result=Wang+1+2+3