一篇写的很好的解释动态代理原理的文章:
博客原地址:https://www.cnblogs.com/lifullmoon/p/14654836.html
代理
代理:在不改变原始代码的情况下修改对象的行为。代理可以以透明的方式为对象添加额外的功能。
言简意赅:方法增强
分类
静态代理
人为编写,编译时就存在
静态代理就是通过实现被代理对象所实现的接口,内部保存了被代理对象,在实现的方法中对处理逻辑进行增强,实际的方法执行调用了被代理对象的方法。
静态代理实现步骤:
-
定义一个接口及其实现类;
-
创建一个代理类同样实现这个接口
-
将目标对象【实现类】注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。
这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
代码示例:
接口
public interface Email {
void sendEmail();
}
实现类
public class EmailImpl implements Email{
@Override
public void sendEmail() {
System.out.println("send email");
}
}
代理类创建
public class EmailProxy implements Email {
private EmailProxy() {
}
private Email email;
public EmailProxy(Email email) {
this.email = email;
}
@Override
public void sendEmail() {
System.out.println("方法开始执行");
email.sendEmail();
System.out.println("方法执行结束");
}
}
测试
public class Test {
public static void main(String[] args) {
EmailProxy emailProxy = new EmailProxy(new EmailImpl());
emailProxy.sendEmail();
}
}
缺点:
1、接口一旦新增方法,目标对象和代理对象都要进行修改
2、每个代理类只能为一个接口服务,不能为多个接口服务【针对每个目标类都单独创建一个代理类】
假设还有一个Phone的接口,又要再写一个Phone的代理类,麻烦!
3、代理类必须实现接口
动态代理
jdk动态代理
运行时生成代理类,编译时不存在
解决了静态代理每个代理类只能代理一个目标的问题,但是目标对象必须还是要实现接口
代码示例:
代理类:Proxy.newProxyInstance()方法
参数:类加载器、目标对象实现的接口、处理接口方法调用的InvocationHandler
处理器
public class JDKProxyFactory {
private JDKProxyFactory(){}
public static Object getProxy(Object target){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyMethodInterceptor(target));
}
}
处理器
public class MyMethodInterceptor implements InvocationHandler {
private final Object target;
public MyMethodInterceptor(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法开始执行");
Object res = method.invoke(target, args);
System.out.println("方法执行结束");
return res;
}
}
测试
public class Test {
public static void main(String[] args) {
Email email = (Email) JDKProxyFactory.getProxy(new EmailImpl());
email.sendEmail();
System.out.println("=================================");
Phone phon = (Phone) JDKProxyFactory.getProxy(new PhoneImpl());
phon.call();
}
}
缺点:只能代理实现了接口的类
为什么 JDK 动态代理只能基于接口代理,不能基于类代理?
在JDK动态代理的底层代码中,对于入参中的
interfaces
如果存在非接口,那么会抛出异常;且从生成的代理对象中看到会继承Proxy
这个类,在 Java 中类只能是单继承关系,无法再继承一个代理类,所以只能基于接口代理。
CGLIB 动态代理
【天空一声巨响,cglib闪亮登场】
CGLIB 通过继承方式实现代理。
CGLIB 动态代理基于类代理(字节码提升),通过 ASM(Java 字节码的操作和分析框架)将被代理类的 class 文件加载进来,通过修改其字节码生成子类来处理。
很多知名的开源框架都使用到了CGLIB,例如 Spring 中的 AOP 模块中:
如果目标对象实现了接口或者是代理类的子类,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
CGLIB基本介绍:
Code Generation Library代码生成类库,可以动态的给代理类生成一个子类,然后使用继承方式重写被代理方法
CGLIB基本使用
-
创建增强器对象Enhancer
-
给增强器对象设置要代理的父类以及拦截器回调
-
创建方法拦截器类,实现MethodInterceptor接口
-
创建代理对象,并调用方法
JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。
代码示例:
类
public class Email1 {
public void sendEmail()
{
System.out.println("发送邮件");
}
}
创建代理类
public class CglibProxyFactory {
private CglibProxyFactory(){}
public static Object getProxy(Class<?> clazz)
{
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(clazz.getClassLoader());
enhancer.setSuperclass(clazz);
enhancer.setCallback(new MyCallback());
return enhancer.create();
}
}
拦截器回调Callback
因为 MethodInterceptor 继承了 Callback 回调接口,所以可以传入一个 MethodInterceptor 方法拦截器
注意,如果你想设置一个 Callback[] 数组去处理不同的方法,那么需要设置一个 CallbackFilter 筛选器,用于选择这个方法使用数组中的那个 Callback 去处理
public class MyCallback implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after");
return result;
}
}
测试
public class Test {
public static void main(String[] args) {
Email1 email = (Email1) CglibProxyFactory.getProxy(Email1.class);
email.sendEmail();
}
}
CGLIB 动态代理在 JVM 运行时会新生成一个代理类,这个代理对象继承了目标类,也就是创建了一个目标类的子类,从而字节码得到提升。
标签:JDK,Object,代理,接口,class,CGLIB,public From: https://www.cnblogs.com/kk-koala/p/18335996