代理模式是一种结构型设计模式,它为目标对象提供一种代理,用来控制对目标对象的访问。代理对象在客户端和目标对象之间起到中介的作用。客户端通过代理对象间接地访问目标对象,而不需要直接与目标对象进行交互,可以在不改变目标对象内容的前提下通过代理对象扩展目标对象的行为逻辑;
根据代理创建的时期,代理模式可以分为:
一、静态代理
1.静态代理定义
静态代理是以硬编码的方式,手动定义创建的代理类
2.实现方式
定义一个接口,包含目标类和代理类都需要实现的方法。
package com.ztt.Demo01;
public interface UserService {
public void select();
public void update();
}
创建目标类,实现该接口
package com.ztt.Demo01;
//RealSubject 真正主题(真正的业务类)
public class UserServiceImpl implements UserService{
@Override
public void select() {
System.out.println("查询selectById");
}
@Override
public void update() {
System.out.println("更新update");
}
}
创建代理类,实现同样的接口,并在其中调用目标类的方法。
package com.ztt.Demo01;
//代理
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();
}
}
测试
package com.ztt.Demo01;
public interface Test01 {
public static void main(String[] args) {
UserServiceProxy userServiceProxy=new UserServiceProxy();
userServiceProxy.select();
// userServiceProxy.update();
}
}
二、动态代理
1.动态代理定义
动态代理的代理类是在运行时动态创建的,在JDK的核心类库中,可以使用Java.lang.reflect.Proxy 类和 InvocationHandler 接口来生成动态代理
基于接口的动态代理(jdk自带 Proxy)JDK动态代理是基于接口实现的代理,只能代理实现了接口的类。
JDK动态代理是Java标准库中提供的一种代理方式,它可以在运行时动态生成一个代理对象,代理对象实现和原始类一样的接口,并将方法调用转发给被代理对象,同时还可以在方法调用前后执行额外的增强处理。JDK动态代理通过反射机制实现代理功能,其原理分为以下几个步骤:
- 创建实现InvocationHandler接口的代理类工厂:在调用Proxy类的静态方法newProxyInstance时,会动态生成一个代理类。该代理类实现了目标接口,并且持有一个InvocationHandler类型的引用。
- InvocationHandler接口:InvocationHandler是一个接口,它只有一个方法invoke。在代理对象的方法被调用时,JVM会自动调用代理类的invoke方法,并将被调用的方法名、参数等信息传递给该方法。
- 调用代理对象的方法:当代理对象的方法被调用时,JVM会自动调用代理类的invoke方法。在invoke方法中,可以根据需要执行各种逻辑,比如添加日志、性能统计、事务管理等。
- invoke方法调用:在invoke方法中,通过反射机制调用目标对象的方法,并返回方法的返回值。在调用目标对象的方法前后,可以执行额外的逻辑。
基于子类的动态代理(第三方 Cglib)CGlib方式是基于继承实现的代理,它不是指真实类需要继承某个父类,而是生成的代理类作为真实类的子类去代理父类,即代理类继承自真实类。这种方式不需实现接口,可以作为JDK代理方式的补充方案。
CGLIB(Code Generation Library)是一个基于ASM(Java字节码操作框架)实现的代码生成库,它可以在运行时动态生成目标类的子类作为代理类,并覆盖其中的方法来实现代理功能。与Java自带的JDK动态代理不同,CGlib动态代理可以代理没有实现接口的类。其原理分为以下几个步骤:
- 创建Enhancer对象:Enhancer是CGLIB库中用于动态生成子类的主要类。通过创建Enhancer对象并设置需要代理的目标类、拦截器等参数,可以生成一个代理类。
- 设置回调拦截器:在生成代理类时,需要指定拦截器。拦截器是实现代理逻辑的关键,它会在代理类的方法被调用时拦截调用,并执行相应的逻辑。在CGLIB中,拦截器需要实现MethodInterceptor接口。
- 创建代理对象:通过调用Enhancer对象的create方法,可以生成一个代理对象。代理对象会继承目标类的方法,并且在调用代理对象的方法时会先调用拦截器的intercept方法,再执行目标方法。
- 调用代理对象:通过调用代理对象的方法,会触发拦截器的intercept方法。在intercept方法中,可以根据需要执行各种逻辑,比如添加日志、性能统计、事务管理等。
2.实现方式
定义一个接口,包含目标类需要实现的方法。
package com.ztt.Demo02;
public interface OrderService {
public void create(double money,int uid);
}
创建目标类,实现该接口。
package com.ztt.Demo02;
public class OrderServiceImpl implements OrderService{
@Override
public void create(double money, int uid) {
System.out.println("订单创建create(方法");
System.out.println("订单金额:"+money+",订单编号:"+uid);
}
}
使用java.lang.reflect.Proxy
类和InvocationHandler
接口来创建动态代理。
package com.ztt.Demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//用于监测方法执行性能的Handler执行器
public class PerformanceInvocationHandler implements InvocationHandler {
private Object real;
public PerformanceInvocationHandler(Object real) {
this.real=real;
}
/**
* 参数 proxy:代理对象
* 参数 method:当前执行的方法对象
* 参数 args:参数列表
*/
@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(method.getName()+"select()执行耗时"+(end-begin)+"毫秒!");
return returnValue;
}
}
测试
package com.ztt.Demo02;
import java.lang.reflect.Proxy;
public class Test01 {
public static void main(String[] args) {
//真实主题对象
OrderServiceImpl realOrderService=new OrderServiceImpl();
//获取类加载器
ClassLoader classLoader = realOrderService.getClass().getClassLoader();
//接口列表
Class[] interfaces = realOrderService.getClass().getInterfaces();
//创建InvocationHandler对象(动态代理的执行逻辑)
PerformanceInvocationHandler h=new PerformanceInvocationHandler(realOrderService);
//创建一个代理对象(动态代理对象)
OrderService orderServiceProxy = (OrderService)Proxy.newProxyInstance(classLoader, interfaces, h);
//调用方法
orderServiceProxy.create(1234,001);
}
}
三、什么是代理模式?
- 代理模式是一种结构型设计模式,它为目标对象提供一种代理,用来控制对目标对象的访问。代理对象在客户端和目标对象之间起到中介的作用。客户端通过代理对象间接地访问目标对象,而不需要直接与目标对象进行交互。可以在不改变目标对象内容的前提下通过代理对象扩展目标对象的行为逻辑;
- 根据代理创建的时期,代理模式可以分为:
- 静态代理:以硬编码的方式,手动定义创建的代理类;
- 动态代理:代理类是在运行时动态创建的,在 DK的核心类库中,可以使用 Java.lang.reflect.Proxy类和 InvocationHandler 接口来生成动态代理:
- 代理模式的应用场景
- 在 MyBatis 框架中,运行期间,为每个Mapper接口都会产生一个动态代理,完成映射 SQL的数据库操作执行;
- 在 Spring 框架中,AOP 面向切面编程是基于动态代理实现:
四、静态代理与动态代理的区别
静态代理和动态代理的主要区别在于以下几个方面:
- 代理类的创建时期:静态代理的代理类在编译时期就已经确定,而动态代理的代理类是在运行时动态地生成的。
- 实现方式:静态代理通常由程序员手动编写代理类,而动态代理的实现方式通常是通过Java反射机制和字节码操作库(如CGLIB、Javassist等)实现的。
- 扩展性:静态代理的扩展性较差,如果要添加新的功能或者修改原有的功能,需要修改和重新编译代理类的代码。而动态代理的扩展性较好,可以根据需要添加新的功能或者修改原有的功能,提高了代码的灵活性和可维护性。
- 性能开销:静态代理的性能开销较小,因为代理类的代码在编译时就确定,没有额外的反射和字节码操作开销。而动态代理的性能开销较大,因为涉及到反射和字节码操作,可能会影响程序的性能。
- 适用场景:静态代理适用于一些功能相对固定、不需要动态改变代理类的行为的情况,例如在某些框架中用于拦截方法调用、实现AOP等功能。而动态代理适用于需要动态改变代理类的行为、扩展代理类的功能的情况,例如在某些框架中用于实现远程调用、事务管理、日志记录等功能。
综上所述,静态代理和动态代理的主要区别在于代理类的创建时期、实现方式、扩展性、性能开销和适用场景等方面。根据实际需求选择合适的代理方式可以更好地提高代码的灵活性和可维护性。