java中反射与代理的关系,动态代理InvocationHandler和Proxy的实现与剖析

继上次的文章,[反射、序列化与反序列之间的联系],本文进一步对反射和代理做分析和撸一下这方面的实现。本文主要是从java方面做实现。

目录

(1)反射与代理的关系

(1.1)JDK中提供的反射接口的使用

(2)代理的概念、实现与解析

(2.1)静态代理

(2.2)动态代理

(2.2.1)JDK代理

(2.2.2)具体内容实现剖析

 (2.3)cglib动态代理


(1)反射与代理的关系

反射如上次所说,就是在运行中,对任意一个类,通过全限定类名,都能够知道这个类的属性和方法。通过反射机制能够动态的调用任意一个类的任意方法和属性,这种动态的获取类信息以及动态调用方法的功能就称为java的反射机制
代理:如上的反射所说,在运行中需要动态的创建类的实例,也就是根据内存中加载的.class类来创建运行时的类信息,而在需要增强类实例或者增加额外功能的情况下,生成的.class文件无法改变,就可以使用动态代理的方式,为相应的对象提供一种通用的代理方式来提供对这个对象的访问。


(1.1)JDK中提供的反射接口的使用

jdk中提供了三种方式来获取类Class

1,Class.forName("com.lzm.controller.MainTestClassServiceImpl");//类的全称

2,Class MTSI=MainTestClassServiceImpl .class

3,MainTestClassServiceImpl mtsi = new MainTestClassServiceImpl();

     Class MTSI2=mtsi.getClass();


(2)代理的概念、实现与解析

内容略多,请大家耐心观看!!!
本文只从服务请求方面的代理进行介绍,代理分为静态代理动态代理,如上述中的反射中使用的就是动态代理,动态代理不需要在编译期就实现,并且往往可以代理多个类class。而静态代理只代理一个类class,由程序员编写,在编译器就生成相应的.class文件,由程序员调用
不论是静态代理或者是动态代理,其目的都是需要对某个类对象进行调用和访问的时候,来代替真实类对象进行调用和访问,在增强类对象或不适合直接调用时使用


(2.1)静态代理


如上述中所说,静态代理类往往只代理某个类,并且由程序员编写,在编译器时已经生成了相应的.class文件。程序员了解该类,但是别的用户调用时不能直接调用该类,使用中间代理类进行调用增强。

如下图所示,通过实现简单的静态代理类,来对相应的类对象进行增强和访问。

简单的实现如下:

//ProxyApplication.java
package com.lzm.proxy;

import com.lzm.controller.MainTestClassProxy;
import com.lzm.controller.MainTestClassServiceImpl;
import com.lzm.service.MainTestClassService;

public class ProxyApplication {

    public static void main(String[] args) {
        MainTestClassService target = new MainTestClassServiceImpl();
        MainTestClassProxy proxy = new MainTestClassProxy(target);
        proxy.HelloTest();
    }

}

//MainTestClassService.java
package com.lzm.service;

public interface MainTestClassService {
    public void HelloTest();
}

//MainTestClassServiceImpl.java
package com.lzm.controller;

import com.lzm.service.MainTestClassService;

public class MainTestClassServiceImpl implements MainTestClassService {

    @Override
    public void HelloTest() {
        System.out.println("你好,我是小明");
    }
}

//MainTestClassProxy.java
package com.lzm.controller;

import com.lzm.service.MainTestClassService;

public class MainTestClassProxy implements MainTestClassService {
    private MainTestClassService target;
    public MainTestClassProxy(MainTestClassService target){
        this.target = target;
    }
    @Override
    public void HelloTest() {
        System.out.println("我是中介代理,小明说:");
        target.HelloTest();
        System.out.println("代理结束");
    }
}

运行结果:

 如上,代理类MainTestClassProxy中实现了需要服务的类MainTestClassService,并在调用起到代理和增强的作用。

(2.2)动态代理

动态代理在运行中通过反射进行生成代理类,通过代理类进行相应的类对象访问。例如:Spring中的AOP增强,servlet的拦截器,运行时的部分注解也是在运行时反射,通过动态代理进行访问等等。动态代理一般开发中不常用。

常用的动态代理分为JDK代理CGLIB(第三方)代理.

(2.2.1)JDK代理

利用java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler接口定义动态代理类的实现。如下图所示,两个服务类,都可以通过代理类进行代理,与静态代理不同的是,MainTestClassProxy实现的是InvocationHandler的接口。

简单的实现如下: 

//ProxyApplication 
package com.lzm.proxy;

import com.lzm.controller.MainTestClassProxy;
import com.lzm.controller.MainTestClassServiceImpl;
import com.lzm.controller.SecondTestClassServiceImpl;
import com.lzm.service.MainTestClassService;
import com.lzm.service.SecondTestClassService;

public class ProxyApplication {

