Java Reflection, Dynamic Proxy & MethodHandle: Core Internals & Performance Compared

This article explores Java's three core metaprogramming mechanisms—reflection, dynamic proxy, and MethodHandle—with code examples, API references, performance comparisons, and real-world usage in frameworks like Spring AOP, MyBatis, and RPC systems.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Java Reflection, Dynamic Proxy & MethodHandle: Core Internals & Performance Compared

Introduction: Java's Three Core Magic Features

Writing Java long enough, you have certainly written code like this: using Class.forName to load a class by configuration, instantiating it via reflection, and invoking methods dynamically. You have also used MyBatis Mapper interfaces without implementation classes, and Spring AOP proxies that automatically open transactions. Behind all these are Java's three great magic powers: Reflection , Dynamic Proxy , and MethodHandle .

Many developers have only a partial understanding of these three concepts. This article explains them from the ground up: what they are, how to use them, underlying principles, performance comparisons, application scenarios, and common pitfalls. After reading, you will understand the technical principles behind MyBatis Mapper, Spring AOP, RPC frameworks, and serialization frameworks, and be able to correctly apply these mechanisms in your own projects.

1. Reflection: Runtime Class Inspection and Manipulation

1.1 What Is Reflection?

Reflection is Java's runtime capability: dynamically obtaining class information (fields, methods, constructors) at runtime, and dynamically invoking methods or accessing fields .

Normal Java code knows the class structure at compile time:

User user = new User();
user.setName("张三");  // Compiler knows User has setName method

Reflection lets you work with classes only known at runtime:

Object obj = ...;  // Runtime type unknown
Method m = obj.getClass().getMethod("setName", String.class);
m.invoke(obj, "张三");  // Runtime method invocation

1.2 Core Reflection APIs

Get Class object: obj.getClass(), Class.forName("fully.qualified.Name"), Xxx.class Get constructors: getConstructor(...), getDeclaredConstructor(...) Create instance: constructor.newInstance(...), clazz.newInstance() (deprecated)

Get methods: getMethod(...), getDeclaredMethod(...) Invoke method: method.invoke(obj, args...) Get fields: getField(...), getDeclaredField(...) Read/write fields: field.get(obj), field.set(obj, value) Bypass access checks:

setAccessible(true)

1.3 Complete Example: Reflectively Operating a Class

public class User {
    private String name;
    private int age;
    public User() {}
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String sayHello(String greeting) {
        return greeting + ", " + name;
    }
    private void secretMethod() {
        System.out.println("私有方法");
    }
}

Using reflection:

public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        // 1. Get Class object
        Class<?> clazz = Class.forName("com.example.User");
        // 2. Create instance
        Object user = clazz.getDeclaredConstructor().newInstance();
        // 3. Invoke public method
        Method sayHello = clazz.getMethod("sayHello", String.class);
        Object result = sayHello.invoke(user, "你好");
        System.out.println(result);  // 你好, null
        // 4. Access private field
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);  // Bypass private restriction
        nameField.set(user, "张三");
        System.out.println(nameField.get(user));  // 张三
        // 5. Invoke private method
        Method secretMethod = clazz.getDeclaredMethod("secretMethod");
        secretMethod.setAccessible(true);
        secretMethod.invoke(user);  // 私有方法
        // 6. Get all information
        System.out.println("Class name: " + clazz.getName());
        System.out.println("Methods: " + Arrays.toString(clazz.getMethods()));
        System.out.println("Fields: " + Arrays.toString(clazz.getDeclaredFields()));
    }
}

1.4 getMethod vs getDeclaredMethod

getMethod(name, ...)

: Returns only public methods, including those inherited from superclasses. getDeclaredMethod(name, ...): Returns all declared methods (public, protected, private), but excludes inherited methods .

Fields follow the same pattern: getField: public fields, including superclasses. getDeclaredField: all fields, excluding superclasses.

This is the most confusing point. To access private fields/methods, you must use getDeclaredXxx + setAccessible(true) .

2. Dynamic Proxy: Runtime Interface Implementation Generation

2.1 What Is Dynamic Proxy?

