代理模式

简介

* 定义:
	* 是结构型设计模式;
	* 为目标对象提供代理:
		1.控制目标对象的访问;
		2.增强目标对象已有功能;


* 举例:
    * spring AopProxy采用代理创建bean;
    * windows快捷方式;
    * Controller是Service的代理, Service是DAO的代理;
    * 票贩子

* 分类:
    * 静态代理:
        * 由程序员创建代理对象;
    * 动态代理:
        * 系统在运行中动态生成代理对象;
        * 调用时调用动态生成的代理对象;

静态代理

代码
思路
* 代理持有目标的引用;
* 代理和目标实现相同接口;
* 使用接口接收代理对象;

* 以windows  打开快捷方式举例;
CODE
  • 接口
public interface IApp {
	
	public void run();

}
  • target
public class App implements IApp {

	public String name ;
	public App(String name) {
		// TODO Auto-generated constructor stub
		this.name = name;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(name+"find source url");
	}

}
  • proxy
public class ProxyForSimple {
	
	IApp app;
	public ProxyForSimple(IApp app) {
		// TODO Auto-generated constructor stub
		this.app = app;
	}
	
	public void run(){
		before();
		app.run();
	}

	public void before(){
		System.out.println("find source url");
	}
}
  • test
	public static void main(String[] args) {
		ProxyForSimple proxy = new ProxyForSimple(new App("Eclipse"));
		proxy.run();
	}

JDK动态代理

介绍
* Jdk Proxy特点:
	1. 采用字节重组方式生产代理类, 实现动态代理;
	2. 目标类必须实现接口;

* jdk Proxy生产代理类过程:
	1. 生产代理对象的方法: Proxy.newInstance(Class<?> [] classes,InovcationHandler handler);
		* classes: target实现的所有接口;
		* handler:
			* 持有增强方法invoke(Object proxy, Method method, Object[] args);
			* 持有被代理对象;
	2. 校验是否需要动态生成proxy的class
		* 根据classes和handler;
		* 如果不需要动态生成, 则不执行3,4,5步;
	3. 动态编写java代码;
		* 新类持有handler的引用, 且构造器赋值;
		* 新类实现所有cleasses;
			* 所有实现的方法体, 都是handler的增强方法invoke(Object proxy, Method method, Object[] args);
	4. 动态生成字节码文件;
	5. 动态加载字节码文件;
	6. 创建proxy对象:
		* 注入handler;

* 代理方法调用目标对象方法:
	* Object obj = method.invoke(this.obj, args);
	* 采用反射技术执行目标对象方法;
代码
思路
* 使用jdk动态代理, 模拟windows打开快捷方式;
* IAPP 和 APP类 , 继续使用静态代理中的代码;
code
  • proxy
public class ProxyForJdk implements InvocationHandler{

	Object obj;
	public Object getInstance(Object obj){
		this.obj = obj;
		Class clazz = obj.getClass();
		ClassLoader classLoader = clazz.getClassLoader();
		Class [] interfaces = clazz.getInterfaces();
		Object objr = Proxy.newProxyInstance(classLoader, interfaces, this);
		return objr;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		before();
		Object obj = method.invoke(this.obj, args);
		return obj;
	}

	public void before(){
		System.out.println("find source url");
	}
}
  • test
	public static void main(String[] args) {
		IApp app = (IApp) new ProxyForJdk().getInstance(new App("Eclipse"));
		app.run();
	}

CGLIB动态代理

介绍
* CGLIB Proy特点:
	* 通过动态继承目标对象的方式实现动态代理;
	* 被代理类不需要实现任何接口;

* CGLIB Proxy生产代理类过程:
	1. 生成代理对象方法: enhancer.create(clazz, args);
		* clazzes: target的类;
		* args : 构造器参数;
	2. 判断是否需要动态生成proxy的字节码文件;
		* enhancer.setCallback(Enhancer proxyEnhancer); //设置提供增强方法的enhancer;
		* cglib 通过proxyEnhancer和clazz判断是否需要生成字节码文件;
	3. 生成字节码文件:
		* 使用ASM框架生成2个字节码文件:
			* targetClass, proxyClass
			* 会为新targetClass和proxyClass中的方法编号;(为FASTClass做准备)
		* proxyClass重写target中的所有方法:
			* 所有重写的方法都执行 2步骤proxyEnhancer中 的intercept(Object obj, Method method, Object[] args, MethodProxy proxy)方法
				* 因为1步骤中enhancer.create(clazz, args)时注入了target构造参数, 所以proxyClass可以持有target对象;
		* 生成字节码文件较为复杂, 相对于JDK效率较低;
	4. 动态生成字节码文件;
	5. 动态加载字节码文件;
	6. 创建proxy和target对象;

