介绍
了解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实现代理的步骤
- 代理和被代理对象类继承同一个接口
- 代理的方法调用对象的同名方法
依旧是面向接口编程
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()

AOP
Aspect-Oriented Programming、面向切面编程、编程范式
将一些对象需要的类似功能提取出来组织(通知),通过动态代理实现,和代理模式概念一致。区别在于将通用增强逻辑以声明式方式集中管理,实现对业务代码的非侵入式横切增强。故是代理模式的上层
- 连接点(Join Point):需要代理的单个方法
- 切入点(PointCut):需要代理的多个方法
- 通知(Advice):非核心的逻辑(日志、权限检测、事物…), 按与核心业务代码的相对位置分为:前置通知、后置通知、环绕通知…
- 切面(Aspect):通知 + 切入点 绑定执行
总结
回调:补齐 外部规定模板方法的 缺失逻辑/行为
静态代理:手写的代理类和实现类继承同一组接口,在代理类同名方法 调用 实现类同名方法 + 通用增强逻辑
动态代理:运行时生成代理类,代理类同名方法由回调、反射实现(自己写)
AOP:动态代理 + 以声明式方式将通用增强逻辑集中管理,实现对业务代码的非侵入式横切增强 (有些实现类用增强逻辑A,有些用增强逻辑B,程序员进行调配、选择、简化开发)
对 面向接口 有了更深理解,重要程度不亚于C指针;
剖析时 加强了对匿名内部类、lambda进一步理解(语法糖);
引出 编程范式、设计思想、接口的default方法、钩子和回调区别、还需继续学习
部分信息可能已经过时