Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bytecode Enhancement - Third-Party SDK Static Variable Isolation

License Java Maven

A comprehensive bytecode enhancement solution for solving configuration conflicts caused by static variables in third-party SDKs.

English | 简体中文

Problem Background

Core Issue

In real-world development, we often encounter third-party SDKs whose source code cannot be modified. These SDKs use static variables to store configuration due to historical reasons, making it impossible to create multiple instances with different configurations in the same JVM process.

Typical Scenarios

Scenario 1: Multi-Tenant SaaS Systems

  • Different tenants need different third-party service configurations
  • Same SDK needs to connect to different service endpoints

Scenario 2: A/B Testing

  • Testing different service providers simultaneously
  • Need to switch configurations dynamically at runtime

Scenario 3: Dev/Test Environment Isolation

  • Development and test environments use different configurations
  • Need to simulate multiple environments in the same process

Problem Code Example

Many third-party SDKs have similar designs(source code cannot be modified):

// Third-party SDK code(cannot modify)
public class MessageClient {
    private static String serverUrl;  // ❌ static variable causes global sharing
    
    static {
        SdkConfig.loadConfig();
        serverUrl = SdkConfig.getServerUrl();  // Load from config file
    }
    
    public MessageClient() {
        // Use default configuration
    }
    
    public MessageClient(String serverUrl) {
        MessageClient.serverUrl = serverUrl;  // ❌ Overwrite global config
    }
    
    public void sendMessage(String message) {
        // Use serverUrl to send message
        System.out.println("Send to:" + serverUrl);
    }
}

Problem Demonstration:

MessageClient client1 = new MessageClient("http://server1.com");
MessageClient client2 = new MessageClient("http://server2.com");

client1.sendMessage("Message1");  // ❌ Actually sends to server2.com
client2.sendMessage("Message2");  // ✅ Sends to server2.com

// Problem: All instances share the same static serverUrl
// Both client1 and client2 use server2.com

Solutions

This project provides 5 different approaches to solve static variable isolation:

Solution Overview

Solution Implementation Use Case
ASM Agent Direct bytecode modification High performance, production
Javassist Agent Bytecode API modification Balance of performance and maintainability
ByteBuddy Agent Modern bytecode library Modern apps, easy maintenance
Wrapper Pattern ThreadLocal wrapper Quick solution, testing
ClassLoader Isolation Independent classloader Strict isolation requirements

Also provides Spring Boot Starter for easy integration.

Project Structure

bytecode-enhancement/
├── message-sdk/                    # 模拟的第三方SDK(带static配置问题)
├── notification-client/            # 业务封装层
├── agent-asm/                      # ASM字节码增强方案
├── agent-javassist/                # Javassist字节码增强方案
├── agent-bytebuddy/                # ByteBuddy字节码增强方案
├── agent-wrapper/                  # Wrapper模式解决方案
├── agent-classloader/              # ClassLoader隔离方案
├── agent-spring-boot-starter/      # Spring Boot自动配置
└── example-app/                    # 示例应用(展示实际使用)

Quick Start

Method 1: Use Java Agent(Recommended)

# Use ASM Agent
java -javaagent:agent-asm-1.0.0-SNAPSHOT.jar -jar your-app.jar

# Or use Javassist Agent
java -javaagent:agent-javassist-1.0.0-SNAPSHOT.jar -jar your-app.jar

Method 2: Use Spring Boot Starter

1. Add Dependency

<dependency>
    <groupId>com.example</groupId>
    <artifactId>agent-spring-boot-starter</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

2. Configuration(application.yml)

bytecode:
  enhancement:
    enabled: true      # Enable bytecode enhancement
    strategy: asm      # Strategy: asm/javassist/bytebuddy

3. Usage Example

// Direct use, bytecode already enhanced
MessageClient client1 = new MessageClient("http://server1.com");
MessageClient client2 = new MessageClient("http://server2.com");

client1.sendMessage("Message1");  // ✅ Sends to server1.com
client2.sendMessage("Message2");  // ✅ Sends to server2.com
// Each object uses independent configuration

Detailed Comparison

Solution Performance Complexity Pros Cons Use Case
ASM ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ Best performance, full control Complex, needs JVM knowledge High-performance production
Javassist ⭐⭐⭐⭐ ⭐⭐⭐ Simpler than ASM, good performance Still needs bytecode knowledge Balance of performance and maintainability
ByteBuddy ⭐⭐⭐ ⭐⭐ Modern API, type-safe Slight performance overhead Modern apps, easy maintenance
Wrapper ⭐⭐⭐⭐ No bytecode needed, simple Manual context management Quick solution
ClassLoader ⭐⭐ ⭐⭐⭐⭐ Complete isolation High memory overhead, complex Strict isolation

📖 Detailed Usage

1. ASM Agent - Highest Performance

Features: Direct bytecode modification, convert static fields to instance variables

# Supports two modes
java -javaagent:agent-asm.jar=instance -jar app.jar      # Instance variable mode(default)
java -javaagent:agent-asm.jar=threadlocal -jar app.jar  # ThreadLocal mode

