【八】Java设计模式GOF23之动态代理(JDK和CGLIB)

本文介绍JDK和CGLIB两种动态代理技术的实现原理与应用场景,包括基于接口的JDK代理和基于继承的CGLIB代理的具体实现步骤及优缺点对比。

一、使用JDK原生动态代理

实现有2种,1.基于Java反射机制 2.基于native方法

Java动态代理是基于接口的,如果对象没有实现接口则选择用CGLIB方式实现动态代理。

实现步骤:

1.首先实现一个InvocationHandler,方法调用会被转发到该类的invoke()方法。

2.然后在需要使用真实对象的时候,通过JDK动态代理获取代理对象。

目录

接口:SayHello.java

package com.sid.proxy.jdk;

public interface SayHello {
    String sayHello();
}

真实的处理:SayHelloImp.java

package com.sid.proxy.jdk;

/**
 * 真实的处理
 * */
public class SayHelloImp implements SayHello {
    public String sayHello() {
        System.out.println("hello word");
        return "done";
    }
}

代理类:HelloHandler.java

package com.sid.proxy.jdk;

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

/**
 * 这是代理类,代理了SayHello接口的所有实现类
 * */
//1.首先实现一个InvocationHandler接口,方法调用会被转发到该类的invoke()方法。
public class HelloHandler implements InvocationHandler {

    SayHello sayHello;

    public HelloHandler(SayHello sayHello) {
        super();
        this.sayHello = sayHello;
    }

