一、介绍
JDK动态代理是代理模式的一种实现方式。因为它是基于接口来做代理的,所以也常被称为接口代理。在JDK动态代理中有两个重要的角色:
InvocationHandler(Interface)
用户实现这个接口,来编写代理类处理的核心逻辑。Proxy(Class)
用来创建一个代理实例,此时需要用到上面自定义的InvocationHandler。
二、功能
动态代理拥有代理模式的基本功能,如:调用真实方法的预处理、模块化通用功能。除此之外,还可以在运行时动态创建代理对象,无需针对每个接口编写代理逻辑(针对每个接口都编写对应的处理逻辑,叫做静态代理)。
三、使用步骤
- 编写目标接口、目标实现类
- 自定义InvocationHandler接口实现类,编写代理处理逻辑
我们来看看InvocationHandler这个接口的唯一一个方法invoke 方法
。
/**
* proxy:代理对象,一般用不到
* method:指代的是我们所要调用真实对象的某个方法的Method对象
* args:指代的是调用真实对象某个方法时接受的参数
**/
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
- 创建代理对象
创建代理对象时,需要关联一个InvocationHandler对象。这样当我们通过代理对象调用目标方法的时候,这个方法的调用就会被转发到InvocationHandler这个接口的 invoke 方法中。
我们一般把创建代理对象的方法,直接写在自定义的InvocationHandler实现类中。
- 使用代理调用目标接口中的方法
四、示例
需求:调用对象的每个方法时,在调用前、调用后、调用异常等都打印出一行日志。
对于这种需求,我们就需要把打印日志的功能模块化起来,不能在每个方法中都编写这种打印日志的代码,那样会把通用功能和业务功能混合在一起,后续不好维护。
1. 编写目标接口、目标类
目标接口(因为JDK动态代理是基于接口实现的,所以被代理的目标类,一定要实现一个接口。)
public interface UserService {
String getUserName(Long userId);
void say(String msg);
}
目标类
public class UserServiceImpl implements UserService {
@Override
public String getUserName(Long userId) {
return "user" + userId;
}
@Override
public void say(String msg) {
System.out.println("say " + msg);
}
}
2. 自定义InvocationHandler
public class LogInvocationHandler implements InvocationHandler {
/**
* 1. 目标类
*/
private final Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
/**
* 2. 代理逻辑
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用目标方法
Object result = null;
try {
//前置通知
System.out.println(method.getName() + "方法开始调用...");
result = method.invoke(target, args);
//返回通知, 可以访问到方法的返回值
System.out.println(method.getName() + "方法返回值:" + result);
} catch (Exception e) {
e.printStackTrace();
//异常通知, 可以访问到方法出现的异常
System.out.println(method.getName() + "方法调用出现了异常");
}
//后置通知.
System.out.println(method.getName() + "方法调用完成!");
return result;
}
/**
* 3. 获取目标类代理
*/
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
自定义的InvocationHandler实现类的三部曲(非常重要):
- 重写invoke方法,编写代理核心逻辑
- 保存目标对象(就是上面的target对象,在创建代理时会用到)
- 提供创建代理的方法
使用Proxy类
的newProxyInstance方法
实现,需要的三个参数:类加载器、目标类接口、代理逻辑处理类(自定义的InvocationHandler)
这样使用代理对象调用接口方法时,就可以转发到代理处理类的invoke方法中了。
3. 使用代理调用目标方法
@Test
public void dynamicProxyTest() {
UserService userService = new UserServiceImpl();
LogInvocationHandler logInvocationHandler = new LogInvocationHandler(userService);
UserService userServiceProxy = (UserService) logInvocationHandler.getProxy();
userServiceProxy.getUserName(1L);
System.out.println("=====================");
userServiceProxy.say("hello");
}
打印结果
可以看到,使用代理对象userServiceProxy来调用接口方法时,请求都转发到了代理处理类
的invoke方法中,在invoke方法中使用反射调用目标方法,最终转发到了target目标对象
中。
相关文章:CGLIB动态代理介绍