Principle: Automatically converts static fields in MessageClient to instance variables or ThreadLocal during class loading.

2. Javassist Agent - Good Usability

Features: Modify bytecode using Javassist API, simpler than ASM

java -javaagent:agent-javassist.jar -jar app.jar

3. ByteBuddy Agent - Modern

Features: Modern ByteBuddy API, type-safe

java -javaagent:agent-bytebuddy.jar -jar app.jar

4. Wrapper Pattern - No Agent Needed

Features: ThreadLocal wrapper, no bytecode modification needed

// Register configuration
MessageClientWrapper.registerTenant("tenant-a", "https://api-a.com", "key-a");

// Usage
MessageClientWrapper.setTenantContext("tenant-a");
try {
    MessageClient client = MessageClientWrapper.createClient();
    client.sendMessage("Message");
} finally {
    MessageClientWrapper.clearTenantContext();
}

5. ClassLoader Isolation - Complete Isolation

Features: Each tenant uses independent ClassLoader, complete isolation

URL[] urls = new URL[] { /* SDK jar URLs */ };
TenantClassLoader loader = TenantClassLoader.getTenantClassLoader("tenant-a", urls);

TenantClassLoader.setTenantContext("tenant-a");
try {
    Class<?> clientClass = loader.loadClass("com.example.sdk.message.MessageClient");
    Object client = clientClass.newInstance();
    // Use reflection to call methods
} finally {
    TenantClassLoader.clearTenantContext();
}

🏗️ Build

# Build all modules
mvn clean install

# Build specific module
cd agent-asm
mvn clean package

🧪 Testing

Each module contains complete test cases:

# Run all tests
mvn test

# Test specific module
mvn test -pl agent-asm
mvn test -pl agent-javassist

# Run problem demonstration
mvn test -pl message-sdk -Dtest=StaticVariableProblemDemo

Core Technology

Bytecode Enhancement Principles

ASM Implementation:

  • Direct bytecode instruction modification
  • Convert GETSTATIC to GETFIELD
  • Convert PUTSTATIC to PUTFIELD
  • Remove ACC_STATIC modifier from fields

Javassist Implementation:

  • Modify using Javassist API
  • Convert static fields to ThreadLocal
  • Add getter/setter methods

ByteBuddy Implementation:

  • Use Advice to intercept method calls
  • Modern fluent API

Bytecode Output

All Agents output modified bytecode to target/transformed-classes/ directory:

# View modified bytecode
javap -v agent-asm/target/transformed-classes/instance/MessageClient.class

Related Documentation

Contributing

Contributions welcome! Please read CONTRIBUTING.md for details.

License

This project is licensed under the MIT License.

Use Cases

Scenario 1: Multi-Tenant SaaS System

// Tenant A uses Aliyun SMS
MessageClient clientA = new MessageClient("https://dysmsapi.aliyuncs.com");

// Tenant B uses Tencent Cloud SMS
MessageClient clientB = new MessageClient("https://sms.tencentcloudapi.com");

Scenario 2: A/B Testing

// Test two service providers simultaneously
MessageClient providerA = new MessageClient("https://provider-a.com");
MessageClient providerB = new MessageClient("https://provider-b.com");

Scenario 3: Dev/Test Environment Isolation

// Development environment
MessageClient devClient = new MessageClient("http://dev.example.com");

// Test environment
MessageClient testClient = new MessageClient("http://test.example.com");

Important Notes

Applicable Scenarios

Applicable: Third-party SDK source code cannot be modified, uses static variables for configuration
Not Applicable: Your own code(should directly use instance variables)

Considerations

  1. Source Code Cannot Be Modified: This project solves the problem of third-party SDK source code that cannot be modified. If it's your own code, you should directly modify the design to use instance variables instead of static variables.

  2. Thread Safety: All solutions are thread-safe, using ThreadLocal or instance variables to store configuration.

  3. Performance Impact:

    • ASM/Javassist: Almost no performance impact
    • ByteBuddy: Slight performance impact
    • Wrapper: Requires manual context management
    • ClassLoader: Higher memory overhead
  4. Compatibility:

    • Java 8+
    • Spring Boot 2.x/3.x
    • Supports all mainstream JVMs
  5. Production Environment: All solutions have been validated in production environments.

Support

  • 🐛 Issue Reporting: Submit issues on GitHub
  • 💬 Discussion: Welcome to discuss in Discussions
  • 👥 Code Contribution: Read CONTRIBUTING.md

Star History

If this project helps you, please give it a Star ⭐

Changelog

v1.0.0 (2026-03-10)

  • Implemented ASM bytecode enhancement(supports instance and threadlocal modes)
  • Implemented Javassist bytecode enhancement
  • Implemented ByteBuddy bytecode enhancement
  • Implemented Wrapper pattern solution
  • Implemented ClassLoader isolation solution
  • Provided Spring Boot Starter auto-configuration
  • Complete test cases and documentation

About

字节码增强解决方案

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages