JDK动态代理
代理模式
核心定义:为目标对象提供一个代理对象,并由代理对象控制对目标对象的方法。代理对象可以在目标对象执行的前后,进行额外的增强处理,目标对象只需要专注自己的核心业务逻辑即可。
静态代理
简单理解
角色分析:
- 抽象角色:一般使用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后会做一些附属操作
- 客户:访问代理的人
以租客找中介向房东租房为例:
Rent.java
1 2 3 4 5
| package JDKproxy.staticProxy;
public interface Rent { public void rent(); }
|
租房这件事是需要中介和房东完成的,是一个抽象角色,所以需要用接口实现。
Host.java
1 2 3 4 5 6 7 8
| package JDKproxy.staticProxy;
public class Host implements Rent{ @Override public void rent() { System.out.println("房东出租房子"); } }
|
这是房东类,需要实现 Rent 接口。
RentProxy.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 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
1 2 3 4 5 6 7 8 9
| 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
1 2 3 4 5 6 7 8
| package JDKproxy.DynamicProxy;
public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
|
被代理的类:UserServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { method.invoke(userService,args); return null; } }
|
启动类:ProxyTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package JDKproxy.DynamicProxy;
import java.lang.reflect.Proxy;
public class ProxyTest { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); UserProxyInvocationHandler userProxyInvocationHandler = new UserProxyInvocationHandler(userService); JDKproxy.DynamicProxy.UserService userproxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), userProxyInvocationHandler); userproxy.add(); } }
|
注:动态代理代理的是接口。代理类是动态生成的,不是静态确定的。
我们生成的代理对象要强转成接口类型,一方面是为了能够调用接口声明的方法,另一方面是因为 java 是单继承机制,而我们生成的代理对象已经继承了 Proxy 类,所以在需要代理对象和真实对象的类型相同的要求下,使用接口类型是必须的。
总结
这篇博客主要是对之前的 JDK 动态代理进行一个整理,顺便理一下思路,目前对于动态代理的内部运行逻辑有了大致的认识,但是对于底层是怎么实现的并没有进行研究,不过目前还是 java 安全基础阶段,或许以后会去研究 Java 底层的一些原理。