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

代理模式

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

代理模式角色分为 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完成的。

相关推荐
Yan_ks36 分钟前
JAVA面向对象——对象和类的基本语法
java·开发语言
Paddy哥1 小时前
jsmpeg+java+ffmpeg 调用摄像头RTSP流播放
java·开发语言·ffmpeg
oioihoii1 小时前
C++23 新增扁平化关联容器详解
java·开发语言·c++23
IT从业者张某某1 小时前
信奥赛-刷题笔记-栈篇-T2-P1165日志分析0519
android·java·笔记
asom222 小时前
Java 04 API
java·开发语言
qq_214225872 小时前
学生成绩管理系统Java实战(Spring Boot+MyBatis Plus)
java·spring boot·其他·mybatis
吗喽对你问好2 小时前
23种设计模式解释+记忆
java·设计模式
188_djh2 小时前
# 终端执行 java -jar example.jar 时(example.jar为项目jar包)报错:“没有主清单属性” 的解决方法
java·pycharm·jar·cmd·没有主清单属性·java -jar
CodeClimb2 小时前
【华为OD-B卷-打印文件 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
C182981825752 小时前
Rocketmq刷盘机制和复制机制区别及关系
java·rocketmq·java-rocketmq