- JDK动态代理
- CGlib动态代理
- 区别
代码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"/>)。