如何在建造者模式方法链末尾通过切面注入代码?
如何通过切面在建造者方法链末尾自动注入代码(执行build方法)
可以实现这个需求,核心思路是通过切面拦截建造者的所有withXXX方法,判断当前调用是否为方法链的最后一步,随后自动触发build()或其他自定义逻辑。下面提供两种落地方案:
方案一:基于Spring AOP的实现(Spring环境)
1. 切面代码实现
利用Spring AOP的环绕通知拦截建造者的withXXX方法,通过调用栈判断是否为方法链的最终调用:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class BuilderAutoBuildAspect { // 拦截Builder接口及其实现类的所有with开头方法 @Around("execution(com.yourpackage.Builder+.with*(..))") public Object interceptWithMethods(ProceedingJoinPoint joinPoint) throws Throwable { // 执行原方法,获取返回的Builder实例 Builder result = (Builder) joinPoint.proceed(); // 检查调用栈,判断是否存在后续的withXXX方法调用 boolean isLastCall = true; StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); for (StackTraceElement element : stackTrace) { if (element.getMethodName().startsWith("with") && element.getClassName().equals(Builder.class.getName())) { isLastCall = false; break; } } // 是最后一步则自动执行build if (isLastCall) { result.build(); } return result; } }
2. 注意事项
- 建造者对象需由Spring容器管理,或通过Spring代理生成
- 若方法链被拆分为多段(如中间赋值给变量),调用栈判断会失效,此时可改用线程本地变量记录调用状态,标记当前是否处于方法链连续调用中
方案二:基于动态代理的通用实现(非Spring环境)
不依赖Spring时,直接用JDK动态代理包裹建造者对象,在代理逻辑中自动触发build():
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class AutoBuildBuilderProxy implements InvocationHandler { private final Builder target; private AutoBuildBuilderProxy(Builder target) { this.target = target; } public static Builder createProxy(Builder target) { return (Builder) Proxy.newProxyInstance( target.getClass().getClassLoader(), new Class[]{Builder.class}, new AutoBuildBuilderProxy(target) ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().startsWith("with")) { // 执行原方法 method.invoke(target, args); // 判断是否为方法链最后一步 boolean isLastCall = true; StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); for (StackTraceElement element : stackTrace) { if (element.getMethodName().startsWith("with") && element.getClassName().equals(Builder.class.getName())) { isLastCall = false; break; } } if (isLastCall) { target.build(); } return proxy; // 返回代理对象维持方法链 } else if ("build".equals(method.getName())) { return method.invoke(target, args); } return method.invoke(target, args); } }
使用方式:
// 生成带自动build逻辑的代理建造者 Builder builder = AutoBuildBuilderProxy.createProxy(createBuilder()); // 调用方法链,无需显式调用build builder.withArg2(arg2).withArg10(arg10);
核心问题说明
- 方法链末尾判断:上述方案采用调用栈分析,是最轻量化的实现;若需更高精度,可结合字节码工具(如ASM)或在建造者内部添加状态标记,但复杂度会提升
- 线程安全:若建造者为多线程共享对象,需确保状态标记的线程隔离(如用
ThreadLocal) - 类代理兼容:JDK动态代理仅支持接口,若使用具体建造者类,需改用CGLIB代理实现
内容的提问来源于stack exchange,提问作者crashtua
相关产品推荐
相关产品推荐

