代理模式---静态代理和动态代理

代理模式

代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式

代理模式角色分为 3 种:

Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口;

RealSubject(真实主题角色):真正实现业务逻辑的类;

Proxy(代理主题角色):用来代理和封装真实主题;

代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。

如果根据字节码的创建时机来分类,可以分为静态代理动态代理

静态代理

所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了

假设有UserService接口及其实现类UserServiceImpl,我们需要在不改变实现类代码的基础上,增加日志记录的功能。

//Subject

java 复制代码
public interface UserService {
	public void select();
	public void update();

}

//RealSubject 真实主题对象(真正的业务类)

java 复制代码
public class UserServiceImpl implements UserService{	
	public void select() {
		System.out.println("查询selectById");		
	}	
	public void update() {
		System.out.println("更新updateById");
		
	}
}

//Proxy 代理对象

java 复制代码
public class UserServiceProxy implements UserService{
	//创建真正的主题对象
    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() {
		// TODO Auto-generated method stub
		long begin=System.currentTimeMillis();
		realUserService.update();
		long end=System.currentTimeMillis();
		System.out.println("update的执行时间为:"+(end-begin)+"毫秒");
	}
}

//Test测试类

java 复制代码
public class Test {
	public static void main(String[] args) {
        //创建代理对象
        UserServiceProxy proxy=new UserServiceProxy();
        proxy.select();
		proxy.update();
    }
}

//运行结果:

查询selectById

select的执行时间为:0毫秒

更新updateById

update的执行时间为:0毫秒

通过静态代理,我们可以达成增强功能而不污染原代码,这是静态代理的优点。但是在一些场景复杂的时候,静态代理的缺点也会暴露出来:

1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式

只维护一个代理类 ,由这个代理类实现多个接口,但是这样就导致代理类过于庞大,导致代码可读性差。

新建多个代理类 ,每个目标对象对应一个代理类,但是这样会产生过多的代理类,难以管理。

2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护

动态代理

Java中两种常见的动态代理方式:JDK原生动态代理CGLIB动态代理(第三方开源类库)。

JDK动态代理

JDK动态代理主要涉及两个类:java.lang.reflect.Proxy 和java.lang.reflect.InvocationHandler。我们通过编写一个调用逻辑处理器 LogHandler 类案例来提供日志增强功能,并实现 InvocationHandler 接口;在 LogHandler 中维护一个目标对象,这个对象是被代理的对象(真实主题角色);在 invoke()方法中编写方法调用的逻辑处理。

//准备好两个真实主题对象及其接口:

//1.UserService和UserServiceImp

java 复制代码
public interface UserService {
	public void select();
	public void update();

}
java 复制代码
//RealSubject 真实主题对象(真正的业务类)
public class UserServiceImpl implements UserService{	
	public void select() {
		System.out.println("查询selectById");		
	}	
	public void update() {
		System.out.println("更新updateById");
		
	}
}

//2.OrderService 和OrderServiceImp

java 复制代码
public interface OrderService {
	public void creat(int money,int uid);

}
java 复制代码
public class OrderServiceImpl implements OrderService{
	@Override
	public void creat(int money, int uid) {
		System.out.printf("商品%d的价格是:%d",uid,money);
		System.out.println();
	}

}

//InvocationHandler 接口的实现类

java 复制代码
//用于检测方法运行时间的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();
		
		//真实业务对象当前的执行方法(基于反射)
        //相当于调用了creat()、select()或update()方法
        //传入的真实主题对象是谁,就调用谁的方法
		Object returnValue=method.invoke(real, args);
		
        //结束时间
		long end=System.currentTimeMillis();
		
		System.out.println("方法耗时:"+(end-begin)+"毫秒");
		
		return returnValue;
	}

}

//实现OrderService代理的测试类

java 复制代码
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.creat(1234, 1);
	
	}

}

//运行结果:

商品1的价格是:1234

方法耗时:33毫秒

//实现UserService代理的测试类

java 复制代码
public class Test02 {
	public static void main(String[] args) {
		//创建真实主题对象
		UserServiceImpl realUserService=new UserServiceImpl();
		
		//获取类加载器
		ClassLoader loader=realUserService.getClass().getClassLoader();
		
		//获取接口列表
		Class[] interfaces=realUserService.getClass().getInterfaces();
		
		//Handler
		PerformanceInvocationHandler h=new PerformanceInvocationHandler(realUserService);
		
		//创建动态代理对象
		UserService userServiceProxy=(UserService)Proxy.newProxyInstance(loader, interfaces, h);
		
		//调用方法
		userServiceProxy.update();
	}

}

//运行结果:

更新updateById

方法耗时:0毫秒

通过动态代理,我们不再需要创建不同代理类来实现不同的逻辑方法。动态代理是通过Proxy创建代理对象,然后将接口方法"代理"给InvocationHandler完成的。

相关推荐
9号达人5 小时前
为什么你应该在 MQ 里用多个消费者,而不是一个
java·后端·架构
焦糖玛奇朵婷6 小时前
健身房预约小程序开发、设计
java·大数据·服务器·前端·小程序
小新同学^O^6 小时前
简单学习 --> TCP协议
java·网络·tcp
月落归舟6 小时前
深入理解Java适配器模式,彻底搞懂设计思想
java·开发语言·适配器模式
Mr_pyx6 小时前
【LeetHOT100】二叉树的中序遍历——Java多解法详解
java·开发语言·深度优先
jay神6 小时前
基于SpringBoot的宠物生命周期信息管理系统
java·数据库·spring boot·后端·web开发·宠物·管理系统
万亿少女的梦1686 小时前
基于SpringBoot的在线考试管理系统设计与实现
java·spring boot·后端
一勺菠萝丶7 小时前
如何在 Linux 服务器上使用 Speedtest 官方 CLI 测试带宽(小白教程)
java·服务器·前端
范什么特西7 小时前
第一个Mybatis
java·开发语言·mybatis
下次再写7 小时前
【Redis实战】深入理解Redis缓存策略:从原理到Spring Boot实践
java·spring boot·redis·缓存穿透·缓存击穿·分布式缓存·缓存策略