(十七)关于动态代理,你能说出动态代理的几种方式?

207 阅读3分钟

微信搜索《Java鱼仔》,每天一个知识点不错过

(一)每天一个知识点

关于动态代理,你能说出动态代理的几种方式?

(二)详解

动态代理是指代理类不是写在代码中的,而是在代码运行过程中产生的,Java提供了两种方式来实现动态代理,分别是基于Jdk的动态代理和基于Cglib的动态代理。

2.1 基于Jdk的动态代理

首先我们来搭建一套实现动态代理的实现,以租房为例,我新建一个租房的接口:

public interface Room {
    void rent();
}

然后下一个实际的租房实现类

public class RealRoom implements Room {
    private String roomname;
    public RealRoom(String roomname) {
        this.roomname = roomname;
    }
    public void rent() {
        System.out.println("租了"+roomname);
    }
}

基于Jdk的动态代理核心在于InvocationHandler 接口,首先我们新建一个类ProxyHandler来实现这个InvocationHandler 接口,并实现里面的invoke方法

public class ProxyHandler implements InvocationHandler {
    Object object;
    public ProxyHandler(Object object) {
        this.object = object;
    }
    //proxy 代理对象
    //method 要实现的方法
    //args 方法的参数    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行之前:"+method.getName());
        Object invoke = method.invoke(object, args);
        System.out.println("代理执行之后:"+method.getName());
        return invoke;
    }
}

InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类invoke,由它实现处理内容。

接下来写一个方法来实现动态代理

public static void main(String[] args) {
    Room room=new RealRoom("碧桂园");
    //obj.getClass().getClassLoader()类加载器
    //obj.getClass().getInterfaces() 目标类实现的接口
    //InvocationHandler对象
    InvocationHandler invocationHandler=new ProxyHandler(room);
    Room proxyRoom = (Room) Proxy.newProxyInstance(room.getClass().getClassLoader(), room.getClass().getInterfaces(), invocationHandler);
    proxyRoom.rent();
}

这段代码的另外一个核心是Proxy.newProxyInstance,该方法需要三个参数,目的是运行期间生成代理类,每个参数的功能已经写在了注释中。这段代码的意思就是Proxy 动态产生的代理对象会调用 InvocationHandler 实现类invoke。

最终我们不需要RealRoom去调用rent方法,通过代理类就可以实现这个rent方法。

最后结果如下:

代理执行之前:rent
租了碧桂园
代理执行之后:rent

运行时就会在控制台上打印出来,这也是Spring AOP的核心

JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。

JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()

2.2 基于Cglib的动态代理

基于Jdk的动态代理局限性在于代理的类必须要实现接口,而基于CGlib的动态代理则没有这个限制:

搭建CGlib环境我们首先要引入一个CGlib的jar包:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

此时我们不再需要接口,直接新建一个CGRoom类:

public class CGRoom {
    public void rent(String roomName){
        System.out.println("租了"+roomName);
    }
}

CGlib实现动态代理的核心在于MethodInterceptor接口:

public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理执行之前:"+method.getName());
        Object object=methodProxy.invokeSuper(o,objects);
        System.out.println("代理执行之后:"+method.getName());
        return object;
    }

这个接口只有一个intercept()方法,拦截被代理对象,这个方法有4个参数:

1)表示增强的对象,即实现这个接口类的一个对象;

2)表示要被拦截的方法;

3)表示要被拦截方法的参数;

4)表示要触发父类的方法对象;

最后生成代理类对象并输出执行结果

public static void main(String[] args) {
    //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
    Enhancer enhancer=new Enhancer();
    //设置目标类的字节码文件
    enhancer.setSuperclass(CGRoom.class);
    //设置回调函数
    enhancer.setCallback(new MyMethodInterceptor());
    //创建代理对象
    CGRoom proxy= (CGRoom) enhancer.create();
   proxy.rent("碧桂园");
}

运行结果:

代理执行之前:rent
租了碧桂园
代理执行之后:rent

(三)总结

两种代理方式各有优劣,在使用方面,JDK动态代理只能基于接口进行实现,而CGLIb对代理的目标对象无限制,无需实现接口。

在依赖方面,Java原生支持JDK动态代理,而CGlib的实现还需要引入相关依赖包。