Java 回调函数的使用

回调函数

回调函数是什么鬼, 回调函数干吗用,回调函数能够怎么用java

若是有过android开发经验,常常能够看到一些相似下面的代码android

Button Btn1 = (Button)findViewById(R.id.button1);//获取按钮资源    
Btn1.setOnClickListener(new Button.OnClickListener(){//建立监听    
    public void onClick(View v) {    
        String strTmp = "点击Button01";    
        Ev1.setText(strTmp);    
    }    

});

上面注册的监听器其实就包含了这个回调的意味了。git

软件模块之间老是存在着必定的接口,从调用方式上,能够把他们分为三类:同步调用、回调和异步调用。
同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用; 回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口; 异步调用:一种相似消息或事件的机制,不过它的调用方向恰好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。github

回调和异步调用的关系很是紧密:使用回调来实现异步消息的注册,经过异步调用来实现消息的通知web

所谓回调,就是客户程序CLIENT调用服务程序SERVER中的某个函数SA(),而后SERVER又在某个时候反过来调用CLIENT中的某个函数mycallback(),对于CLIENT来讲,这个mycallback便叫作回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。apache

简单来讲,就是在调用一个组建的方法时,按照他的定义,注册一个咱们本身的方法,期待这个组建在某一个特意场景下调用咱们注册的方法,实现对应的功能api

设计回调函数的思路

上面简单的说明了什么是回调函数,那么怎么去设计一个回调函数呢?缓存

首先能够明确的是调用方要实现一个注册方法,被调用方提供一个功能,在某些特定的状况下,会调用注册方法java-web

如一个应用场景是:异步

如查询一条微博的点赞数,在db中保存了用户+微博的点赞映射关系,一般须要获取点赞数的时候,须要在db中count一下,计算点赞数,当点赞数不少的时候,改怎么办?每次都count一把? 性能开销难以接受

一个简单的方法是使用缓存,将点赞数保存在缓存中,每次获取点赞数都从缓存取,缓存没有命中的时候,才从db中count一把,并回写到缓存中

上面这个应用场景该如何设计成回调函数的形式呢?

  1. 一个缓存客户端,负责从缓存中获取数据 CacheClient
  2. 业务方,对外提供查询点赞数的功能 (一个对外的getCount()接口,一个内部的查询db计算总数的接口)

通常为了通用性而言,CacheClient内部若是将缓存未命中查db的功能代码封装起来,会有什么问题?耦合过高,无法复用

so 形式话的结构以下:

CacheClient:

  1. 回调接口 CallableInterface
  2. 缓存操做类
  3. 注册回调函数类

使用方 CountService:

  1. db中查询评价总数的方法(否则缓存中的初始数据从哪里来?)
  2. 具体的业务使用逻辑代码

实例

看上面的描述能看懂么?卧槽,本身写的东西本身都看不大懂啊,果真仍是代码是王道,先看看代码,看一下是怎么玩的,而后在回过头去看一下上面的,效果会好不少

注册器相关类:

回调接口 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到什么,就往里面塞什么东西))

相关文章
相关标签/搜索