Dynamic proxy is Java's runtime capability: at runtime, dynamically generate an implementation class for an interface (the proxy class) without manually writing the implementation .

You define an interface, and Java automatically generates an implementation class at runtime. Method calls are forwarded to an InvocationHandler.

2.2 Why Do We Need Dynamic Proxy?

Typical scenarios:

MyBatis Mapper : You only define a UserMapper interface with no implementation; MyBatis uses dynamic proxy to generate the implementation.

Spring AOP : Add logging, transactions, security before/after methods using dynamic proxy.

RPC Frameworks : Call remote interfaces as if they were local methods; dynamic proxy handles network requests.

Logging/Monitoring : Uniformly record method invocation logs.

2.3 Core Dynamic Proxy APIs

// Generate proxy object
Object proxy = Proxy.newProxyInstance(
    classLoader,      // Class loader
    interfaces,       // Array of interfaces to proxy
    invocationHandler // Invocation handler
);
InvocationHandler

:

public interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

2.4 Complete Example: Dynamic Proxy for Logging

Define interface:

public interface UserService {
    User getUserById(Long id);
    void createUser(User user);
}

Implementation class:

public class UserServiceImpl implements UserService {
    @Override
    public User getUserById(Long id) {
        return new User(id, "张三");
    }
    @Override
    public void createUser(User user) {
        System.out.println("创建用户:" + user);
    }
}

Dynamic proxy adding logging:

public class LogInvocationHandler implements InvocationHandler {
    private final Object target;  // Real object being proxied
    public LogInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Before method call
        System.out.println("Calling method: " + method.getName() + ", args: " + Arrays.toString(args));
        // Invoke real method
        Object result = method.invoke(target, args);
        // After method call
        System.out.println("Method returned: " + result);
        return result;
    }
}

Using the proxy:

public class ProxyDemo {
    public static void main(String[] args) {
        UserService realService = new UserServiceImpl();
        // Generate proxy object
        UserService proxy = (UserService) Proxy.newProxyInstance(
            ProxyDemo.class.getClassLoader(),
            new Class[]{UserService.class},
            new LogInvocationHandler(realService)
        );
        // Call proxy methods
        User user = proxy.getUserById(1L);
        proxy.createUser(user);
    }
}

Output:

Calling method: getUserById, args: [1]
Method returned: User{id=1, name='张三'}
Calling method: createUser, args: [User{id=1, name='张三'}]
创建用户:User{id=1, name='张三'}
Method returned: null

2.5 Dynamic Proxy Limitations

JDK dynamic proxy can only proxy interfaces!

The proxy class implements the interfaces you pass in.

