前言
动态代理模式主要有两种
JDK动态代理 和CGlib动态代理
本文分别手写动态代理
JDK的动态代理
思路
1.需要一个接口 定义规则
2.定义实现类实现接口 实现具体功能
3.创建代理类,实现InvocationHandler接口,
并且定义前置后置各种通知点
(我这里用了aop的概念 相信大家都是有spring基础的 所以这点我就不多解释了)
4.在客户端中调用代理方法强转后执行内容即可
上代码!
/**
* 定义运营商接口 有一个打电话的方法
*/
public interface Operator {
void call();
}
/**
* 电信 是一家运营商公司 同时旗下会有许许多多的小代理
*/
public class DianXin implements Operator{
@Override
public void call() {
System.out.println("电信打电话");
}
}
public class BeiJingDaiLi implements InvocationHandler {
// 被代理的对象 什么都可以 这里特指电信
private Object object;
public Object getInstance(Object object){ //创建代理类时调用的方法
if(!(object instanceof DianXin)){
throw new RuntimeException("只有电信能代理");
}
this.object = object;
Class<?> aClass = object.getClass();
return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
}
/**
*
* @param proxy 原被代理类 是实现类
* @param method 接口的方法
* @param args 接口的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("打电话之前判断一下是否是北京。");
//我们真正写的方法 用反射执行 可以拿到返回值 springAOP 不多解释了 上下就是前置方法和后置方法
Object invoke = method.invoke(this.object, args);
System.out.println("打电话之后收费");
return invoke; //方法返回值返回
}
}
/*
* 执行方法
*/
public class App {
public static void main(String[] args) {
Operator instance = (Operator)new BeiJingDaiLi().getInstance(new DianXin());
instance.call();
}
}
输出---->
打电话之前判断一下是否是北京。
电信打电话
打电话之后收费
成功代理 说一下原理和注意事项
原理
- JDK动态代理是基于反射
- 通过反射机制 寻找被代理的类实现的接口
- 通过接口的内容将所有方法全部重写。 生成全新的一个类 (就是我们的$Proxy类)
注意事项
- 被代理类一定要实现某个接口
- 强转的时候一定要转成接口 不能强转成实现类
CGlib的动态代理
思路
实现MethodInterceptor接口
重写intercept方法 代码上实现方式类似于JDK
public class DianXin {
//注意此处没有实现接口
public void call(){
System.out.println("电信打电话");
}
}
public class BeiJingDianXin implements MethodInterceptor {
public Object getProxy(Class<?> clazz){
Enhancer enhancer = new Enhancer();
//注入父类的class类 因为cglib是通过继承的方式 所以不需要实现接口
enhancer.setSuperclass(clazz);
//设置回调函数 运行哪个对象的方法
enhancer.setCallback(this);
return enhancer.create();
}
/**
* @param o 当前对象"this"
* @param method 当前方法
* @param objects 方法参数
* @param methodProxy 被代理的方法 我们执行就是执行这个方法
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("判断是否是北京");
//这里如果调用invoke 就是调用当前方法 然后当前方法会继续调用方法 形成递归 出现stackOut
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("收费奥");
return invoke;
}
}
public class App {
public static void main(String[] args) {
DianXin proxy = (DianXin)new BeiJingDianXin().getProxy(DianXin.class);
proxy.call();
}
}
---->执行
判断是否是北京
电信打电话
收费奥
原理
cglib是通过继承来实现动态代理 通过继承 重新生成被代理的类
通过FastClass机制 在被调用代理类的方法时会产生两个FastClass
注意事项
- CGlib基于继承 所以不能代理final修饰的方法
- CGlib通过asm框架写class文件 所以必须依赖asm包 这点很关键
说些区别
JDK | CGlib |
---|---|
基于反射 | 基于继承 |
必须实现接口 | 不需要 |
所有方法都可以代理 | 不能代理final修饰的方法 |
直接写Class文件 效率高 | 用Asm框架写 更复杂 生成效率低 |
反射调用 执行效率低 | FastClass机制调用 执行效率高 |
最后说说spring的AOP
Spring的代理模式 ProxyFactoryBean
他会检测被代理的类是否实现了接口
如果实现了就会使用JDK的 否则就是CGlib
不过可以通过配置强制使用CGlib
个人感觉 spring还是更喜欢JDK的方式可能更稳定吧