Spring如何为final类创建代理?为何final类定时任务可正常运行
核心原理
你之前了解的「Spring不能代理final类」的结论只在特定场景下成立:当Spring需要通过生成目标类子类的方式做AOP增强时,final类因为无法被继承才会报错。你贴的这段代码能正常跑,原因非常直接:这个场景下Spring根本没给MyService生成任何代理。
具体执行流程
@EnableScheduling注解会往容器里注入ScheduledAnnotationBeanPostProcessor这个后置处理器。它的工作逻辑很简单:等每个单例Bean初始化完成后,扫一遍这个Bean里有没有带@Scheduled注解的方法,如果有,就把当前Bean的实例引用、对应的方法、配置的定时规则打包成一个任务,注册到Spring内置的任务调度器里。- 定时任务触发的时候,调度器是直接拿着原Bean的实例,通过反射调用对应的
scheduleCall方法,根本不需要在方法调用前后做任何拦截、织入额外逻辑,自然就不需要生成代理。 - 代码里的
@PostConstruct是Bean标准生命周期回调,是容器在Bean属性填充完之后直接调用原实例的方法,全程也碰不到代理逻辑。
常见误区说明
很多人会默认Spring里所有特殊注解的实现都靠AOP代理,实际上不同注解的实现逻辑差异很大:
- 必须依赖代理的场景:
@Transactional、@Async、@Cacheable、自定义方法切面这类功能,需要拦截外部对Bean方法的调用,在方法执行前后加事务提交、异步提交、缓存查询这类额外逻辑,才会用JDK动态代理或者CGLIB生成子类代理。这时候如果类是final的、或者要增强的方法是final的,才会出现代理失败的报错。 - 完全不需要代理的场景:
@Scheduled、@PostConstruct、@PreDestroy、@EventListener这类注解,要么是容器启动阶段扫描元数据后直接注册对应逻辑,要么是框架内部自己持有原Bean实例调用方法,和代理没有关系,哪怕类加了final修饰也不会有任何影响。
你可以自己写个简单验证:在scheduleCall方法里打印this.getClass(),会看到输出就是原生的MyService类型,没有任何代理类的标记。要是你给这个类加上@Transactional注解,启动的时候立刻就会因为无法生成final类的子类代理抛错。
对应示例代码:
@Service @EnableScheduling public final class MyService { @PostConstruct public void init(){ System.out.println("MyService started"); } @Scheduled(fixedDelay= 1000) public void scheduleCall() { System.out.println("scheduleCall"); } }
内容的提问来源于stack exchange,提问作者gstackoverflow
相关产品推荐
相关产品推荐

