简述动态代理
- 想要理解动态代理就得先知道代理模式和静态代理
- 代理模式顾名思义和我们生活中找代理人差不多, 处理的还是我们的是, 但是让他们帮我们去直接面对去处理问题, 而静态代理是一个代理者只能服务一个对象, 而动态代理就是这个代理者是万能的, 可以代理所有对象
JDK的动态代理
- 也叫做基于接口的代理模式
- 是JDK官方提供的一种依据类对象和反射得来的代理对象, 所以这种实现方式生成代理对象快, 效率高, 但是要求也高, 被代理被必须是实现接口, 生成代理对象也是基于接口的, 所以JDK实现动态代理是基于接口的, 不是很灵活, 第二是调用过程有性能问题,因为是通过反射来实现调用的,所以比正常的直接调用来得慢,并且通过生成类文件也会多消耗部分方法区空间,可能引起Full GC。
- 常见的方法一个是:Proxy类中的newProxyInstance方法得到代理对象
- 还有一个是: InvocationHandler接口:用于提供增强的代码熔断invoke方法, 如果我们想要调用被代理类中的方法时, 就会进入invoke方法
实现
- 接口
public interface UserService {
public String getName(int id);
public Integer getAge(int id);
}
- 被代理类
public class UserServiceImpl implements UserService {
public String getName(int id) {
System.out.println("------getName------");
return "cat";
}
public Integer getAge(int id) {
System.out.println("------getAge------");
return 10;
}
}
- 动态代理
public class JDKProxyHandler implements InvocationHandler {
private Object target;
/**
* 绑定委托对象并返回一个代理类
*
* @param target 传入被代理对象
* @return 返回代理对象
*/
public Object bind(Object target) {
this.target = target;
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("getName".equals(method.getName())) {
System.out.println("------before " + method.getName() + "------");
Object result = method.invoke(target, args);
System.out.println("------after " + method.getName() + "------");
return result;
} else {
return method.invoke(target, args);
}
}
}
- 测试
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
JDKProxyHandler proxy = new JDKProxyHandler();
UserService userServiceProxy = (UserService) proxy.bind(new UserServiceImpl());
System.out.println(userServiceProxy.getName(1));
System.out.println(userServiceProxy.getAge(1));
}
}
CGLIB的动态代理
- JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理, 也就是不能对一个终端类实现CGLIB动态代理
- CGLIB的实现来源是第三方, 他在生成代理对象上是使用继承来实现子类的方式实现代理, 所以在生成代理对象的过程中是慢于JDK的, 但是在调用方法过程中是直接调用, 比JDK的好, 如果对其进行优化的话就是将这个代理对象进行一个缓存, 这样就避免了代理对象的频繁创建和销毁的消耗, 但是这样还是消耗了一定的内存
- 核心方法:
- Enhancer - 生成代理对象的增强器, 所有核心参数都是使用它来, 包括设置被代理对象和得到代理对选哪个
- MethodInterceptor - 方法拦截器, 顾名思义就是使用者在使用目标的方法时就会被方法拦截器拦截到, 进入到代理对象中的方法中 最后再intercept方法中执行增强后的方法
public class CGLIBProxy implements MethodInterceptor {
private Object target;
/**
* 创建代理对象
*
* @param target 被代理对象
* @return 代理对象
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
System.out.println(method.getName());
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("++++++after " + methodProxy.getSuperName() + "++++++");
return result;
}
}
-测试
/**
* 测试CGLIB
*/
public class Client {
public static void main(String[] args) {
CGLIBProxy cglibProxy = new CGLIBProxy();
UserService userService = (UserService) cglibProxy.getInstance(new UserServiceImpl());
userService.getName(1);
userService.getAge(1);
}
}
Spring中的动态代理
- Spring中, JDK和CGLIB都是支持的, 只是他会进行一个判断, 如果被代理对象实现了接口, 就会使用JDK的, 如果没有实现接口就会使用CGLIB的动态代理
- Spring和Hibernate中的cglib是一个基于ASM的更高层次的自动代码生成工具
- ASM 是一个 Java 字节码操控框架。可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为(也就是生成的代码可以覆盖原来的类也可以是原始类的子类)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
- 不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解。~那些大神太厉害了