Java 回调函数的使用
回调函数
回调函数是什么鬼, 回调函数干嘛用,回调函数可以怎么用
如果有过android开发经验,经常可以看到一些类似下面的代码
Button Btn1 = (Button)findViewById(R.id.button1);//获取按钮资源
Btn1.setOnClickListener(new Button.OnClickListener(){//创建监听
public void onClick(View v) {
String strTmp = "点击Button01";
Ev1.setText(strTmp);
}
});
上面注册的监听器其实就包含了这个回调的意味了。
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。 同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用; 回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口; 异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。
回调和异步调用的关系非常紧密:使用回调来实现异步消息的注册,通过异步调用来实现消息的通知
所谓回调,就是客户程序CLIENT调用服务程序SERVER中的某个函数SA(),然后SERVER又在某个时候反过来调用CLIENT中的某个函数mycallback(),对于CLIENT来说,这个mycallback便叫做回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。
简单来说,就是在调用一个组建的方法时,按照他的定义,注册一个我们自己的方法,期待这个组建在某一个特地场景下调用我们注册的方法,实现对应的功能
设计回调函数的思路
上面简单的说明了什么是回调函数,那么怎么去设计一个回调函数呢?
首先可以明确的是调用方要实现一个注册方法,被调用方提供一个功能,在某些特定的情况下,会调用注册方法
如一个应用场景是:
如查询一条微博的点赞数,在db中保存了用户+微博的点赞映射关系,通常需要获取点赞数的时候,需要在db中count一下,计算点赞数,当点赞数很多的时候,改怎么办?每次都count一把? 性能开销难以接受
一个简单的方法是使用缓存,将点赞数保存在缓存中,每次获取点赞数都从缓存取,缓存没有命中的时候,才从db中count一把,并回写到缓存中
上面这个应用场景该如何设计成回调函数的形式呢?
- 一个缓存客户端,负责从缓存中获取数据 CacheClient
- 业务方,对外提供查询点赞数的功能 (一个对外的getCount()接口,一个内部的查询db计算总数的接口)
一般为了通用性而言,CacheClient内部如果将缓存未命中查db的功能代码封装起来,会有什么问题?耦合太高,没法复用
so 形式话的结构如下:
CacheClient:
- 回调接口 CallableInterface
- 缓存操作类
- 注册回调函数类
使用方 CountService:
- db中查询评价总数的方法(不然缓存中的初始数据从哪里来?)
- 具体的业务使用逻辑代码
实例
看上面的描述能看懂么?卧槽,自己写的东西自己都看不大懂啊,果然还是代码是王道,先看看代码,看一下是怎么玩的,然后在回过头去看一下上面的,效果会好很多
注册器相关类:
回调接口 CacheCallBackInterface
package com.mushroom.hui.common.register.callback;
/**
* 缓存未命中的回调函数
* Created by yihui on 16/4/5.
*/
public interface CacheCallBackInterface {
String getKey(int id);
int getExpire();
Object getObject(String key);
}
注册接口 BaseRegister.java --> 为什么要设计成接口?看CacheCient的时候会理解一点的
package com.mushroom.hui.common.register;
import com.mushroom.hui.common.register.callback.CacheCallBackInterface;
import java.util.HashMap;
import java.util.Map;
/**
* Created by yihui on 16/4/5.
*/
public interface BaseRegister {
Map<String, CacheCallBackInterface> map = new HashMap<>();
void register(String name, CacheCallBackInterface callback) throws Exception;
Object exec(String name, int id) throws Exception;
<T> T exec(String name, int id, Class<T> clz) throws Exception;
}
CacheClient.java 对外提供的缓存客户端, 这个里面就实现了传说中的回调函数的使用
package com.mushroom.hui.common.cache;
import com.mushroom.hui.common.cache.api.CacheInterface;
import com.mushroom.hui.common.register.BaseRegister;
import com.mushroom.hui.common.register.callback.CacheCallBackInterface;
import com.mushroom.hui.common.register.exception.RegisterException;
import org.apache.commons.lang.StringUtils;
import javax.annotation.Resource;
/**
* Created by yihui on 16/4/5.
*/
public class CacheClient implements BaseRegister {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CacheClient.class);
@Resource(name = "cacheService")
private CacheInterface methodCache;
@Override
public void register(String name, CacheCallBackInterface callback) throws Exception {
if(StringUtils.isBlank(name)) {
throw new IllegalArgumentException("key is empty!");
}
if (map.containsKey(name)) {
throw new RegisterException("this callback interface has already registered! name : " + name);
}
map.put(name, callback);
}
@Override
public Object exec(String name, int id) throws Exception {
return exec(name, id, Object.class);
}
@Override
public <T> T exec(String name, int id, Class<T> clz) throws Exception {
CacheCallBackInterface callBackInterface = map.get(name);
if (callBackInterface == null) {
throw new RegisterException("this callback interface has already registered! name : " + name);
}
String key = callBackInterface.getKey(id);
Object obj = methodCache.getObject(key, clz);
if (obj == null) {
obj = callBackInterface.getObject(key);
methodCache.setObject(key, obj, callBackInterface.getExpire());
}
return (T) obj;
}
/**
* 获取缓存的对象
* @param name 注册的回调函数name
* @param id id
* @param clz 返回的对象类型
* @param <T>
* @return
* @throws Exception
*/
public <T> T getObject(String name, int id, Class<T> clz) throws Exception {
return exec(name, id, clz);
}
}
测试类中,对上面的功能进行测试,代码如下:
@Test
public void testCacheClient() throws Exception {
int id = 1002;
String name = "cache_test";
// 注册回调函数
cacheClient.register(name, new CacheCallBackInterface(){
@Override
public String getKey(int id) {
return "test_" + id;
}
@Override
public int getExpire() {
return 30;
}
@Override
public Object getObject(String key) {
return key.length();
}
});
Integer count = cacheClient.getObject(name, id ,Integer.class);
logger.info("The count is : {}", count);
cacheService.setObject("test_" + id, 1231234, 30);
count = cacheClient.getObject(name, id, Integer.class);
logger.info("The count is : {}", count);
Thread.sleep(1000);
count = cacheClient.getObject(name, id, Integer.class);
logger.info("The count is : {}", count);
}
题外话
写代码的时候还是很清晰,知道应该怎么玩,而实际动手写这个流程的时候,还是有很多问题啊,有一些地方居然不清楚为什么要那么设计,为什么要那么玩,简直了,看来这一块了解的还是不够透彻,后面把这一块吃透后,得重写一遍
最后给出代码的git地址 : https://github.com/liuyueyi/java-web-archetype/tree/demo
(这个工程主要是一个简单的java web demo实例工程,会逐渐的向其中添加一些公用的组件(工作中get到什么,就往里面塞什么东西))
- 1611: [Usaco2008 Feb]Meteor Shower流星雨
- 3893: [Usaco2014 Dec]Cow Jog
- 3892: [Usaco2014 Dec]Marathon
- BZOJ 2793: [Poi2012]Vouchers(调和级数)
- 3891: [Usaco2014 Dec]Piggy Back
- Java8-如何构建一个Stream
- 2016: [Usaco2010]Chocolate Eating
- javascript 闭包详解
- 3016: [Usaco2012 Nov]Clumsy Cows
- POJ 3207 Ikki's Story IV - Panda's Trick(2-SAT)
- 3359: [Usaco2004 Jan]矩形
- 漫谈Java IO之 Netty与NIO服务器
- Java线程的几种状态
- POJ3683 Priest John's Busiest Day(2-SAT)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Matplotlib自定义坐标轴刻度的实现示例
- Python基于time模块表示时间常用方法
- ITK 实现多张图像转成单个nii.gz或mha文件案例
- 一文轻松掌握python语言命名规范规则
- php原生数据库分页的代码实例
- python对一个数向上取整的实例方法
- Laravel框架FormRequest中重写错误处理的方法
- python名片管理系统开发
- PHP封装mysqli基于面向对象的mysql数据库操作类与用法示例
- PHP实现微信商户支付企业付款到零钱功能
- yii2 开发api接口时优雅的处理全局异常的方法
- PHP调用全国天气预报数据接口查询天气示例
- Python使用itcaht库实现微信自动收发消息功能
- Laravel源码解析之路由的使用和示例详解
- PHP实现的用户注册表单验证功能简单示例