IKAnalyzer1.3.4要自定义我们自己的词库,而且我们可以随时新增分词,网上查了一圈没有相关资料,看来只有自己搞定了。这里大家需要熟悉HTTP协议中的Last-Modified、ETags这些概念,这样能更容易理解IKAnalyzer作者的设计思路。
观察了下IKAnalyzer分词器的配置文件IKAnalyzer.cfg.xml发现其中有这样一个选项:
<!--用户可以在这里配置远程扩展字典 --><entry key="remote_ext_dict"><entry>,而且作者做了注释说这个就是扩展远程词典的。于是打开IKAnalyzer的源码查看,最后在Dictronary这类下找到这个方法:getRemoteWords。经过查看发现其实用的是HttpClient去获取分词。于是我就用SpringMVC写了个Controller来解决。这里要注意:每个分词之间要使用换行符即“\r\n”来分割,貌似问题圆满解决,
可是,我发现IKAnalyzer的这个获取分词的动作只是在启动的时候去访问我写的Controller。很显然这是不行的,这就违背了我随时新增分词的愿望了,看来我要扩展这个分词器了。于是我开始从头翻这个开源分词器的源码,我在Dictronary最后找到以下的代码,顿时让我眼前一亮:
public void reLoadMainDict(){
logger.info("重新加载词典...");
loadMainDict();
loadStopWordDict();
}
顾名思义,这个是重载分词的。于是我问自己,什么时候重载?如何重载?于是我搜了下,最后再Monitor这个类下找到run这个方法,这是Monitor实现Runnable接口的,在这个方法里这个分词器先去构造httphead,并且带上If-None-Match、If-Modified-Since这俩参数去访问Controller(关于这俩参数的概念,大家可以上网查),然后根据返回来的response的head里的Last-Modified和ETags来和Monitor缓存的变量进行比较,如果任何一个不相同就需要重新访问Controller中去获取数据,说到这里可以总下:可以在服务端(Controller)中设置这俩变量,来控制IKAnalyzer是否重新加载分词。OK,分析到这里问题解决了。最后我写的Controller中代码大概如下:
/**
* 获取分词
* @return
*/
@RequestMapping("getDict")
@ResponseBody
public String getDict(HttpServletRequest request, HttpServletResponse response) {
String result = "";
StringBuilder sb = new StringBuilder();
List<Word> wordList = wordService.selectAllWord();//获取所有分词,这里可以改进使用缓存等。
String eTag = request.getHeader("If-None-Match");
Long modified= request.getDateHeader("If-Modified-Since");
//设置头
if(null == modified || -1 == modified) {
//如果没有,则使用当前时间
modified = System.currentTimeMillis();
}
/ /设置头信息。
String oldEtag = wordList.size() + "";
response.setDateHeader("Last-Modified", Long.valueOf(modified));
response.setHeader("ETags", wordList.size() + "");
if(!oldEtag.equals(eTag)) {
//拼装结果
for(Word tempWord : wordList) {
//分词之间以换行符连接
if(StringUtils.isNotEmpty(sb.toString())) {
sb.append("\r\n");
}
sb.append(tempWord.getValue());
}
result = sb.toString();
//更新时间
response.setDateHeader("Last-Modified", System.currentTimeMillis());
}
return result;
}
这里还可以再优化,这是后话了,有了这个思路其余的都是锦上添花。