    /**
     * 所有的流程控制都在这里面处理
     * 调用真实的SayHello接口的实现类的所有方法,都会先进这个invoke方法,
     * 由method.invoke去真正的调用SayHello的方法
     *
     * 入参proxy是代理类,mehtod表示调用真实类的哪个方法,args表示调用真实类的方法的入参
     * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置方法");
        // obj是调用真实类方法的返回值
        Object obj = method.invoke(sayHello, args);
        System.out.println(obj);
        System.out.println("后续方法");
        return obj;
    }
}

Main方法调用

package com.sid.proxy;

import com.sid.proxy.jdk.HelloHandler;
import com.sid.proxy.jdk.SayHello;
import com.sid.proxy.jdk.SayHelloImp;

import java.lang.reflect.Proxy;

public class MainClass {
    public static void main(String[] args) {
        //2.然后在需要使用SayHello的时候,通过JDK动态代理获取SayHello的代理对象。
        //newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法
        SayHello proxy = (SayHello) Proxy.newProxyInstance(
                ClassLoader.getSystemClassLoader(),//1. 类加载器
                new Class[]{SayHello.class},//2. 代理需要实现的接口,可以有多个
                new HelloHandler(new SayHelloImp())  // 3. 方法调用的实际处理者,代理对象的方法调用都会转发到这里
        );
        proxy.sayHello();

    }
}

运行结果

二、使用CGLIB动态代理

基于ASM的字节码生成库,它允许在运行时对字节码进行修改和动态生成。

CGLIB动态代理的原理就是用Enhancer生成一个被代理类的子类,并且设置好callback到proxy, 当被代理类的每个方法被调用就会转为调用实现了MethodInterceptor接口的proxy代理类的intercept() 函数。

优点:1.CGLIB通过继承方式实现代理。2.效率比JDK的代理要高。

缺点:CGLIB只能代理所有非final的类、方法。

实现步骤

1.首先实现一个MethodInterceptor接口,方法调用会被转发到该类的intercept()方法。

2.创建Enhancer对象,给enhancer设置父类(就是被代理的目标类),给enhancer设置callback(callback就是第一步实现的MethodInterceptor,就是拦截到目标类的方法后要做的代理逻辑),enhancer.create创建代理类。

设置callback的时候可以设置一组callback,即是有多个不同的MethodInterceptor实现,然后设置回调过滤器CallbackFilter来指定不同的真实方法使用不同的代理逻辑

CGLIB包对callback接口有一些简单实现:

1.FixedValue固定值,MethodInterceptor需要实现FixedValue接口。这会忽略被代理目标类调用的方法的返回值,使用固定值来替换。

2.NoOp.INSTANCE,直接调用真实被代理类的方法,在代理中对该方法不做任何逻辑处理。

3.LazyLoader,继承了Callback接口,有一个loadObject的方法,LazyLoader只有在第一次调用时会执行loadObject获取对象。

4.Dispatcher,继承了Callback接口,有一个loadObject的方法,每次调用时都触发loadObject方法。

目录

 

需要引包

pom.xml

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

真实类SayHelloImp.java

public class SayHelloImp {

    public String sayHello1() {
        System.out.println("hello word 1");
        return "done";
    }

    public String sayHello2() {
        System.out.println("你好! 2");
        return "完成";
    }

    public void sayHello3() {
        System.out.println("我会被代理,但是callbackFilter里面设置的处理sayHello3方法是不做任何代理逻辑,直接调用真实方法. 3");
    }

    public final void sayHello4() {
        System.out.println("我是final方法,用cglib方式代理的时候,我不会被代理. 4");
    }
}

代理处理逻辑 SayHelloMethodInterceptor1.java 和SayHelloMethodInterceptor2.java

public class SayHelloMethodInterceptor1 implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("SayHelloMethodInterceptor1 do. param:"+ Arrays.toString(args) );
        System.out.println("before...");
        //通过调用MethodProxy.invokeSuper()方法,将调用转发给原始对象
        Object obj = methodProxy.invokeSuper(o, args);
        System.out.println(obj);
        System.out.println("after...");
        return obj;
    }
}


public class SayHelloMethodInterceptor2 implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("SayHelloMethodInterceptor do.param:"+ Arrays.toString(args) );
        System.out.println("前置方法");
        //通过调用MethodProxy.invokeSuper()方法,将调用转发给原始对象
        Object obj = methodProxy.invokeSuper(o, args);
        System.out.println(obj);
        System.out.println("后续方法");
        return obj;
    }
}

设置不同方法用不同代理逻辑CallbackFilterImpl.java

public class CallbackFilterImpl implements CallbackFilter {
    // 这里设置真实对象不同的方法被调用,使用不同的MethodInterceptor代理逻辑来吹了
    @Override
    public int accept(Method method) {
        String methodName = method.getName();

        if ("sayHello1".equals(methodName)) {

            return 0; // sayHello1()方法使用callbacks[0]对象拦截。

        } else if ("sayHello2".equals(methodName)) {

            return 1; // sayHello2()方法使用callbacks[1]对象拦截。

        }else if ("sayHello3".equals(methodName)) {

            return 2; // sayHello3()方法使用callbacks[2]对象拦截。

        }

        return 0;
    }
}

Main方法调用 

public class MainClassCglib {
    public static void main(String[] args) {
        // 2. 然后在需要使用真实对象的时候,通过CGLIB动态代理获取代理对象。
        //通过CGLIB的Enhancer来指定要代理的目标对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(SayHelloImp.class);
        //指定实际处理代理逻辑的对象 这样只设置了一个MethodInterceptor,真实类所有的方法都被同一个MethodInterceptor处理
        //enhancer.setCallback(new SayHelloMethodInterceptor1());

        // 回调实例数组
        Callback[] callbacks = new Callback[] { new SayHelloMethodInterceptor1(), new SayHelloMethodInterceptor1(), NoOp.INSTANCE };
        enhancer.setCallbacks(callbacks);
        enhancer.setCallbackFilter(new CallbackFilterImpl());

        //通过调用create()方法得到代理对象
        //对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法
        SayHelloImp star = (SayHelloImp)enhancer.create();
        star.sayHello1();
        star.sayHello2();
        star.sayHello3();
        star.sayHello4();
    }
}

结果

 

三、JDK动态代理和Gglib动态代理的区别

1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。 

FastClass机制的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。 这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值