介绍
之前的动态代理篇幅写的不好,所以重写了一下(Java实现),作为个人的复习3。同时这一章承上启下,承接反射篇章,引出AOP思想和Spring AOP
了解AOP的前置知识(本人目前只了解Java AOP)
- 回调函数
- 静态代理
- 反射
- jdk动态代理
回调方法 CallBack
将核心代码交给使用者编写
A想使用B设计的代码,其中的大部分功能符合A需求,但是A仍不满足,如果代码是写死的话,A还要重构一下并自己封装,冗余而不方便。
假设B的代码将A想重写的部分抽离出去,其余部分做一个框架,A就只需要写核心的代码即可,非常方便。所以被抽离的部分就是回调函数!
想要实现这个思想,各个语言各有不同的方法(noob的猜测);Java要实现回调函数,就要利用其重要的面向接口(Interface)思想
1.回调接口
public interface CallBack {public void callBackMethod();
}
2.调用类
public class Caller {public void call(CallBack callBack) {System.out.println("无关代码");callBack.callBackMethod();System.out.println("");}
}
3.重写回调方法,为了方便使用匿名内部类
public static void main(){Caller caller = new Caller();caller.call(new CallBack(){@Overridepublic void callBackMethod(){System.out.println("");}});
}
/*
无关代码
回调方法: Hello I'm Oddpalmer
无关代码
*/
只需要编写一个继承callback的类,并重写方法即可传入Caller实现回调,it's simple
静态代理 Static Proxy
代理就是将非核心代码剥离出去,只关注对象本身的核心
非核心代码在AOP中称为通知(Advice)
以明星和经纪人为例:对接、签约....,这些工作经纪人做就行,明星只需要唱歌、演戏....即可,这里经纪人就作了代理的工作,并且经纪人可以去为多个明星服务。
Java实现代理的步骤
- 代理和被代理对象类继承同一个接口
- 代理的方法调用对象的同名方法

依旧是面向接口编程
1.接口
public interface UserService {void addUser(String name);
}
2.实现类
public class UserServiceImpl implements UserService{@Overridepublic void addUser(String name) {System.out.println("添加用户 " + name + " 成功");}
}
3.实现类代理
public class UserServiceProxy implements UserService{private UserService target;public UserServiceProxy(UserService target) {this.target = target;}@Overridepublic void addUser(String name) {System.out.println("代理:[权限检查]");// 前置额外逻辑target.addUser(name); //UserService真实对象的核心业务逻辑System.out.println("代理:[日志上传]");// 后置额外逻辑}
}
4.Main
// 调用
public class ProxyTest {public static void main(String[] args) {UserService proxy = new UserServiceProxy(new UserServiceImpl());proxy.addUser("张三");}
}
/* 结果代理:[权限检查]添加用户 张三 成功代理:[日志上传]
*/
虽然静态代理能在一定程度上帮我们减少代码冗余,但是不难发现只有继承了UserService接口的类才可以被代理。如果我还有OrderService、ProductService也需要权限检查或者日志上传,就需要多个静态代理才可以实现,局促而不优雅,这就引出了我们的动态代理。
动态代理 Dynamic Proxy
我们已经知道静态代理与被代理对象的类会继承同一个接口。那么Java实现动态代理也要知道创造的代理类要继承什么接口,从而和被代理对象在接口上保持一致!所以就要依赖Java反射机制,以获取运行时类的接口信息和方法,然后由JVM在内存中动态创造多个静态代理类,实现代理!(也称作JDK动态代理,用接口实现。cglib本文不作讨论)
不了解反射的可以看博主的这篇文档 待重构todo
jvm生成的一个动态代理类大致长这样,发现没有,和静态代理一样的,区别只是动态生成的(里面涉及了一个回调,我们稍后再讲),DeepSeek给出的代码:
public final class $Proxy0 extends Proxy implements UserService {private static Method m_addUser;static {try {m_addUser = UserService.class.getMethod("addUser", String.class);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}// 父类 Proxy 已经持有 InvocationHandler hpublic $Proxy0(InvocationHandler h) {super(h);}@Overridepublic void addUser(String name) {try {// invoactionHandler.invoke(), 所有接口方法 → 回调调用者h.invoke(this, m_addUser, new Object[]{name});} catch (RuntimeException | Error e) {throw e;} catch (Throwable t) {throw new UndeclaredThrowableException(t);}}
}
如何生成一个动态代理类?Java给我们设计了java.lang.reflect.Proxy类,其中的newProxyInstance()的三个参数 就是关键...
public static Object newProxyInstance(ClassLoader loader, // 用target类加载器Class<?>[] interfaces, // 用target的接口:硬性规定只能代理接口InvocationHandler h // 传入回调实现类: 要求重写回调方法,用于承载“方法调用时的统一处理逻辑”。
)
参数1涉及的底层我还理解不了;参数2很明显是通过反射获取某个类的接口;参数3就是我们上文提到的回调接口,参数如下:
Interface InvotationHandler{public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
创造代理对象的步骤:
- 调用newProxyInstance()
- 将被代理对象的实例作为参数传入
- 重写invocationHandler回调接口的invoke()
实际开发中,动态代理可以再封装成一个工具类,不用每次代理都去newProxyInstance(),简化代码
public class ProxyUtil {@SuppressWarnings("unchecked")public static <T> T createProxy(T target) {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Override// InvokeHandler接口定义的回调方法,控制权在程序员手里public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("[权限检测]");// 前置 额外逻辑Object o = method.invoke(target, args);// 反射包下invoke 调用真实对象方法System.out.println("[日志记录]");// 后置 额外逻辑return o;}});}
}
当你调用了ProxyUtil后
UserService userServiceProxy = new ProxyUtil().createProxy(new UserServiceImple());userServiceProxy.addUser("oddpalmer");/*[权限检测]添加 oddpalmer 成功[日志记录]*/
程序执行顺序:
- jvm生成了动态代理类$ProxyXXX
- main调用代理类UserServiceProxy的addUser(),会通过重写的回调方法调用被代理对象本身的addUser()

引出Spring AOP
面向切面AOP的思想很简单,将一个对象的核心代码抽离出来,使得开发者更注重核心代码而非其他的冗余代码(通知)。动态代理就是实现这一思想的利器
Spring AOP就很好的帮我们省去了写ProxyUtil类的过程....
