面向对象设计模式有个原则叫“开闭原则”,就是对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的,对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。
代理(Proxy)设计模式中的一种,就是为一个对象创建一个替身,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,而不用修改原始类的代码。
静态代理
接口
|
|
实现类
代理类
|
|
运行结果是:
从运行的结果看来,StaticProxy代理类既保留了RealSubject原始类的操作,又在其方法的前与后扩展了新的功能,有没有对RealSubject的源码进行修改。但这种方式也有明显的缺点:
- 代理类必须与目标对象实现一样的接口。
- 如果接口有变更,比如增加了方法,代理类又得重新维护。
- 对于多个接口的代理,可能需要创建多个代理类,代码量增加。
针对这些缺点,Java引入了动态代理。
动态代理
调用处理类
InvocationHandler
是代理实例的调用处理程序 实现的接口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke
方法。
运行结果:
和静态代理的区别是,proxySubject这个代理对象是动态创建的,执行这个代理对象的任何方法是,会被指派到ProxyHandler
类的invoke()
方法。
动态代理只需通过一个简单的类简单的方法,就能够代理多个接口的多个方法,有点类似是个门面,在执行被代理对象的方法时,所有方法最终都会执行这个代理类的invoke()方法。
ProxyHandler(Subject subject)这个构造方法注入了一个Subject对象,这个步骤不是必须,ProxyHandler和Subject,这两个类可以完全没有任何关联。下面Mybatis-Spring中使用动态代理的例子中,就没有任何关联。
InvocationHandler的Lambda表达式
InvocationHandler
是个函数式接口,可以直接用lambda表达式
Mybatis-Spring中使用动态代理
在用mybatis的数据操作时,调用方法之前自动获取SqlSession对象,在调用方法之后自动提交事务和关闭SqlSession对象。这里会用AOP技术,而在Mybatis-Spring就是用动态代理来实现AOP。
SqlSessionTemplate的应用
阅读SqlSessionTemplate的源码会发现,在这个类创建了一个动态代理类。
获取SqlSession对象->提交事务->关闭SqlSession对象,这些操作都是SqlSessionTemplate这个类直接处理,所以我们在Dao写数据操作时,直接调用SqlSessionTemplate的方法,而不用写事务相关的代码。
Cglib代理
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
目标对象类
Cglib代理工厂类
测试类