java动态代理
为什么要使用动态代理?
在设计模式中有一个非常常用的模式:代理模式。学术一些来讲,就是为某些对象的某种行为提供一个代理对象,并由代理对象完全控制该行为的实际执行。
通俗来说,就是我想点份外卖,但是手机没电了,于是我让同学用他手机帮我点外卖。在这个过程中,其实就是我同学(代理对象)帮我(被代理的对象)代理了点外卖(被代理的行为),在这个过程中,同学可以完全控制点外卖的店铺、使用的APP,甚至把外卖直接吃了都行(对行为的完全控制)。
因此总结一下代理的4个要素:
代理对象
被代理的行为
被代理的对象
行为的完全控制
如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:
- 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
- 而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件
我们首先了解一下静态代理
静态代理
我们先通过实例来学习静态代理,然后理解静态代理的缺点,再来学习本文的主角:动态代理
编写一个接口 UserService ,以及该接口的一个实现类 UserServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface UserService { public void select(); public void update(); }
public class UserServiceImpl implements UserService { public void select() { System.out.println("查询 selectById"); } public void update() { System.out.println("更新 update"); } }
|
我们将通过静态代理对 UserServiceImpl 进行功能增强,在调用select
和update
之前记录一些日志。写一个代理类 UserServiceProxy,代理类需要实现 UserService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class UserServiceProxy implements UserService { private UserService target;
public UserServiceProxy(UserService target) { this.target = target; } public void select() { before(); target.select(); after(); } public void update() { before(); target.update(); after(); }
private void before() { System.out.println(String.format("log start time [%s] ", new Date())); } private void after() { System.out.println(String.format("log end time [%s] ", new Date())); } }
|
再编写一个客户端类用于测试代码
1 2 3 4 5 6 7 8 9
| public class Client1 { public static void main(String[] args) { UserService userServiceImpl = new UserServiceImpl(); UserService proxy = new UserServiceProxy(userServiceImpl);
proxy.select(); proxy.update(); } }
|
运行结果
静态代理就是的所有逻辑已经在代码表示出来了,原理较为简单
但是,静态代理有一些缺点:
1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
- 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
- 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
由于以上问题,所以就产生了动态代理
动态代理
如何使用动态代理?
创建java动态代理需要使用如下类
1 2
| java.lang.reflect.Proxy java.lang.reflect.InvocationHandler
|
我们仍然通过案例来学习编写一个调用逻辑处理器 LogHandler 类,提供日志增强功能,并实现 InvocationHandler 接口;在 LogHandler 中维护一个目标对象,这个对象是被代理的对象(真实主题角色);在 invoke
方法中编写方法调用的逻辑处理。
代理类
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
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Date;
public class LogHandler implements InvocationHandler { Object target;
public LogHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(target, args); after(); return result; } private void before() { System.out.println(String.format("log start time [%s] ", new Date())); } private void after() { System.out.println(String.format("log end time [%s] ", new Date())); } }
|
客户端
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
| import proxy.UserService; import proxy.UserServiceImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;
public class Client2 { public static void main(String[] args) throws IllegalAccessException, InstantiationException { UserServiceImpl userServiceImpl = new UserServiceImpl(); ClassLoader classLoader = userServiceImpl.getClass().getClassLoader(); Class[] interfaces = userServiceImpl.getClass().getInterfaces(); InvocationHandler logHandler = new LogHandler(userServiceImpl);
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler); proxy.select(); proxy.update(); } }
|
运行结果
对于newProxyInstance的参数解释:
1 2 3
| public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
|
ClassLoader类型的loader:被代理的类的加载器,可以认为对应4要素中的被代理的对象。
Class数组的interfaces:被代理的接口,这里其实对应的就是4要素中的被代理的行为,可以注意到,这里需要传入的是接口而不是某个具体的类,因此表示行为。
InvocationHandler接口的h:代理的具体行为,对应的是4要素中的行为的完全控制,当然也是java动态代理的核心。
最后返回的对象Object对应的是4要素中的代理对象。