1509 字
4 分钟
从回调与代理认识AOP

介绍#

了解AOP(目前只了解Java AOP),你需要了解:回调函数、反射、代理模式(jdk动态代理,cglib不做考虑)之前写的不好,所以重写了一下(全由Java实现),作为个人的复习^3^

回调方法#

Callback控制反转

方法内部执行步骤已经规定,缺省的逻辑 由 回调函数 根据实际需求补全,将核心代码交给使用者编写

[!details] 详细解释 A想使用B设计的代码,其中的大部分功能符合A需求,但是A仍不满足,如果代码是写死的话,A还要重构一下并自己封装,冗余而不方便。假设B的代码将A想重写的部分抽离出去,其余部分做一个框架,A就只需要写核心的代码即可,非常方便。所以被抽离的部分就是回调函数!想要实现这个思想,各个语言各有不同的方法(noob的猜测);Java要实现回调函数,就要利用其重要的面向接口(Interface)思想

Java的实现回调步骤:实现类继承回调接口,写具体代码。Caller调用继承回调接口实例的方法。

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(){
@Override
public void callBackMethod(){
System.out.println("Hello I'm Oddpalmer");
}
});
}
/*
写死的代码
回调方法: Hello I'm Oddpalmer
写死的代码
*/

代理模式#

Proxy Pattern设计模式

以明星和经纪人为例:对接、签约…,这些工作经纪人做就行,明星只需要唱歌、演戏…即可,这里经纪人就作了代理的工作,并且经纪人可以去为多个明星服务。

代理就是将非核心代码剥离出去,只关注对象本身的核心。非核心代码在AOP中称为通知(Advice)

静态代理#

Java实现代理的步骤

  • 代理和被代理对象类继承同一个接口
  • 代理的方法调用对象的同名方法 image-20260129232455651 依旧是面向接口编程

1.接口

public interface UserService {
void addUser(String name);
}

2.实现类

public class UserServiceImpl implements UserService{
@Override
public void addUser(String name) {
System.out.println("添加用户 " + name + " 成功");
}
}

3.实现类代理

public class UserServiceProxy implements UserService{
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public 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也需要权限检查或者日志上传,就需要多个静态代理才可以实现,冗余而不优雅,这就引出了我们的动态代理。

动态代理#

coder手动创建的静态代理(麻烦) -> JVM用反射动态生成的静态代理(方便);不了解反射的可以看博主的这篇文档 待重构todo

因为静态代理要获取实现类接口,所以JVM也要知道你用的什么接口;因为是动态生成的,所以里面的步骤控制权要转让给编写代码的人,就要用回调接口的方法(invoke())。

以上是我对动态代理的核心理解,其本质就是由jvm创造的静态代理

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 h
public $Proxy0(InvocationHandler h) {
super(h);
}
@Override
public 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 // 传入回调实现类: 要求重写回调方法
)

InvotationHandler 动态代理的回调接口

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 成功
[日志记录]
*/

当你调用ProxyUtil后程序执行顺序:

  • jvm生成了动态代理类$ProxyXXX
  • main调用代理类UserServiceProxy的addUser(),会通过重写的回调方法调用被代理对象本身的addUser() image-20260127201103034

AOP#

Aspect-Oriented Programming面向切面编程编程范式

将一些对象需要的类似功能提取出来组织(通知),通过动态代理实现,和代理模式概念一致。区别在于将通用增强逻辑以声明式方式集中管理,实现对业务代码的非侵入式横切增强。故是代理模式的上层

  • 连接点(Join Point):需要代理的单个方法
  • 切入点(PointCut):需要代理的多个方法
  • 通知(Advice):非核心的逻辑(日志、权限检测、事物…), 按与核心业务代码的相对位置分为:前置通知、后置通知、环绕通知…
  • 切面(Aspect):通知 + 切入点 绑定执行

总结#

回调:补齐 外部规定模板方法的 缺失逻辑/行为

静态代理:手写的代理类和实现类继承同一组接口,在代理类同名方法 调用 实现类同名方法 + 通用增强逻辑

动态代理:运行时生成代理类,代理类同名方法由回调反射实现(自己写)

AOP:动态代理 + 以声明式方式将通用增强逻辑集中管理,实现对业务代码的非侵入式横切增强 (有些实现类用增强逻辑A,有些用增强逻辑B,程序员进行调配、选择、简化开发)


面向接口 有了更深理解,重要程度不亚于C指针; 剖析时 加强了对匿名内部类、lambda进一步理解(语法糖); 引出 编程范式、设计思想、接口的default方法、钩子和回调区别、还需继续学习


分享

如果这篇文章对你有帮助,欢迎分享给更多人!

从回调与代理认识AOP
https://oddpalmer.space/posts/aop/从回调与代理认识aop/
作者
Oddpalmer
发布于
2026-01-29
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时