JAVA动态代理

  1. JDK动态代理
  2. CGlib动态代理
  3. 区别

代码github地址:https://github.com/LynHB/ProjectA/tree/master/src/main/java/study/dynamic/proxy

1. JDK动态代理

1.1 代理对象代码

1.1.1 顶级接口类

package study.dynamic.proxy.entity;

/**
 * @Author LynHB
 * @Description : 代理接口
 * @Date 23:13 2020/7/8
 **/
public interface IPerson {
    /**
     * @Description : 吃菜
     * @Date 23:14 2020/7/8
     * @param dishName : 菜名
     * @return java.lang.String
     **/
    String eat(String dishName);

    /**
     * @Description : 饥渴
     * @Date 23:15 2020/7/8
     * @return java.lang.String
     **/
    String hungry();
}

1.1.2 实现类

package study.dynamic.proxy.entity;


import lombok.extern.slf4j.Slf4j;

/**
 * @Author LynHB
 * @Description :
 *      Baby 实现来自IPerson接口的类
 * @Date 23:17 2020/7/8
 **/
@Slf4j
public class Baby implements IPerson {
    @Override
    public String eat(String dishName) {
        log.info("mother feed me {}",dishName);
        return "eat end";
    }

    @Override
    public String hungry() {
        log.info("wa wa wa");
        return "wa wa wa";
    }

}

1.2 代理类

package study.dynamic.proxy.proxy;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @Author LynHB
 * @Description : 代理类
 * @Date 23:53 2020/7/8
 **/
@Slf4j
public class PersonProxy implements InvocationHandler {
    private Object target;

    public void setTarget(Object o){
        this.target = o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("invoke {} function",method.getName());
        return method.invoke(target,args);
    }

    /**
     * @Description : 生产代理类
     * @Date 0:01 2020/7/9
     * @return java.lang.Object
     **/
    public Object CreateProxyObj(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

1.3 主函数执行

package study.dynamic.proxy.main;

import study.dynamic.proxy.entity.Baby;
import study.dynamic.proxy.entity.IPerson;
import study.dynamic.proxy.proxy.PersonProxy;

public class PersonMain {
    public static void main(String[] args){
        // 创建原始对象
        IPerson baby = new Baby();

        // 设置代理类,将原始对象传入
        PersonProxy personProxy = new PersonProxy();
        personProxy.setTarget(baby);

        // 从代理类中获取对应的对象,进行方法调用
        IPerson person = (IPerson) personProxy.createProxyObj();
        person.hungry();
        person.eat("watermelon");

    }
}
-----------------------------------------------
2020-07-09 00:09:39 [main] INFO  PersonProxy:24 - invoke hungry function
2020-07-09 00:09:39 [main] INFO  Baby:22 - wa wa wa
2020-07-09 00:09:39 [main] INFO  PersonProxy:24 - invoke eat function
2020-07-09 00:09:39 [main] INFO  Baby:16 - mother feed me watermelon

2 CGLib动态代理

2.1 代理类

package study.dynamic.proxy.proxy;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

@Slf4j
public class CGLibProxy implements MethodInterceptor {
    private Object target;


    public Object createProxyObject(Object obj) {
        this.target = obj;
        Enhancer enhancer = new Enhancer();
        //这一步就是告诉cglib,生成的子类需要继承哪个类
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        // 返回代理对象
        //第一步、生成源代码
        //第二步、编译成class文件
        //第三步、加载到JVM中,并返回被代理对象
        return proxyObj;
    }
    @Override
    public Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {
        Object obj = null;
        if (method.getName().equals("hungry")){
            log.info("has some time no eat");
        }
        obj = method.invoke(target, args);
        //这个obj的引用是由CGLib给我们new出来的
        //cglib new出来以后的对象,是被代理对象的子类(继承了我们自己写的那个类)
        //OOP, 在new子类之前,实际上默认先调用了我们super()方法的,
        //new了子类的同时,必须先new出来父类,这就相当于是间接的持有了我们父类的引用
        //子类重写了父类的所有的方法
        //我们改变子类对象的某些属性,是可以间接的操作父类的属性的
        //proxy.invokeSuper(obj, args);
        return obj;
    }
}

2.2 主函数执行

package study.dynamic.proxy.main;

import study.dynamic.proxy.entity.Baby;
import study.dynamic.proxy.entity.IPerson;
import study.dynamic.proxy.proxy.CGLibProxy;
import study.dynamic.proxy.proxy.PersonProxy;

public class CGLibMain {
    public static void main(String[] args){
        CGLibProxy cgLibProxy = new CGLibProxy();
        IPerson baby =(IPerson) cgLibProxy.createProxyObject(new Baby());
        baby.hungry();
        baby.eat("watermelon");

    }
}
-------------------------------
2020-07-09 09:35:53 [main] INFO  CGLibProxy:33 - has some time no eat
2020-07-09 09:35:53 [main] INFO  Baby:22 - wa wa wa
2020-07-09 09:35:53 [main] INFO  Baby:16 - mother feed me watermelon

Process finished with exit code 0

3 区别

3.1 原理区别

  • JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
  • CGLib动态代理:利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

3.2 使用场景

  • 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
  • 如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
  • 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

3.3 字节码生成差异

  • JDK动态代理:只能对实现了接口的类生成代理,而不能针对类。
  • CGLIB动态代理:是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。

3.4 速度差异

  • 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
  • 在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。

3.5 Spring的选择

  • 当Bean实现接口时,Spring就会用JDK的动态代理。
  • 当Bean没有实现接口时,Spring使用CGlib是实现。
  • 可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。