Modifying Bytecode Before Class Loading in Spring Cloud Using Javassist
This article demonstrates how to use Spring's ApplicationContextInitializer together with Javassist to intercept class loading in a Spring Cloud environment, modify the bytecode of org.apache.commons.lang3.RandomStringUtils, and record method calls, handling parent‑child container initialization nuances.
In Spring Cloud projects many features are implemented via AOP or Java agents. When neither approach is feasible, developers can directly manipulate bytecode using Javassist to achieve the desired behavior before a class is loaded.
To perform bytecode modification early, Spring provides the ApplicationContextInitializer extension point. In a Spring Cloud setup with parent and child containers, this initializer runs twice, so the modification should be applied during the child container initialization.
The following abstract base class defines the initializer framework. Subclasses implement the doJavassist method to apply specific bytecode changes.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import java.io.IOException;
import java.util.*;
/**
* @author:
* date: 2088/15/19
*/
public abstract class BaseApplicationContextInitializer implements ApplicationContextInitializer {
protected Logger log = LoggerFactory.getLogger(getClass());
private static volatile int initialized = 0;
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
Optional
optional = Arrays.stream(applicationContext.getEnvironment().getActiveProfiles())
.filter(t -> "online".equals(t) || "unittest".equals(t))
.findFirst();
if (optional.isPresent()) {
return;
}
if (initialized == 1) {
log.info(getClass().getSimpleName() + " begin");
try {
doJavassist();
log.info(getClass().getSimpleName() + " end");
} catch (Throwable e) {
log.error(getClass().getSimpleName(), e);
throw new RuntimeException(e);
}
initialized += 1;
}
if (initialized == 0) {
initialized += 1;
}
}
protected abstract void doJavassist() throws Exception;
}The concrete implementation below modifies the random method of Apache Commons RandomStringUtils . It creates a copy of the original method, renames it, and injects a new method body that records the invocation and result via a hypothetical RecordUtils.record utility.
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import java.util.Random;
/**
* @author:
* date: 2088/15/19
*/
public class RandomStringUtilsAop extends BaseApplicationContextInitializer {
@Override
protected void doJavassist() throws Exception {
ClassPool classPool = ClassPool.getDefault();
classPool.importPackage("java.util.Random");
CtClass randomStringUtilsClass = classPool.get("org.apache.commons.lang3.RandomStringUtils");
CtClass[] paramsClasses = new CtClass[]{
classPool.get(int.class.getName()),
classPool.get(int.class.getName()),
classPool.get(int.class.getName()),
classPool.get(boolean.class.getName()),
classPool.get(boolean.class.getName()),
classPool.get(char[].class.getName()),
classPool.get(Random.class.getName())
};
CtMethod ctMethod = randomStringUtilsClass.getDeclaredMethod("random", paramsClasses);
CtMethod srcInvoke = CtNewMethod.copy(ctMethod, randomStringUtilsClass, null);
srcInvoke.setName("srcInvoke");
randomStringUtilsClass.addMethod(srcInvoke);
ctMethod.setBody("{\n"
+ " String className = \"org.apache.commons.lang3.RandomStringUtils\";\n"
+ " String methodName = \"random\";\n"
+ " String result = null;\n"
+ " Throwable ex = null;\n"
+ "\n"
+ " try {\n"
+ " result = srcInvoke($$);\n"
+ " } catch (RuntimeException e) {\n"
+ " ex = e;\n"
+ " }\n"
+ "\n"
+ " RecordUtils.record(className, methodName, result, ex);\n"
+ " if (ex != null) {\n"
+ " throw ex;\n"
+ " }\n"
+ " return result;\n"
+ " }");
randomStringUtilsClass.toClass(); // load modified class, ensure it was not loaded before
if (randomStringUtilsClass.isFrozen()) {
randomStringUtilsClass.defrost();
}
}
}By placing this initializer in the Spring Cloud application, the bytecode of RandomStringUtils.random is altered before the class is first used, enabling custom recording or monitoring logic without relying on AOP or external agents.
Cognitive Technology Team
Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.