使用场景
在某些复杂场景下,我们需要对运行在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