文章目录
参考: Java 动态代理详解
一、静态代理
实现静态代理分三步:
- 主题抽象类或者接口
- 真实对象实现接口方法
- 代理对象实现接口,代理主题
例子
假设要对一个service进行前后通知,先创建UserService主题接口
- 编写UserService主题
package com.yzx.proxy;
public interface UserService {
void select();
void update();
}
- 编写真实对象实现主题接口
package com.yzx.proxy;
public class UserServiceImpl implements UserService{
@Override
public void select() {
System.out.println("查询数据");
}
@Override
public void update() {
System.out.println("更新数据");
}
}
- 编写代理类对UserService进行增强
package com.yzx.proxy;
public class UserServiceProxy implements UserService{
private UserService userService;
public UserServiceProxy(UserService userService){
this.userService = userService;
}
@Override
public void select() {
System.out.println("我要准备查询数据了");
userService.select();
System.out.println("查询数据完成");
}
@Override
public void update() {
System.out.println("我要准备更新数据了");
userService.update();
System.out.println("更新数据完成");
}
}
- 客户端测试
package com.yzx.proxy;
public class Client01 {
public static void main(String[] args) {
UserServiceProxy proxy = new UserServiceProxy(new UserServiceImpl());
proxy.select();
proxy.update();
}
}
输出
从结果可看出已经对真实对象进行了增强,并且对真实对象代码没有任何修改,这就是静态代理的一个好处。
但是仔细想想假如我要对多个类进行增强,使用静待代理解决的话有两种方式:
- 使用一个代理类、里面写多个接口,缺点是代理类会变的很庞大,里面接口过多。
- 使用多个代理类,每个代理类对一个类进行增强。缺点是代理类会很多。
并且当主题接口要添加方法,删除方法时,代理类,真实对象类都要修改,这样维护会很麻烦。
要解决上面的问题就要使用到动态代理。
二、动态代理
两种常见的动态代理技实现:
- 通过实现接口的方式,JDK动态代理
- 通过继承的方式,CGLIB动态代理
1. JDK动态代理
jdk动态代理主要涉及到两个类:java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
编写一个实现InvocationHandler调用逻辑处理器LogHandler类,提供日志增强功能;在LogHandler中维护一个目标对象;在invoke方法中编写调用方法的处理逻辑;
package com.yzx.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogHandler implements InvocationHandler {
Object object;
public LogHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(object, args);
after();
return result;
}
private void before(){
System.out.println("前置通知");
}
private void after(){
System.out.println("后置通知");
}
}
编写客户端,获取动态生成的代理类的对象须使用Proxy的newProxyInstance方法,具体步骤可见代码和注释
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 {
// 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名
// System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 1. 创建被代理的对象,UserService接口的实现类
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 2. 获取对应的 ClassLoader
ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
// 3. 获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService,
Class[] interfaces = userServiceImpl.getClass().getInterfaces();
// 4. 创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用
// 这里创建的是一个自定义的日志处理器,须传入实际的执行对象 userServiceImpl
InvocationHandler logHandler = new LogHandler(userServiceImpl);
/*
5.根据上面提供的信息,创建代理对象 在这个过程中,
a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
b.然后根据相应的字节码转换成对应的class,
c.然后调用newInstance()创建代理实例
*/
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler);
// 调用代理的方法
proxy.select();
proxy.update();
// 保存JDK动态代理生成的代理类,类名保存为 UserServiceProxy
// ProxyUtils.generateClassFile(userServiceImpl.getClass(), "UserServiceProxy");
}
}
输出结果