1. 概念
所谓 Java 的动态代理,我个人的理解其实就是通过反射机制,创建一个接口的临时实现类,并返回一个实例化对象。
注意:
-
只能代理接口:JDK动态代理只能为接口创建代理,不能为具体类创建代理
-
反射调用:每次方法调用都会通过反射机制转发到 InvocationHandler
-
临时类:代理类是运行时生成的,存在于内存中,不会持久化到磁盘
通俗来讲,比如坤坤是一位大明星,要开演唱会,坤坤只负责唱歌和跳舞,那么布置舞台这些事情,肯定不是坤坤自己来做,所以就需要代理人来做这些。
于是我们可以这样做:
package top.mygld.demo.test;
public class KunKun {
public String sing(String name){
System.out.println("坤坤唱了:" + name);
return "坤坤唱完了";
}
public void dance(String name){
System.out.println("坤坤跳了:" + name + "舞蹈!");
}
}
package top.mygld.demo.test;
public class ProxyPeople {
KunKun kunKun = new KunKun();
public String sing(String name){
System.out.println("代理人布置场地。" );
System.out.println("代理人布置场地完成。" );
return kunKun.sing(name);
}
public void dance(String name){
System.out.println("坤坤跳了:" + name + "舞蹈!");
kunKun.dance(name);
}
}
然后我们只需要在主类中运行:
ProxyPeople proxyPeople = new ProxyPeople();
proxyPeople.sing("只因你太美");
proxyPeople.dance("芭蕾舞");
即可。
但是这样写显然不够优雅,于是你想到了用多态的方法:
package top.mygld.demo.test;
public interface People {
String sing(String name);
void dance(String name);
}
package top.mygld.demo.test;
public class KunKun implements People{
@Override
public String sing(String name){
System.out.println("坤坤唱了:" + name);
return "坤坤唱完了";
}
@Override
public void dance(String name){
System.out.println("坤坤跳了:" + name + "舞蹈!");
}
}
package top.mygld.demo.test;
public class ProxyPeople implements People{
KunKun kunKun = new KunKun();
@Override
public String sing(String name){
System.out.println("代理人布置场地。" );
System.out.println("代理人布置场地完成。" );
return kunKun.sing(name);
}
@Override
public void dance(String name){
System.out.println("代理人布置场地。" );
System.out.println("代理人布置场地完成。" );
kunKun.dance(name);
}
}
这样就优雅了许多,但是还不够,这样的话我们需要单独去写一个实现类 ProxyPeople
,显得有些多余,因为我们的语句比较简单,那有没有不用去单独写一个实现类的方法?动态代理就是做这个的。
2. Proxy 类
在 java.lang.reflect
包下,有一个 Proxy
类,它有一个 newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
方法,可以临时生成实现类并返回一个对象。
其中,newProxyInstance
第一个参数是一个类加载器,第二个是一个接口的 Class 数组,第三个参数是用来指定代理对象要做什么事情,例如:
package top.mygld.demo.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
public static People createProxyPeople(KunKun kunKun) {
return (People) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
new Class[]{People.class},
new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("代理人布置场地。" );
System.out.println("代理人布置场地完成。" );
if (method.getName().equals("sing")) {
return method.invoke(kunKun, objects);
}
else{
method.invoke(kunKun,objects);
return null;
}
}
});
}
}
然后 KunKun
的类不需要改动,直接在主类中运行:
People proxyPeople = ProxyUtil.createProxyPeople(new KunKun());
System.out.println(proxyPeople.sing("只因你太美"));
proxyPeople.dance("芭蕾舞");
这里做一个补充,为什么 newProxyInstance
第二个参数是一个数组呢?因为一个代理对象可以同时实现多个接口,例如:
假设设你有多个接口:
interface UserService {
void saveUser(String name);
}
interface LogService {
void log(String message);
}
interface CacheService {
void cache(String key, Object value);
}
你可以创建一个代理对象,同时实现这三个接口:
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用方法: " + method.getName());
// 根据方法名或接口类型进行不同处理
if (method.getName().equals("saveUser")) {
System.out.println("保存用户: " + args[0]);
} else if (method.getName().equals("log")) {
System.out.println("记录日志: " + args[0]);
} else if (method.getName().equals("cache")) {
System.out.println("缓存数据: " + args[0] + " -> " + args[1]);
}
return null;
}
};
// 同时实现多个接口
Object proxy = Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class, LogService.class, CacheService.class}, // 多个接口
handler
);
// 可以转换成任意一个接口使用
UserService userService = (UserService) proxy;
LogService logService = (LogService) proxy;
CacheService cacheService = (CacheService) proxy;
userService.saveUser("张三");
logService.log("操作完成");
cacheService.cache("user:1", "张三");
当你传入多个接口时,JVM会生成一个同时实现所有接口的代理类:
// 动态生成的代理类会实现所有接口
public final class $Proxy0 extends Proxy
implements UserService, LogService, CacheService {
// 实现UserService的方法
public void saveUser(String name) throws Throwable {
super.h.invoke(this, saveUserMethod, new Object[]{name});
}
// 实现LogService的方法
public void log(String message) throws Throwable {
super.h.invoke(this, logMethod, new Object[]{message});
}
// 实现CacheService的方法
public void cache(String key, Object value) throws Throwable {
super.h.invoke(this, cacheMethod, new Object[]{key, value});
}
}
3. 实际使用场景
这种设计在框架中很常见,比如:
- Spring AOP:代理对象需要实现原始接口 + 额外的增强接口
- 事务管理:业务接口 + 事务管理接口
- 缓存代理:业务接口 + 缓存管理接口
所以数组设计是为了支持多接口实现的灵活性,即使你只传入一个接口,也要用数组的形式 new Class[]{YourInterface.class}
。
这样就可以了,以上就是 Java 动态代理的相关知识点。