首页 / 资讯中心 / 文章详情

回调机制详解:从概念到 Spring 实践

回调机制详解:从概念到 Spring 实践 ★ FEATURED ARTICLE
回调机制详解从概念到 Spring 实践一、什么是回调回调Callback是一种编程模式把一段代码函数或对象作为参数传给另一个方法由那个方法在合适的时机反过来调用这段代码。通俗理解你找别人帮忙办事顺便留了个电话告诉对方“事情办完了打这个电话通知我”。对方办完事后按你留的电话联系你——这就是回调。你留的电话就是回调函数对方在合适的时机“反过来调用”你。二、为什么需要回调在没有回调的代码里调用方必须等待被调用方执行完毕然后自己处理后续逻辑。这导致调用方必须知道被调用方的执行细节无法在特定时机插入自定义逻辑代码耦合度高扩展困难回调把这些控制权反转过来被调用方在合适的时机主动通知调用方调用方只需要提供“通知我时该做什么”。这本质上是一种控制反转IoC——控制权从调用方转移到了被调用方。三、回调的两种类型1. 同步回调调用方传入回调对象被调用方在执行过程中直接调用回调方法整个过程在同一个线程中完成调用方会阻塞直到被调用方执行完毕。// 回调接口publicinterfaceCallback{voidonComplete(Stringresult);}// 被调用方publicclassTaskExecutor{publicvoidexecute(Callbackcallback){System.out.println(开始执行任务);Stringresult任务完成;// 执行完毕后回调callback.onComplete(result);}}// 调用方publicclassClient{publicstaticvoidmain(String[]args){TaskExecutorexecutornewTaskExecutor();executor.execute(newCallback(){OverridepublicvoidonComplete(Stringresult){System.out.println(收到回调result);}});}}输出开始执行任务 收到回调任务完成2. 异步回调调用方传入回调对象后立即返回被调用方在另一个线程中执行任务完成后通过回调通知调用方。调用方不阻塞。publicclassAsyncTaskExecutor{publicvoidexecute(Callbackcallback){newThread(()-{System.out.println(异步任务开始);try{Thread.sleep(1000);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}callback.onComplete(异步任务完成);}).start();System.out.println(调用方继续执行其他逻辑);}}输出调用方继续执行其他逻辑 异步任务开始 1 秒后 异步任务完成四、回调的常见实现形式1. 接口回调最经典的形式。定义一个接口调用方实现接口被调用方持有接口引用并调用。publicinterfaceEventListener{voidonEvent(Stringevent);}2. Lambda 表达式回调Java 8 之后函数式接口可以用 Lambda 简化。publicclassTaskExecutor{publicvoidexecute(ConsumerStringcallback){StringresultdoSomething();callback.accept(result);}}3. 匿名内部类回调Java 8 之前常用的写法现在逐渐被 Lambda 取代。executor.execute(newCallback(){OverridepublicvoidonComplete(Stringresult){System.out.println(result);}});五、Spring 中的回调机制Spring 框架大量使用了回调机制理解回调是理解 Spring 扩展点的关键。1. Bean 生命周期中的回调Spring 在 Bean 创建过程中会在不同阶段回调 Bean 实现的方法ComponentpublicclassMyBeanimplementsInitializingBean,DisposableBean{OverridepublicvoidafterPropertiesSet(){// 属性填充完成后Spring 回调此方法System.out.println(初始化回调);}Overridepublicvoiddestroy(){// 容器关闭时Spring 回调此方法System.out.println(销毁回调);}}PostConstruct和PreDestroy也是回调ComponentpublicclassMyBean{PostConstructpublicvoidinit(){// Spring 在 Bean 初始化阶段回调}PreDestroypublicvoidcleanup(){// Spring 在容器关闭时回调}}2. Aware 接口回调Bean 实现 Aware 接口后Spring 会回调对应方法把容器资源传进来ComponentpublicclassMyBeanimplementsBeanNameAware,ApplicationContextAware{OverridepublicvoidsetBeanName(Stringname){// Spring 回调传入 Bean 名称}OverridepublicvoidsetApplicationContext(ApplicationContextcontext){// Spring 回调传入 ApplicationContext}}3. BeanPostProcessor 回调BeanPostProcessor是 Spring 最重要的回调扩展点。容器在创建每个 Bean 时都会回调所有注册的BeanPostProcessorComponentpublicclassMyBeanPostProcessorimplementsBeanPostProcessor{OverridepublicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName){// 初始化前回调returnbean;}OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName){// 初始化后回调AOP 代理在这里生成returnbean;}}4. 事件监听回调Spring 的事件机制也是回调ComponentpublicclassMyEventListener{EventListenerpublicvoidhandleEvent(ApplicationReadyEventevent){// 应用启动完成后Spring 回调此方法System.out.println(应用已就绪);}}5. JdbcTemplate 回调JdbcTemplate中的RowMapper和ResultSetExtractor是典型的回调接口ListUserusersjdbcTemplate.query(SELECT * FROM user,newRowMapperUser(){// 回调对象OverridepublicUsermapRow(ResultSetrs,introwNum)throwsSQLException{// JdbcTemplate 遍历结果集时回调此方法UserusernewUser();user.setId(rs.getLong(id));user.setName(rs.getString(name));returnuser;}});JdbcTemplate负责管理连接、执行 SQL、遍历结果集但“每一行如何映射为对象”这个逻辑由调用方通过回调提供。6. 事务回调TransactionTemplate使用回调封装事务transactionTemplate.execute(newTransactionCallbackWithoutResult(){OverrideprotectedvoiddoInTransactionWithoutResult(TransactionStatusstatus){// 事务内执行的逻辑由 TransactionTemplate 回调userDao.save(user);orderDao.save(order);}});TransactionTemplate负责开启事务、提交/回滚事务具体业务逻辑由回调提供。六、回调 vs 普通方法调用对比维度普通方法调用回调控制方向调用方主动调用被调用方被调用方在合适时机调用调用方执行时机调用方决定被调用方决定代码耦合调用方依赖被调用方双方都依赖接口解耦扩展性修改被调用方才能扩展传入不同的回调实现即可扩展典型场景工具类方法事件处理、生命周期钩子、模板方法七、回调的本质回调的本质是将行为参数化。正常情况下我们传给方法的是数据字符串、数字、对象。回调把“行为”一段逻辑也作为参数传递让被调用方在合适的时机执行这段行为。这带来了两个核心好处解耦调用方不需要知道被调用方的内部实现只需要提供一个回调接口的实现。扩展被调用方的代码不变通过传入不同的回调实现可以改变程序行为。回调也是模板方法模式的基础。模板方法定义流程骨架具体步骤由回调填充。Spring 的JdbcTemplate、TransactionTemplate都是这个思路。八、总结维度核心要点定义把一段代码作为参数传给另一个方法由对方在合适时机调用本质行为参数化控制反转类型同步回调阻塞、异步回调不阻塞实现形式接口、Lambda、匿名内部类Spring 中的应用生命周期回调、Aware 接口、BeanPostProcessor、事件监听、JdbcTemplate、事务模板核心价值解耦、扩展、控制反转回调不是什么高深的技术而是一种思维方式我把“做什么”告诉你你决定“什么时候做”。理解回调就能理解 Spring 为什么能在不修改业务代码的情况下插入事务、日志、缓存等横切逻辑——因为它把扩展点设计成了回调接口让开发者可以“留个电话等通知”。
阅读完成 · 觉得有帮助?
咨询建站