Java中非Spring类特定方法前置通用代码执行方案咨询
解决方案建议
针对你遇到的问题,以下是几种可行的实现思路,各有优劣,可根据你的维护成本和侵入性需求选择:
方案1:工具类+显式调用(低侵入,高可维护)
这种方式把通用逻辑封装到工具类中,在目标方法里显式调用,完全避免“远距离操作”的问题,逻辑直观可见。
实现步骤:
- 编写通用代码执行工具类,包含条件判断和通用逻辑:
import java.time.LocalDate; import java.time.DayOfWeek; import java.util.function.Supplier; import java.util.function.Runnable; public class CommonCodeHandler { // 通用代码逻辑 private static void runCommonLogic() { // 你的通用业务代码 System.out.println("执行通用代码"); } // 封装带条件的执行逻辑:先执行目标方法,再判断条件触发通用代码 public static void executeTargetWithCondition(Runnable targetAction, Supplier<Boolean> condition) { targetAction.run(); if (condition.get()) { runCommonLogic(); } } }
- 在CC1~CC5的目标方法中调用工具类:
public class CC1 extends C1 { @Override public void print(String a) { // 原有逻辑 } public void scan() { CommonCodeHandler.executeTargetWithCondition(() -> { // 原来的scan()方法逻辑 System.out.println("CC1执行scan方法"); }, () -> { // 条件判断:是否为周末 DayOfWeek day = LocalDate.now().getDayOfWeek(); return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY; }); } }
优缺点:
- 优点:逻辑完全透明,维护成本低,不需要额外依赖,适合对代码可维护性要求高的场景。
- 缺点:需要修改每个目标方法的代码,但仅需调用工具类,比直接复制粘贴通用代码更简洁,且通用逻辑集中便于修改。
方案2:CGLIB动态代理(无侵入,可控性较好)
如果不想修改原有类的代码,可以用CGLIB在运行期生成代理类,拦截目标方法调用,触发通用逻辑。因为你的类不是Spring Bean,Spring AOP无法直接使用,CGLIB是更合适的动态代理方案。
实现步骤:
- 引入CGLIB依赖(Maven):
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
- 编写方法拦截器,实现方法调用的拦截和条件判断:
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.time.LocalDate; import java.time.DayOfWeek; public class TargetMethodInterceptor implements MethodInterceptor { private static void runCommonLogic() { // 通用代码逻辑 System.out.println("执行通用代码"); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 先执行原方法 Object result = proxy.invokeSuper(obj, args); // 根据方法名和条件判断是否触发通用代码 if ("scan".equals(method.getName()) && isWeekend()) { runCommonLogic(); } // 可添加其他方法的判断逻辑,比如CC2的特定方法 if ("refresh".equals(method.getName()) && someOtherCondition()) { runCommonLogic(); } return result; } private boolean isWeekend() { DayOfWeek day = LocalDate.now().getDayOfWeek(); return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY; } private boolean someOtherCondition() { // 其他自定义条件 return true; } }
- 创建代理对象并使用:
import net.sf.cglib.proxy.Enhancer; public class Main { public static void main(String[] args) { // 创建CC1的代理对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(CC1.class); enhancer.setCallback(new TargetMethodInterceptor()); CC1 proxyCc1 = (CC1) enhancer.create(); // 调用方法时自动触发拦截逻辑 proxyCc1.scan(); } }
优缺点:
- 优点:不需要修改原有类的代码,通用逻辑集中管理,适合已上线代码的改造。
- 缺点:需要引入CGLIB依赖,代理对象的创建需要显式处理,方法名硬编码可能带来维护风险(可通过注解标记目标方法优化)。
方案3:AspectJ字节码织入(无侵入,批量处理)
如果需要批量处理多个类的多个方法,AspectJ是更高效的选择,它可以在编译期或类加载期把通用逻辑织入目标方法,不需要Spring环境支持。
实现步骤:
- 引入AspectJ依赖(Maven):
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.9.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.9.1</version> </dependency>
- 编写切面类,定义切点和通知逻辑:
import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import java.time.LocalDate; import java.time.DayOfWeek; @Aspect public class CommonCodeAspect { private static void runCommonLogic() { // 通用代码逻辑 System.out.println("执行通用代码"); } // 定义切点:匹配CC1的scan方法 @After("execution(void com.example.CC1.scan())") public void afterCC1Scan() { if (isWeekend()) { runCommonLogic(); } } // 可添加其他切点,比如CC2的特定方法 @After("execution(void com.example.CC2.refresh())") public void afterCC2Refresh() { if (someOtherCondition()) { runCommonLogic(); } } private boolean isWeekend() { DayOfWeek day = LocalDate.now().getDayOfWeek(); return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY; } private boolean someOtherCondition() { // 自定义条件 return true; } }
- 配置AspectJ织入(编译期织入需配置Maven插件,运行期织入需添加JVM参数
-javaagent:aspectjweaver-1.9.9.1.jar)。
优缺点:
- 优点:完全无侵入,批量处理效率高,切点表达式灵活。
- 缺点:有一定学习成本,逻辑分散在切面中,存在“远距离操作”的风险,调试和定位问题相对复杂。
关于AOP顾虑的说明
你担心的“Action At a Distance”反模式确实存在于AOP类方案中(AspectJ、动态代理),因为通用逻辑和目标方法分离,不熟悉代码的人可能难以关联两者。如果选择这类方案,建议:
- 把所有通用逻辑和条件判断集中在一个类中,避免分散。
- 给切面或拦截器添加详细注释,说明哪些方法会被拦截、触发条件是什么。
- 尽量使用精确的切点表达式(比如指定具体类和方法名),避免误拦截。
总结建议
- 如果优先考虑代码可维护性,**方案1(工具类+显式调用)**是最优选择,逻辑清晰无隐藏行为。
- 如果无法修改原有代码,**方案2(CGLIB代理)**比AspectJ更可控,适合小规模方法拦截。
- 如果需要批量处理大量方法,且能接受一定维护成本,**方案3(AspectJ)**更高效。
内容的提问来源于stack exchange,提问作者ANKIT SRIVASTAVA
相关产品推荐
相关产品推荐

