反射核心操作 Link to heading
public class ReflectDemo {
public static void main(String[] args) throws Exception{
//1.获取class对象
Class<?>clazz=Class.forName("com.example.Person");
//2.创建对象实例(调用有参构造实例)
Constructor<?>constructor=clazz.getConstructor(String.class,int.class);
Object person=constructor.newInstance("张三",25);
//3. 调用方法
Method sayHello=clazz.getMethod("sayHello");
sayHello.invoke(person);
//4. 修改私有属性
Field nameField=clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(person,"李四");
sayHello.invoke(person);
}
}
优缺点: Link to heading
优点: Link to heading
- 动态性:运行时加载类,实现高度灵活的代码(如插件化开发)。
- 突破封装:可以访问私有属性和方法(慎用!破坏封装性)。
缺点: Link to heading
- 性能开销:反射操作比直接调用慢(JVM 无法优化反射代码)。
- 安全隐患:可以绕过权限检查,破坏封装性。
- 代码复杂度:反射代码可读性差,调试困难。
首先拿到class
使用构造器创建实例
调用方法使用invoke
私有属性SetAccessible
代理模式 Link to heading
使用代理对象来代替真实对象的访问
静态代理 Link to heading
编译时就将接口,实现类,代理类都变成了一个个实际的class文件
接口
public interface SmsService {
String send(String message);
}
实现类
package com.example.impl;
import com.example.SmsService;
public class SmsServiceImpl implements SmsService {
public String send(String message){
System.out.println("Send message : " + message);
return message;
}
}
代理类实现
package com.example.impl;
import com.example.SmsService;
public class SmsProxy implements SmsService {
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
@Override
public String send(String message) {
System.out.println("before send");
smsService.send(message);
System.out.println("after send");
return null;
}
}
测试
package com.example;
import com.example.impl.SmsProxy;
import com.example.impl.SmsServiceImpl;
public class Main {
public static void main(String[] args) {
SmsService smsService=new SmsServiceImpl();
SmsProxy smsProxy=new SmsProxy(smsService);
smsProxy.send("java");
}
}
动态代理 Link to heading
相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。
Java 中两种实现方式:
- JDK 动态代理(基于接口)
- CGLIB 动态代理(基于子类继承)
JDK动态代理 Link to heading
** 核心类和接口** Link to heading
类/接口 | 作用 |
---|---|
java.lang.reflect.Proxy |
生成代理类的工厂类,提供创建代理对象的静态方法 |
InvocationHandler |
调用处理器接口,由开发者实现,用于定义代理逻辑(如前置/后置增强) |
在 Java 动态代理机制中 InvocationHandler
接口和 Proxy
类是核心。
Proxy
类中使用频率最高的方法是:newProxyInstance()
,这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
}
这个方法一共有 3 个参数:
- loader :类加载器,用于加载代理对象。
- interfaces : 被代理类实现的一些接口;
- h : 实现了
InvocationHandler
接口的对象;
当我们的动态代理对象调用方法时,实际上会被转发到InvocationHandler
接口类的 invoke
方法来调用:
public interface InvocationHandler {
/**
* 当你使用代理对象调用方法的时候实际会调用到这个方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
invoke()
方法有下面三个参数:
- proxy :动态生成的代理类
- method : 与代理类对象调用的方法相对应
- args : 当前 method 方法的参数
具体使用方法 Link to heading
因为实际上调用的是InvocatinHandler接口的invoke方法,所以我们需要先实现Invocationhandler接口作为我们的动态代理类,然后再通过Proxy类中的newProxyInstance()方法,通过传入类加载器(加载代理对象),接口,动态代理类来生成代理对象
定义接口
public interface SmsService {
String send(String message);
}
实现接口
public class SmsServiceImpl implements SmsService {
public String send(String message){
System.out.println("Send message : " + message);
return message;
}
}
实现InvocationHandler接口,定义动态代理类
public class DebugInvocationHandler implements InvocationHandler {
//代理类中的真实对象
private Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before method"+method.getName());
Object result=method.invoke(target, args);
System.out.println("after method"+method.getName());
return result;
}
}
获取代理对象的工厂类
public class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new DebugInvocationHandler(target)
);
}
}
实际使用
public class Main {
public static void main(String[] args) {
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java e");
}
}
总结流程:
Proxy.newProxyInstance()通过反射动态生成代理对象,传递参数为classloader,interfaces和invocationHandler,代理类中的方法调用会被转发到invocationHandler.invoke方法。
GGLIB动态代理 Link to heading
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。
静态代理和动态代理的对比 Link to heading
- 灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
- JVM 层面:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。