Bootstrap

代理模式-动态代理

一、代理模式

代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
代理模式角色分为 3种:
Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口;
RealSubject(真实主题角色):真正实现业务逻辑的类;
Proxy(代理主题角色):用来代理和封装真实主题;
代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。

如果根据字节码的创建时机来分类,可以分为静态代理动态代理:
所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运
行前就确定了。
而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代
理类的字节码文件 

 1.1静态代理

静态代理的工作原理如下:

  • 定义一个接口(或抽象类)作为目标接口,目标对象实现这个接口。
  • 创建一个代理类,实现目标接口,并持有目标对象的引用。
  • 在代理类中重写目标接口的方法,在方法调用前后执行需要的额外操作。
  • 客户端使用代理对象来访问目标对象。

代码例子: 

静态代理通过 UserServiceProxy实现,代理类同时也需要实现UserService 接口。

UserService:

public interface UserService {
	public void select();	
	public void update();
}

UserServiceImpl: 

//RealSubject 真实主题(真正的业务类)
public class UserServiceImpl implements UserService{
	@Override
	public void select() {
		System.out.println("查询selectById");		
	}
	@Override
	public void update() {
		System.out.println("更新update");
	}

}

UserServiceProxy:

//代理
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();		
	}
}

 测试:

public class Test {
	public static void main(String[] args) {
		UserServiceProxy userServiceProxy=new UserServiceProxy();
		userServiceProxy.select();
	}
}

通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。
虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也
会暴露出来。
当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:

  • 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
  • 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类

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

静态代理的特点:

需要手动编写代理类,工作量较大。

目标对象必须实现接口。

代理类和目标类的关系在编译时就确定了,无法动态改变

1.2动态代理

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

动态代理的工作原理如下:

定义一个接口,作为目标接口。

创建一个InvocationHandler接口的实现类,该类负责处理方法调用并执行额外的操作。

使用Proxy类的静态方法newProxyInstance()生成代理对象,同时指定目标对象和InvocationHandler。

客户端使用代理对象来访问目标对象的方法。

 代码例子如下:

OrderService :

public interface OrderService {
	public void create(double money,int uid);

}

OrderServiceImpl: 

public class OrderServiceImpl implements OrderService {
	@Override
	public void create(double money, int uid) {
		System.out.println("订单金额:¥"+money);
		System.out.println("订单id:"+uid);
	}
}

PerformanceInvocationHandler: 

//用于监测方法执行性能的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;
	}
}

Test:

public class Test {
	public static void main(String[] args) {
		//真实主题对象
		OrderServiceImpl realOrderService=new OrderServiceImpl();
		
		//获取类加载器
		ClassLoader classLoader=realOrderService.getClass().getClassLoader();
		
		//接口列表
		Class[] interfaces=realOrderService.getClass().getInterfaces();
		
		//创建InvocationHandler对象(动态代理的执行逻辑)
		PerformanceInvocationHandler p=new PerformanceInvocationHandler(realOrderService);
		
		//创建一个代理对象(动态代理对象)
		OrderService orderServiceProxy=(OrderService)Proxy.newProxyInstance(classLoader, interfaces, p);
		
		//调用方法
		orderServiceProxy.create(1345, 0001);		
	};
}

动态代理的特点:

不需要手动编写代理类,代理对象在运行时动态生成。

目标对象可以不实现接口,只需定义目标对象的共同接口。

代理对象和目标对象的关系在运行时确定,可以动态改变。

;