使用場景
在某些複雜場景下,我們需要對運行在tomcat容器中部分功能進行mock(替換其實現),但該部分功能散落在各處,我們希望不修改源代碼以非侵入的方式來實現Mock,在這種情況下,我們可以應用Javassist來實現。
使用Javassist來動態Mock
我們可以定義一個ContextListener的實例,在tomcat啓動時通過Javassist對源代碼進行動態替換,來實現mock的功能。
使用Javassist在tomcat容器中動態替換源碼來實現動態Mock:
以下是示例代碼片段:
package test;
import lombok.extern.log4j.Log4j2;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.HashMap;
import java.util.Map;
import org.lightfw.utilx.dynamic.JavassistUtil;
/**
*/
@Log4j2
public class MockListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent contextEvent) {
log.info("Context Listener start................");
//將TestController類的test方法的實現替換爲"return 1;"
JavassitUtil.replaceMethodBody("com.xx.web.controller.TestController", "test", "return 1");
log.info("Context Listener started");
}
public void contextDestroyed(ServletContextEvent sc) {
log.info("Context Listener stopped");
}
接下來,在web.xml中添加該Listener就可以了。
<listener>
<listener-class>test.MockListener</listener-class>
</listener>
Javassist相關代碼:
private static ClassPool classPool;
static {
classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(JavassitUtil.class)); //主要用於web環境
}
/**
* 替換方法體
*
* @param className 類名,如:foo.Student
* @param methodName 方法名
* @param newMethodBody 新的方法體,如:"System.out.println(\"this method is changed dynamically!\");"
*/
public static void replaceMethodBody(String className, String methodName, String newMethodBody) {
try {
CtClass clazz =classPool.get(className);
CtMethod method = clazz.getDeclaredMethod(methodName);
method.setBody(newMethodBody);
clazz.toClass();
} catch (NotFoundException | CannotCompileException e) {
throw new RuntimeException(e);
}
}
本例需要使用的工具類可以到以下地址下載:https://github.com/bjo2008cnx/lightfw-util/blob/master/src/main/java/org/lightfw/utilx/dynamic/JavassitUtil.java