目录
一、什么是代理模式
代理模式是一种结构型设计模式,它为目标对象提供一种代理,用来控制对目标对象的访问。代理对象在客户端和目标对象之间起到中介的作用。客户端通过代理对象间接地访问目标对象,而不需要直接与目标对象进行交互。可以在不改变目标对象内容的前提下,通过代理对象扩展目标对象的行为逻辑;
根据代理创建的时期,代理模式可以分为:
- 静态代理:以硬编码的方式,手动定义创建的代理类;
- 动态代理:代理类是在运行时动态创建的,在DK的核心类库中,可以使用 Java.lang.reflect.Proxy 类和 InvocationHandler 接来生成动态代理;
代理模式的应用场景:
- 在 myBatis 框架中,运行期间,为每个Mapper接口都会产生一个动态代理,完成映射 SQL 的数据库操作执行;
- 在 spring 框架中AOP 面向切面编程是基于动态代理实现;
二、静态代理
1、什么是静态代理
代理对象与目标对象一起实现相同的接口或者继承相同父类,由程序员创建或特定工具自动生成源代码,即在编译时就已经确定了接口,目标类,代理类等。在程序运行之前,代理类的 .class 文件就已经生成。你可以简单认为代理对象写死持有目标对象。
静态代理是指在编译时就已经确定了代理对象和目标对象的关系,代理类是通过手动编写代码来实现的。在静态代理中,代理类和目标类都实现相同的接口,代理类持有目标对象,并在方法调用前后进行额外的操作。
2、静态代理的工作原理
- 定义一个接口(或抽象类)作为目标接口,目标对象实现这个接口。
- 创建一个代理类,实现目标接口,并持有目标对象的引用。
- 在代理类中重写目标接口的方法,在方法调用前后执行需要的额外操作。
- 客户端使用代理对象来访问目标对象。
3、静态代理的特点
- 需要手动编写代理类,工作量较大。
- 目标对象必须实现接口。
- 代理类和目标类的关系在编译时就确定了,无法动态改变。
4、静态代理的应用场景
- 安全控制:代理类可以在调用目标方法前进行权限检查等安全控制。
- 远程调用:代理类可以封装网络通信相关的细节,使得调用远程方法就像调用本地方法一样简单。
- 性能监控:代理类可以收集方法的执行时间、调用次数等信息,用于性能监控和统计分析。
5、实例代码
首先定义一个业务接口:
public interface UserService {
public void select();
public void update();
}
然后定义一个该接口的实现类:
public class UserServiceImpl implements UserService {
@Override
public void select() {
System.out.println("查询 selectById");
}
@Override
public void update() {
System.out.println("更新 update");
}
}
然后手动创建一个静态代理类:
// 代理
public class UserServiceProxy implements UserService {
// 包含Subject真实的主题
private UserServiceImpl realUserService = new UserServiceImpl();
@Override
public void select() {
long begin = System.currentTimeMillis();
// 调用真正的业务逻辑
realUserService.select();
long end = System.currentTimeMillis();
System.out.println("select()执行耗时" + (end - begin) + "毫秒!");
}
@Override
public void update() {
realUserService.update();
}
}
首先这个代理类也实现了业务接口,所以它跟实现类一样要定义接口中的所有方法,它在调用完业务方法后又增加了一些自定义的逻辑。代理类中包含了一个 UserServiceImpl 的实例,realUserService,即被代理的真实业务类。代理类通过它来执行真实的业务逻辑。
然后重写select()方法,在这个方法中调用真正的业务逻辑,并且在前后添加自定义的逻辑计算耗时,最后打印出来。update()调用 realUserService 的 update 方法,update 方法的代理没有做任何增强,只是简单地传递调用。
最后在测试类中进行测试:
public interface Test01 {
public static void main(String[] args) {
UserServiceProxy userServiceProxy = new UserServiceProxy();
userServiceProxy.select();
userServiceProxy.update();
}
}
创建一个代理工厂对象,调用两个方法执行传递调用执行。
三、动态代理
1、什么是动态代理
动态代理是指在运行时生成代理对象,而无需手动编写代理类。Java的动态代理机制是基于反射实现的,通过使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现动态代理。
在动态代理中,代理类的创建和方法调用都是在运行时完成的。代理对象是在内存中动态创建的,并实现了目标对象的接口,同时持有目标对象的引用。在方法调用时,代理对象通过调用InvocationHandler接口中的方法来处理请求,可以在方法调用前后执行额外的操作。
2、动态代理的工作原理
- 定义一个接口,作为目标接口。
- 创建一个InvocationHandler接口的实现类,该类负责处理方法调用并执行额外的操作。
- 使用Proxy类的静态方法newProxyInstance()生成代理对象,同时指定目标对象和InvocationHandler。
- 客户端使用代理对象来访问目标对象的方法。
3、动态代理的特点
- 不需要手动编写代理类,代理对象在运行时动态生成。
- 目标对象可以不实现接口,只需定义目标对象的共同接口。
- 代理对象和目标对象的关系在运行时确定,可以动态改变。
4、动态代理的应用场景
- AOP(面向切面编程):动态代理可以在目标方法执行前后执行额外的操作,如权限检查、事务管理等。它是实现AOP的一种常见方式。
- 延迟加载:动态代理可以在目标方法被调用时才进行加载和初始化,实现延迟加载的效果。
- 日志记录:动态代理可以在目标方法执行前后记录日志信息。
5、代码示例
首先定义了一个业务接口:
public interface UserService {
public void select();
public void update();
public void createUser(int age,String name);
}
然后定义了一个该接口的实现类:
public class UserServiceImpl implements UserService {
@Override
public void select() {
System.out.println("查询 selectById");
}
@Override
public void update() {
System.out.println("更新 update");
}
@Override
public void createUser(int age, String name) {
System.out.println("学生姓名:" + name + ",学生年龄:" + age);
}
}
然后创建一个用于监听方法测试性能的Handler执行器:
// 用于监听方法测试性能的Handler执行器
public class PerformanceInvocationHandler implements InvocationHandler {
private Object real;
public PerformanceInvocationHandler(Object real) {
this.real = real;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long begin = System.currentTimeMillis();
// 真实业务对象当前的执行方法(基于反射的方式)
// 它会调用目标方法并返回执行结果。
Object returnValue = method.invoke(real, args);
long end = System.currentTimeMillis();
System.out.println("方法执行耗时" + (end - begin) + "毫秒!");
return returnValue;
}
}
首先 PerformanceInvocationHandler 实现了 InvocationHandler 接口,这个接口是用于实现动态代理的核心接口。动态代理允许在运行时生成代理类,并在代理类中执行一些额外的逻辑,例如性能监控、日志记录等。
PerformanceInvocationHandler 持有一个 Object 类型的引用 real,这是被代理的真实业务对象。也就是用于传入咱们刚才定义的实现类。通过构造方法将实际业务对象传递给 PerformanceInvocationHandler,以便在代理对象中调用真实的业务逻辑。
然后是invoke方法,invoke 方法是 InvocationHandler 接口的核心方法。每当代理对象调用某个方法时,都会触发 invoke 方法的执行。其中三个参数分别代表的是 proxy 是代理对象,method 是当前调用的方法对象,args 是传递给该方法的参数。
记录方法开始执行时的时间,然后通过使用反射调用 real 对象的实际方法(也就是刚才定义的实现类)。method.invoke(real, args) 会执行 real (实现类)对象中与 method 对应的方法,传递参数 args,并返回该方法的执行结果。相当于是调用real.method(args)。这是代理模式的核心部分,代理对象通过反射调用真实业务对象的方法。
最后记录执行后的时间然后输出,返回方法的执行结果。
最后定义一个测试类用于测试动态代理:
public class Test01 {
public static void main(String[] args) {
// 真实主题对象
UserServiceImpl realUserService = new UserServiceImpl();
// 获取类加载器
ClassLoader classLoader = realUserService.getClass().getClassLoader();
// 接口列表
Class[] interfaces = realUserService.getClass().getInterfaces();
// 创建InvocationHandler对象(动态代理的执行逻辑)
PerformanceInvocationHandler h = new PerformanceInvocationHandler(realUserService);
// 创建一个代理对象(动态代理对象)
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, h);
// 调用方法
userServiceProxy.createUser(18, "张睿泽");;
}
}
首先创建真实主题对象,然后创建类加载器(classLoader)用于加载动态代理类。动态代理类是在运行时生成的,使用的类加载器通常与真实业务对象相同。
interfaces 是真实业务对象实现的接口列表。动态代理需要这些接口来创建代理对象,因为代理对象会实现这些接口并代理其方法调用。
然后是创建一个InvocationHandler对象,PerformanceInvocationHandler 是一个 InvocationHandler 的实现类,用于定义代理对象的执行逻辑。h 是 PerformanceInvocationHandler 的实例,持有 realUserService,即真实业务对象的引用。当代理对象调用方法时,实际会调用 h.invoke() 方法,这样可以在调用真实方法前后添加一些逻辑(如性能监控)。
创建动态代理对象,Proxy.newProxyInstance()方法用于创建动态代理对象。这里面的参数分别为:
- classLoader:类加载器,用于加载代理类。
- interfaces:代理对象实现的接口列表。
- h:InvocationHandler 实现,用于定义代理对象的执行逻辑。
userServiceProxy 是动态代理对象,它实现了 UserService 接口,并代理 UserServiceImpl 的方法调用。
最后调用了createUser()有参方法,实际上会触发 PerformanceInvocationHandler 的 invoke 方法。在invoke方法中调用的是真实的UserServiceImpl的createUser方法。
至此,动态代理步骤实现完成。