JDK动态代理 #
代理模式 #
核心定义:为目标对象提供一个代理对象,并由代理对象控制对目标对象的方法。代理对象可以在目标对象执行的前后,进行额外的增强处理,目标对象只需要专注自己的核心业务逻辑即可。
静态代理 #
简单理解 #
角色分析:
- 抽象角色:一般使用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后会做一些附属操作
- 客户:访问代理的人
以租客找中介向房东租房为例:
Rent.java
package JDKproxy.staticProxy;
public interface Rent {
public void rent();
}
租房这件事是需要中介和房东完成的,是一个抽象角色,所以需要用接口实现。
Host.java
package JDKproxy.staticProxy;
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
这是房东类,需要实现 Rent 接口。
RentProxy.java
package JDKproxy.staticProxy;
public class RentProxy implements Rent{
private Host host;
public RentProxy(Host host) {
this.host = host;
}
public RentProxy() {
}
@Override
public void rent() {
host.rent();
seeHouse();
fare();
contract();
}
public void seeHouse(){
System.out.println("中介看房");
}
public void fare(){
System.out.println("收中介费");
}
public void contract(){
System.out.println("签合同");
}
}
这是中介类,也就是代理,中介代表房东出租房子,同时也会提供一些额外的服务。
Client.java
package JDKproxy.staticProxy;
public class Client {
public static void main(String[] args) {
Host host = new Host();
RentProxy rentProxy = new RentProxy(host);
rentProxy.rent();
}
}
这是一个启动类,也就是上面讲的客户,客户的需求很简单,就是找到中介(代理),然后租房。途中还会有一些其他的操作,这些就都是由中介(代理)完成的。
静态代理的优点:
- 逻辑清晰,容易理解,不需要反射,性能较好。
- 让真实角色更加纯粹,不需要去关注公共的事情。
- 公共的业务由代理完成,实现分工。
- 公共业务拓展时更加集中且方便。
缺点:
- 一个真实类对应一个代理,一旦类多了,代码量直接翻倍,效率太低。
我们需要静态代理的优点同时要减少代码量,就出现了动态代理。
动态代理 #
静态代理的代理类是我们手写的,而动态代理的代理类是在运行时由 JVM 动态生成的,无需手动创建.java文件。它是 JDK 原生支持的,前提是目标对象必须实现接口。
动态代理的核心是一个接口和一个代理类:
InvocationHandler接口:额外的逻辑写在这里,所有代理方法的调用,最终都会进入invoke方法中。Proxy类:用它来动态生成并加载代理类。
下面先给一个样例,然后根据样例来学习动态代理。
样例 #
接口:UserService.java
package JDKproxy.DynamicProxy;
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
被代理的类:UserServiceImpl.java
package JDKproxy.DynamicProxy;
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("添加");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("更新");
}
@Override
public void query() {
System.out.println("查询");
}
}
调用处理器:UserProxyInvocationHandler.java
package JDKproxy.DynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class UserProxyInvocationHandler implements InvocationHandler {
private UserServiceImpl userService;
public UserProxyInvocationHandler(UserServiceImpl userService) {
this.userService = userService;
}
@Override //这里的参数都是 JVM 自动获取的,不需要手动设置
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(userService,args);
return null;
}
}
启动类:ProxyTest.java
package JDKproxy.DynamicProxy;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
//创建一个真实对象
UserServiceImpl userService = new UserServiceImpl();
//创建调用处理器,以后所有对代理方法的调用都会经过这个处理器的invoke方法
UserProxyInvocationHandler userProxyInvocationHandler = new UserProxyInvocationHandler(userService);
//动态生成代理对象
//JVM 底层会做这些事:
//a) 在内存中动态拼出一个 .class 字节码,这个类实现了 UserService 接口
//b) 这个类长这样(伪代码):
// class $Proxy0 implements UserService {
// InvocationHandler h;
// public final void add() {
// h.invoke(this, m3, null); // m3 就是 add 的 Method 对象
// }
// public final void delete() {
// h.invoke(this, m4, null);
// }
// ... update() / query() 同理
// }
// c) 用 classLoader 把这个类加载到 JVM
// d) 创建该类的实例,把 userProxyInvocationHandler 赋给它的 h 字段
JDKproxy.DynamicProxy.UserService userproxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), userProxyInvocationHandler);
// 步骤4: 调用代理对象的 add() 方法
//
// userproxy 是 $Proxy0 的实例,调用 add() 时:
// userproxy.add()
// → $Proxy0.add() 被 JVM 调用
// → 内部执行 this.h.invoke(this, method, args)
// → 进入 UserProxyInvocationHandler.invoke()
// → invoke 里用反射: method.invoke(真实对象, args)
// → 真实对象 UserServiceImpl.add() 执行
// → 打印 "添加"
userproxy.add();
}
}
注:动态代理代理的是接口。代理类是动态生成的,不是静态确定的。
我们生成的代理对象要强转成接口类型,一方面是为了能够调用接口声明的方法,另一方面是因为 java 是单继承机制,而我们生成的代理对象已经继承了 Proxy 类,所以在需要代理对象和真实对象的类型相同的要求下,使用接口类型是必须的。
总结 #
这篇博客主要是对之前的 JDK 动态代理进行一个整理,顺便理一下思路,目前对于动态代理的内部运行逻辑有了大致的认识,但是对于底层是怎么实现的并没有进行研究,不过目前还是 java 安全基础阶段,或许以后会去研究 Java 底层的一些原理。