Reflection
Reflection is Java’s ability to dynamically obtain class information (such as class names, methods, properties, constructors, etc.) and manipulate classes at runtime.
API
Getting Class Objects
1
2
3
4
5
6
7
| String str="test";
Class<?> clazz = String.class;
String str = "test";
Class<?> clazz = str.getClass();
Class<?> clazz = Class.forName("java.lang.String");
|
Creating Objects Through Reflection
1
2
3
4
5
| Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.getDeclaredConstructor().newInstance();
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("Alice", 25);
|
Getting Fields
getDeclaredField(“fieldName”)
Advantages
- Can access private objects/fields of classes, convenient for unit testing
1
2
3
| Field nameField = personClass.getDeclaredField("name");
nameField.setAccessible(true); // If field is private, need to set accessibility
System.out.println("Name before: " + nameField.get(person));
|
Dynamic proxy, improves code reusability
Spring framework
Relationship Between Generics, Reflection and Type Inference
Dynamic Proxy
Advantages: Dynamically generates proxy classes, no need to manually define multiple proxy classes.
- Define an interface UserService with method A that I want to be proxied
- Create a proxy class UserServiceProxy that implements the InvocationHandler interface, which means overriding the invoke() method. Specifically, add the logic I want before or after calling method A. invoke calls method A through reflection.
- UserServiceImpl class implements the UserService interface methods.
- In the main program, first create a UserServiceImpl class object, then use the newProxyInstance method to create a UserService class object, this is a proxy. The method parameters include: UserService’s class loader, UserService’s interface, and a UserServiceProxy class object.
- userService can then call method A to implement the logic we want
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class UserServiceProxy implements InvocationHandler {
private Object target;
public UserServiceProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Logic before method call");
Object result = method.invoke(target, args);
System.out.println("Logic after method call");
return result;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy(userService);
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
proxy);
userServiceProxy.addUser();
}
}
|
vs Static Proxy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| Car car = new Car();
InvocationHandler carHandler = new DynamicProxyHandler(car);//Dynamically generate proxy class based on passed object
Vehicle carProxy = (Vehicle) Proxy.newProxyInstance(
car.getClass().getClassLoader(),
car.getClass().getInterfaces(),
carHandler);
carProxy.drive();
Motorcycle motorcycle = new Motorcycle();
InvocationHandler motorcycleHandler = new DynamicProxyHandler(motorcycle);//Dynamically generate proxy class based on passed object
Vehicle motorcycleProxy = (Vehicle) Proxy.newProxyInstance(
motorcycle.getClass().getClassLoader(),
motorcycle.getClass().getInterfaces(),
motorcycleHandler);
motorcycleProxy.drive();
|
Static proxy requires implementing a CarProxy class and a Motorcycle proxy class separately for the Vehicle interface, passing in a Car and Motor object respectively.
CGLib Proxy
The Spring framework chooses which proxy method to use: if there’s an interface, use JDK Proxy; if there’s no interface, use CGLib.
For example, if there’s a class that doesn’t implement an interface and you want to add some functionality to its methods, you can use CGLIB.
- Define implementation class
- Define an Interceptor class that implements the MethodInterceptor interface, implementing the logic we want to be proxied.
- Use Enhancer to set its parent class as the target class, then set the callback (method interceptor). This way CGLIB can dynamically generate a subclass of the target class as the proxy class at runtime.
| Scenario | JDK Proxy | CGLib Proxy |
|---|
| Proxy Generation Speed | Faster (directly generates interface proxy class) | Slower (needs to generate subclass and enhance bytecode) |
| Method Call Speed | Slower (reflection call) | Faster (directly calls subclass method) |
| Memory Usage | Low (lightweight proxy class) | Higher (generated subclass occupies more metaspace) |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // Target class (no need to implement interface)
public class UserService {
public void save() {
System.out.println("Save user");
}
}
// Method interceptor
public class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Pre-processing");
Object result = proxy.invokeSuper(obj, args); // Call parent class method
System.out.println("Post-processing");
return result;
}
}
// Create proxy object
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MyMethodInterceptor());
UserService proxy = (UserService) enhancer.create();
proxy.save();
|