    public static void main(String[] args) {
        MainTestClassService target = new MainTestClassServiceImpl();
        MainTestClassProxy proxy = new MainTestClassProxy(target);
        MainTestClassService serviceproxy = (MainTestClassService)proxy.getProxy();
        serviceproxy.HelloTest();

        SecondTestClassService target2 = new SecondTestClassServiceImpl();
        MainTestClassProxy proxy2 = new MainTestClassProxy(target2);
        SecondTestClassService serviceproxy2 = (SecondTestClassService)proxy2.getProxy();
        serviceproxy2.HelloTest();
    }
}

//MainTestClassService 
package com.lzm.service;

public interface MainTestClassService {
    public void HelloTest();
}

//SecondTestClassService 
package com.lzm.service;

public interface SecondTestClassService {
    public void HelloTest();
}

//MainTestClassServiceImpl 
package com.lzm.controller;

import com.lzm.service.MainTestClassService;

public class MainTestClassServiceImpl implements MainTestClassService {

    @Override
    public void HelloTest() {
        System.out.println("你好,我是小明");
    }
}

//SecondTestClassServiceImpl 
package com.lzm.controller;

import com.lzm.service.SecondTestClassService;

public class SecondTestClassServiceImpl implements SecondTestClassService {
    @Override
    public void HelloTest() {
        System.out.println("我是小红");
    }
}

//MainTestClassProxy 
package com.lzm.controller;

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

public class MainTestClassProxy implements InvocationHandler {
    private Object target;
    public MainTestClassProxy(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是总代理,我给大家介绍");
        Object object = method.invoke(target , args);
        System.out.println("代理结束");
        return object;
    }

    public Object getProxy(){
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class[] interfaces = target.getClass().getInterfaces();
        return Proxy.newProxyInstance(loader , interfaces , this::invoke);
    }
}

运行结果:

如上代码所示,MainTestClassProxy类中实现InvocationHandler接口,Proxy生成代理对象,生成一个代理类,不通过实现固定的服务类方法

(2.2.2)具体内容实现剖析

既然要了解jdk的动态代理,那就从InvocationHandler接口和Proxy类继续往下深入:

  • (1) 使用的接口和类中的方法介绍

先简单介绍一下,上文中使用的类和接口,其中参数和返回值。

InvocationHandler接口是proxy类调用处理服务类的接口,其中只有一个方法,invoke方法:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

参数各自代表的意义:

  1. proxy:调用方法的代理实例
  2. method:我们所要调用某个对象当前的方法
  3. args:指代代理对象方法传递的参数
  4. 返回值可以是proxy,代理对象,也可以是method.invoke()方法封装好的数据对象

使用proxy类的newProxyInstance方法生成代理对象

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

参数各自代表的意义:

  1. loader:加载当前对象的类加载器
  2. interfaces:动态代理类需要实现的接口集合
  3. h:监听当前动态代理的对象调用的方法是哪个服务对象实现的InvocationHandler
  4. 返回对象就是一个代理类对象
  • (2) 以上述动态代理的代码为例具体实例分析

 第一幅图为代理类实现,第二幅图为主程序。主要内容包含如下:

  • (1) getProxy()方法生成代理对象

首先,生成target的实例化对象,将对象送入代理类生成proxy对象,(其中proxy实现InvocationHandler的接口),proxy调用getProxy()方法生成代理对象。

(1.1) proxy.newProxyInstance的第一个参数代理对象的类加载器,其中我在上文中使用的方法是:

        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class[] interfaces = target.getClass().getInterfaces();
        return Proxy.newProxyInstance(handler.getClass().getClassLoader() , interfaces ,this);

实际上真正调用的应该是:

handler为代理对象的调用处理程序,我们将要代理的真实对象target2传入代理对象的调用处理的构造函数中,构造代理对象

(1.2)proxy.newProxyInstance的第二个参数为需要实现的接口集合,因为需要实现的代理类可以实现多个服务接口,所以生成相应的代理类集合,生成相应的代理对象。如下图所示,实现多个接口,通过获取接口集合送入生成代理中。

(1.3)其中proxy.newProxyInstance的第三个参数代表是哪个服务类实现的InvocationHandler,本文代码是通过简写,在getProxy()方法中直接使用MainTestClassProxy的invoke的方法,实际上可以表示为如下的方法:

 就是通过绑定相应的实例化对象,生成相应的 InvocationHandler,送入newProxyInstance,生成实例化内容。

  •  (2) InvocationHandler调用相应方法

proxy.newProxyInstance调用InvocationHandler对象的invoke方法,来实现代理对象对真是对象方法的调用。

 (2.1)InvocationHandler接口中第一个参数,没有调用,为什么能代表调用方法的实例。我们进行简单输出可以看出。

这个proxy不是我们主动传入的,是jdk环境的参数,代表的是实际的代理对象。invoke()方法它可以返回Method方法的返回值,也可以返回真实的代理对象proxy进行操作。

 (2.2)InvocationHandler接口中第二个参数,method,他传入的实际上就是需要调用的实例类的真实方法,我们通过简单的输出就可以得到。