It cannot proxy classes (classes that don't implement an interface).

The proxy object and the real object are siblings; both implement the same interface.

In Spring AOP:

If the target class implements an interface → use JDK dynamic proxy.

If the target class does not implement an interface → use CGLIB proxy (generates a subclass).

3. MethodHandle: Java 7's Method Handles

3.1 What Is MethodHandle?

MethodHandle

is a core concept in the java.lang.invoke package introduced in Java 7. It represents a reference to a method, field, or constructor and can be invoked like a method.

Similar to reflection but with different design philosophy:

Reflection : Early Java API, based on java.lang.reflect.

MethodHandle : JVM-level lightweight method reference, more modern design.

Lambda expressions : Under the hood, they are implemented via MethodHandle.

3.2 Core MethodHandle Concepts

MethodHandle

: Method handle, can be invoked directly. MethodType: Method type (return type + parameter types). MethodHandles.Lookup: Entry point for finding methods, simulating different class lookup permissions.

3.3 Complete Example: MethodHandle Invoking Methods

public class MethodHandleDemo {
    public static void main(String[] args) throws Throwable {
        User user = new User("张三", 25);
        // 1. Get Lookup
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        // 2. Find method (sayHello(String) returns String)
        MethodType methodType = MethodType.methodType(String.class, String.class);
        MethodHandle sayHello = lookup.findVirtual(User.class, "sayHello", methodType);
        // 3. Invoke method (first arg is receiver, then method args)
        String result = (String) sayHello.invokeExact(user, "你好");
        System.out.println(result);  // 你好, 张三
        // 4. Invoke static method
        MethodHandle staticMethod = lookup.findStatic(
            UserUtils.class, "staticMethod",
            MethodType.methodType(void.class)
        );
        staticMethod.invokeExact();
        // 5. Access field
        MethodHandle nameGetter = lookup.findGetter(User.class, "name", String.class);
        String name = (String) nameGetter.invokeExact(user);
        System.out.println(name);  // 张三
    }
}

3.4 MethodHandle vs Reflection Comparison

Introduced version: Reflection since JDK 1.0; MethodHandle since JDK 7.

API level: Reflection at Java language level; MethodHandle at JVM level.

Performance: Reflection slower (security checks on every invoke); MethodHandle faster (compiled to bytecode, JIT optimized).

Invocation style: Reflection uses method.invoke(obj, args); MethodHandle uses methodHandle.invokeExact(args).

Type safety: Reflection checks parameter types at runtime; MethodHandle checks at compile time and runtime.

Flexibility: Reflection high (can inspect all class info); MethodHandle lower (focuses only on invocation).

Design purpose: Reflection for frameworks/tools; MethodHandle for lambdas and dynamic language support.

3.5 MethodHandle Application Scenarios

Lambda expressions : () -> System.out.println("hello") generates MethodHandle underneath.

Dynamic languages : Groovy, Kotlin, Scala, and other JVM dynamic languages.

High-performance frameworks : Replace reflection to improve invocation performance.

VarHandle : Java 9 introduction, MethodHandle version for volatile fields.

4. Three-Way Comparison: Reflection, Dynamic Proxy, MethodHandle

4.1 Comparison Summary

Purpose: Reflection – runtime class/method/field manipulation; Dynamic Proxy – runtime interface implementation generation; MethodHandle – lightweight method reference.

Core API: Reflection – java.lang.reflect.Method; Dynamic Proxy – java.lang.reflect.Proxy, InvocationHandler; MethodHandle – java.lang.invoke.MethodHandle.

Introduced: Reflection – JDK 1.0; Dynamic Proxy – JDK 1.3; MethodHandle – JDK 7.

Performance: Reflection – moderate; Dynamic Proxy – moderate (relies on reflection for invocation); MethodHandle – high.

Requires interface: Reflection – no; Dynamic Proxy – yes (JDK dynamic proxy); MethodHandle – no.

Typical applications: Reflection – JDBC, serialization, ORM; Dynamic Proxy – MyBatis Mapper, Spring AOP; MethodHandle – Lambda, dynamic languages.

Flexibility: Reflection – high; Dynamic Proxy – medium; MethodHandle – medium.

4.2 Relationships

Dynamic proxy uses reflection internally : The InvocationHandler.invoke parameter is a Method object; proxy method calls ultimately go through reflection's method.invoke.

MethodHandle is the modern replacement for reflection : Lighter design, better performance, but less flexible than reflection.

The three are not mutually exclusive : Spring AOP uses dynamic proxy, which uses reflection; Lambdas use MethodHandle.

5. Underlying Principles Overview

5.1 Reflection Internals: Method.invoke

What does Method.invoke do?

Security check (verify caller has permission to invoke).

Parameter type checking and conversion.

Invoke underlying JVM method.

Handle return value and exceptions.

Every invoke performs these checks, making it slower than direct calls. The JIT compiler optimizes hot code, but reflection optimization is less effective than direct invocation.

5.2 Dynamic Proxy Internals: Bytecode Generation

What does Proxy.newProxyInstance do at runtime?

Generate proxy class bytecode (e.g., $Proxy0).

The proxy class implements the provided interfaces.

Each method implementation calls InvocationHandler.invoke(proxy, method, args).

Load the proxy class into the JVM.

Create proxy instance.

MyBatis Mapper works exactly this way: UserMapper interface → dynamic proxy generates UserMapper implementation → method calls forwarded to MapperProxy.invoke → SQL execution.

5.3 MethodHandle Internals: Bytecode Level

MethodHandle is a JVM-level concept:

It is a type-safe, directly executable method reference .

The JVM can compile it into efficient bytecode.

Supports invokedynamic instruction (introduced in Java 7).

Lambda expressions are implemented via invokedynamic + MethodHandle.

invokedynamic is a JVM instruction designed for dynamic languages and lambdas. Unlike invokevirtual , invokestatic , and other ordinary method invocation instructions, it dynamically decides which method to call at runtime.

6. Application Scenarios

6.1 MyBatis Mapper: Dynamic Proxy

@Mapper
public interface UserMapper {
    User selectById(Long id);
}

You don't write a UserMapper implementation, yet userMapper.selectById(1) executes SQL. Principle:

MyBatis uses MapperProxyFactory to generate a dynamic proxy for UserMapper.

The proxy class implements the UserMapper interface.

Calling selectById forwards to MapperProxy.invoke. MapperProxy finds the corresponding SQL statement by method name.

Execute SQL and return result.

6.2 Spring AOP: Dynamic Proxy

@Transactional
public void createOrder(Order order) {
    // ...
}

Spring generates a proxy for OrderService:

If OrderService implements an interface → JDK dynamic proxy.

If no interface → CGLIB proxy.

On method call, proxy starts transaction, invokes real method, then commits/rolls back transaction.

6.3 JSON Serialization: Reflection

Jackson, Fastjson, and other JSON libraries:

When converting object to JSON string, use reflection to read field values.

When parsing JSON string to object, use reflection to call setters or write fields directly.

6.4 JDBC: Reflection + Factory Pattern

JDBC driver loading: Class.forName("com.mysql.cj.jdbc.Driver"); Reflection loads the driver class, which registers itself with DriverManager.

6.5 Spring IoC: Reflection + Dynamic Proxy

Bean creation: reflection invokes constructors.

Dependency injection: reflection calls setters or writes fields directly.

AOP: dynamic proxy.

6.6 RPC Frameworks: Dynamic Proxy

Dubbo, Feign, and other RPC frameworks:

You define an interface; the framework generates a proxy.

Calling the interface method serializes method name and arguments, sends to remote service.

Remote service executes and returns result; proxy deserializes and returns.

Summary

Reflection, dynamic proxy, and MethodHandle are Java's three core magic powers and the foundation of framework development.

Key Points Recap

Reflection :

Runtime class info retrieval, dynamic method invocation, field access.

Core APIs: Class, Method, Field, Constructor. getDeclaredXxx + setAccessible(true) to access private members.

Performance moderate but sufficient for business scenarios.

Dynamic Proxy :

Runtime interface implementation generation.

Core APIs: Proxy.newProxyInstance, InvocationHandler.

Can only proxy interfaces, not classes.

Typical applications: MyBatis Mapper, Spring AOP, RPC frameworks.

MethodHandle :

Java 7 lightweight method reference.

Performance better than reflection, close to direct invocation.

Lambda expressions use MethodHandle underneath.

Used for dynamic languages and high-performance scenarios.

Relationships :

Dynamic proxy uses reflection internally.

MethodHandle is the modern replacement for reflection.

All three are cornerstones of framework development.

Application Scenarios :

MyBatis Mapper: dynamic proxy.

Spring AOP: dynamic proxy.

JSON serialization: reflection.

RPC frameworks: dynamic proxy.

Lambda: MethodHandle.

These magic features look profound, but understanding the principles reveals: Reflection is "query class info at runtime then invoke"; Dynamic proxy is "generate interface implementation at runtime"; MethodHandle is "JVM-level method pointer". Frameworks are frameworks because they encapsulate these complex magics, letting business code focus only on business logic.

Reflection, dynamic proxy, and MethodHandle are core to Java framework development and frequent topics in senior interviews. Understanding these principles makes reading Spring, MyBatis, and Dubbo source code much easier, and helps diagnose "why it doesn't work" or "why it throws an error" from the bottom up. Stay tuned for upcoming JVM and internals series: Java bytecode & ASM, class loading deep dive, JVM memory model, garbage collection algorithms, JIT compilation optimization, and more.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JVMreflectionlambdamethodhandleMyBatisspring-aopdynamic-proxyjava-internals
Java Tech Workshop
Written by

Java Tech Workshop

Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.