package com.apexsoft.citic.managed.assistance.module.backdoor.service;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Service
public class BackdoorService {
private static Logger log = Logger.getLogger(BackdoorService.class);
@Resource
SqlSession sqlSession;
private static long lastRefreshMapperTime = System.currentTimeMillis();
private static boolean isModifiedStrictMapOfConfiguration = false;
public Set<String> refreshMappper() throws NoSuchFieldException, IllegalAccessException, IOException {
long startRefreshMapperTime = System.currentTimeMillis();
Configuration configuration = sqlSession.getConfiguration();
Field loadedResourcesField = configuration.getClass().getDeclaredField("loadedResources");
loadedResourcesField.setAccessible(true);
Set<String> loadedResources = (Set<String>) loadedResourcesField.get(configuration);
Set<String> loadedResourcesBak = new HashSet<String>(loadedResources);
Set<String> refreshedResources = new HashSet<String>();
for (String resource : loadedResourcesBak) {
if (resource.endsWith(".xml") && isNeedRefresh(resource)) {
loadedResources.remove(resource);
if(!isModifiedStrictMapOfConfiguration){
modifyStrictMapOfConfiguration(configuration);
}
//使用classloader.getResource().openStream() 可以清除缓存
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResource(resource).openStream();
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
refreshedResources.add(resource);
}
}
lastRefreshMapperTime = startRefreshMapperTime;
return refreshedResources;
}
private synchronized void modifyStrictMapOfConfiguration(Configuration configuration) throws NoSuchFieldException, IllegalAccessException {
if (!isModifiedStrictMapOfConfiguration){
isModifiedStrictMapOfConfiguration = true;
// 清理原有资源,更新为自己的StrictMap方便,增量重新加载
String[] mapFieldNames = new String[]{"mappedStatements", "caches", "resultMaps", "parameterMaps", "keyGenerators", "sqlFragments"};
for (String fieldName : mapFieldNames) {
Field field = configuration.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Map map = ((Map) field.get(configuration));
InvocationHandler invocationHandler = new StrictMapInvocationHandler(map);
Map mapProxy = (Map)Proxy.newProxyInstance(map.getClass().getClassLoader(), map.getClass().getSuperclass().getInterfaces(), invocationHandler);
field.set(configuration, mapProxy);
}
}
}
private boolean isNeedRefresh(String resource) {
String nodepath = this.getClass().getClassLoader().getResource("/").getPath();
File file = new File(nodepath + resource);
if (file.lastModified() < lastRefreshMapperTime) {
return false;
}
return true;
}
protected class StrictMapInvocationHandler implements InvocationHandler{
private Map target;
public StrictMapInvocationHandler(Map target){
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("put")){
String key = args[0].toString();
target.remove(key);
log.info("refresh key:" + key.substring(key.lastIndexOf(".") + 1));
}
return method.invoke(target, args);
}
}
}
ps: 目前代码只是实现了mapping.xml的重新加载,可以作为java热部署技术的补充