我们通过输出SecondTestClassService类class的method方法,可以得到我们有两个方法,对比newProxyInstance的Interfaces接口集合不同的是,我们传入的是相应的方法名而不是接口的全限定名。

我们调用HelloTest方法,InvocationHandler的method的值也为相应的HelloTest,进行调用

method.invoke(target,args),方法与InvocationHandler的invoke()不同,不要记错,method.invoke()实际将方法的反射获取委托给MethodAccessor.invoke(target,args)方法,实现调用。(MethodAccessor我并没有继续读,有兴趣的可以继续阅读)。

 (2.3)InvocationHandler接口中第三个参数,就是真实对象需要传入的参数。

以上内容就可以对JDK进行动态代理有一个基础的了解,但是使用过程中我也发现了,如果一个实现类没有相应的接口,会出现错误,具有一定的局限性

 (2.3)cglib动态代理

上述JDK代理出现的问题,cglib代理可以解决,他不要求实现类必须又相应的接口。简单实现如下:

//ProxyApplication.java
package com.lzm.proxy;

import com.lzm.controller.*;
import com.lzm.service.MainTestClassService;
import com.lzm.service.SecondTestClassService;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;

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

public class ProxyApplication {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ThirdTestClassServiceImpl.class);
        enhancer.setCallback(new MainMethodInterceptor());
        ThirdTestClassServiceImpl userService = (ThirdTestClassServiceImpl)enhancer.create();
        userService.HelloTest();
    }

}

//MainMethodInterceptor.java
package com.lzm.controller;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MainMethodInterceptor implements MethodInterceptor{

        public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("我是总代理,我给大家介绍");
            Object object = proxy.invokeSuper(target, args);
            System.out.println("代理结束");
            return object;

    }
}

//ThirdTestClassServiceImpl.java
package com.lzm.controller;

import com.lzm.service.MainTestClassService;
import com.lzm.service.SecondTestClassService;

public class ThirdTestClassServiceImpl  implements MainTestClassService, SecondTestClassService {
    
    @Override
    public void MainHelloTest() {
        System.out.println("你好,我是小明");
    }

    @Override
    public void MainHelloTest2() {
        System.out.println("你好,我是小明2号");
    }

    @Override
    public void HelloTest() {
        System.out.println("你好,我是小明3号");
    }

    @Override
    public void HelloTest2() {
        System.out.println("你好,我是小明3号");
    }
}


与JDK动态代理相比,不需要接口,仍然可以实现相应的动态代理,通过在运行时,动态修改字节码达到修改类的目的。这一部分内容,我不做详细赘述,有兴趣深究的可以进一步探究。

关于反射,代理与动态代理的实现与解析,就暂时到这里,接下来是撸一下java的反射。

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
代理模式是一种常见的设计模式,它通过创建一个代理对象来控制对实际对象的访问,从而实现对实际对象的间接访问。Java动态代理机制就是一种基于代理模式实现方式,可以在运行时动态地生成代理类。 Java动态代理机制是通过反射实现的,它主要涉及到两个类:`Proxy`和`InvocationHandler`。`Proxy`类是用来创建代理对象的类,`InvocationHandler`则是代理对象的调用处理器,用来处理代理对象的方法调用。 `InvocationHandler`接口有一个`invoke()`方法,当代理对象的方法被调用时,`invoke()`方法会被自动调用,并将方法名、参数等信息传递给该方法。在`invoke()`方法,我们可以对代理对象的方法进行增强处理,也可以选择调用实际对象的方法。 下面是一个简单的示例,演示了如何使用`InvocationHandler`实现动态代理: ```java public class DynamicProxyDemo { public static void main(String[] args) { // 创建实际对象 RealObject realObject = new RealObject(); // 创建代理对象 MyInvocationHandler handler = new MyInvocationHandler(realObject); Interface proxy = (Interface) Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[]{Interface.class}, handler); // 调用代理对象的方法 proxy.doSomething(); } } // 实际对象 class RealObject implements Interface { public void doSomething() { System.out.println("Do something..."); } } // 调用处理器 class MyInvocationHandler implements InvocationHandler { private Object realObject; public MyInvocationHandler(Object realObject) { this.realObject = realObject; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before invoke..."); Object result = method.invoke(realObject, args); System.out.println("After invoke..."); return result; } } // 接口 interface Interface { void doSomething(); } ``` 在这个示例,我们首先创建了一个`RealObject`实际对象,然后创建了一个`MyInvocationHandler`调用处理器,并将实际对象传递给它。接着,我们使用`Proxy.newProxyInstance()`方法创建了一个代理对象,并将调用处理器绑定到代理对象上。最后,我们调用代理对象的`doSomething()`方法,实际上是调用了`MyInvocationHandler`的`invoke()`方法,在该方法对实际对象的方法进行了增强处理。 在实际开发,我们常常会使用Java动态代理机制来实现AOP(面向切面编程)等功能,通过在代理对象的调用处理器添加相应的增强代码来实现对目标对象的增强处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值