* FASTCLASS技术:
	* 当CGLIB第一次调用代理方法时, 会产生一个FASTCLASS的字节码文件;
	* FASTCLASS文件中存储 proxy和target方法的对应关系;
		* 根据步骤3中, targetClass和proxyClass中方法的编号
* CGLIB中proxy调用target方法:
	* Object objr = proxy.invokeSuper(obj, args);
	* 使用FASTCLASS技术, 快速找到对应的target中的方法;
	* 执行方法效率高于JDK PROXY的反射技术;
代码
思路
* 使用CGLIB动态代理, 模拟windows打开快捷方式;
* IAPP 和 APP类 , 继续使用静态代理中的代码;
CODE
  • proxy
public class ProxyForCglib implements MethodInterceptor{

	public Object getInstance(App obj){
		Enhancer enhancer = new Enhancer();
		enhancer.setCallback(this);
		enhancer.setSuperclass(obj.getClass());
		

		Class [] clazzes = {String.class};
		String [] args = {obj.name};
		return enhancer.create(clazzes, args);
	}
	
	
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		// TODO Auto-generated method stub
		before();
		System.out.println(obj.getClass());  //obj是新生成的代理对象;
		Object objr = proxy.invokeSuper(obj, args);
		return objr;
	}

	public void before(){
		System.out.println("find source url");
	}
}
  • test
	public static void main(String[] args) {
		IApp app = (IApp) new ProxyForCglib().getInstance(new App("Eclipse"));
		app.run();
	}

CGLIB Proxy和JDK Proxy对比

* 实现方式不同:
    * JDK Proxy 采用实现接口的方式;
    * CGLIB Rpoxy 采用继承的方式;

* 生产字节码方式不同:
	* JDK 直接写java代码;
	* CGLIB采用ASM技术, 直接生成target和proxy的class, 并采用FASTCLASS技术关联target和proxy中的方法;

* 调用被代理对象的方法不同:
	* JDK 采用反射技术;
	* CGLIB 采用FASTClass技术;


* 实际应用场景:
	* 在实际应用场景中, proxy.class只生成一次,更多是被调用;
	* 因为CGLIB调用target效率高,所以CGLIB优势;
	* 比如 spring只在容器初始化时动态生成bean的代理class, 其他时候只是调用方法;

代理模式在spring中的应用

* Spring通过动态代理实现AOP:
	* AopProxy采用策略模式, 决定创建Bean的方式;
    * 当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
    * 当 Bean 没有实现接口时,Spring 选择 CGLib
    * Spring 可以通过配置强制使用 CGLib

* BeanFactory与ApplicationContext
    * ApplicationContext是BeanFactory的子接口,也被称为应用上下文。
    * ApplicationContext和容器一起初始化;
    * BeanFactory getBean():
		* 此时ApplicationContext已经初始化完成, 代理字节码文件已经生成;
		* 获取代理bean的方式有两种: 一种是单例, 一种是克隆;

静态代理和动态代理的本质区别

* 静态代理:
	* 只能手动完成代理操作;
	* 如果被目标类增加新方法, 代理类需要同步新增;
	* 违背开闭原则;

* 动态代理:
	* 采用动态生成字节码完成代理操作;
	* 不存在目标类增加方法时扩展限制;
	* 如果增加代理方法的逻辑扩展, 可结合策略模式, 只需要新增策略类即可完成, 无需修改代码;

代理模式优缺点

* 优点:
	* 代理对象控制目标对象的访问;
	* 代理对象一定成都是降低了耦合度;
	* 代理对象可以对目标对象增强;

* 缺点:
	* 代理模式会造成系统设计中类的数量增加。
	* 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
	* 增加了系统的复杂度。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值