代理模式
Java的设计模式中有一项设计模式叫做代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
作用:
- 功能增强:在原有的功能上,增强了额外的功能。
- 控制访问:代理类不让你访问目标
实现代理的方式:
-
静态代理
-
动态代理
静态代理:
- 针对每个被代理对象写一个代理类,操作不够优雅
- 特点:
- 实现简单
- 容易理解。
- 缺点:
- 当目标类增加了,代理类可能也需要成倍的增加。代理类数量过多。
- 当接口中功能增加了或者修改了,会影响众多的实现类,厂家类,代理都需要修改。影响比较多。
例如:模拟一个用户购买u盘的行为。
用户:客户端类
商家: 代理,代理某个品牌的u盘。
厂家: 目标类。
三者的关系: 用户(客户端)—商家(代理)—厂家(目标)商家和厂家都是卖u盘的,他们完成的功能是一致的,都是卖u盘。
实现步骤:
- 建一个接口,定义卖u盘的方法,表示你的厂家和商家做的事情。
- 创建厂家类,实现1步骤的接口
- 创建商家,就是代理,也需要实现1步骤中的接口。
- 创建客户端类,调用商家的方法买一个u盘。
代理类完成的功能:
- 目标类中方法的调用
- 功能增强
/* UsbSell接口 */
// 表示功能的,厂家,商家都要完成的功能
public interface UsbSell {
// 定义方法,参数amount:表示一次购买的数量
// 返回值表示一个U盘的价格
float sell(int amount);
}
/* UsbKingFactory类 --- 厂家 */
import com.sproxy.service.UsbSell;
// 目标类:金士顿厂家,不接受用户单独购买
public class UsbKingFactory implements UsbSell {
@Override
public float sell(int amount) {
//一个128G的u盘是85元。
//后期根据amount ,可以实现不同的价格,例如10000个,单击是80 ,50000个75
return 85.0f;
}
}
/* TaoBao类 --- 商家 */
import com.sproxy.factory.UsbKingFactory;
import com.sproxy.service.UsbSell;
// Taobao是一个商家,代理金士顿U盘的销售
public class TaoBao implements UsbSell {
// 声明商家代表的厂家具体是谁
private UsbKingFactory factory = new UsbKingFactory();
@Override
// 实现卖U盘的功能
public float sell(int amount) {
// 向厂家发送订单,告诉厂家我卖了U盘,厂家发货
float price = factory.sell(amount); // 厂家的价格
// 商家加价,赚取代理费,增加价格
price += 25;
// 增加后的价格
return price;
}
}
/* 用户类 */
import com.sproxy.shangjia.TaoBao;
public class ShopMain {
public static void main(String[] args) {
// 创建代理的商家taobao对象
TaoBao taoBao = new TaoBao();
float price = taoBao.sell(1);
System.out.println("通过taobao商家,购买的U盘价格为:" + price);
}
}
动态代理:
dynamic 动态的
- 可以根据接口动态的生成代理类
- 动态代理是指代理类对象在程序运行时由
JVM
根据反射机制动态生成的。动态代理不需要定义代理类的.java
源文件。 - 动态代理其实就是
jdk
运行期间,动态创建class字节码并加载到JVM
。 - 动态代理的实现方式常用的有两种:使用
JDK
动态代理,与通过cglib
动态代理。
cglib
动态代理
CGLIB
(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code 生成类库,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP
的框架使用,例如Spring AOP
。
cglib
是第三方的工具库,创建代理对象。
cglib
的原理是继承,cglib
通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。
CGLIB
代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。所以,使用CGLIB
生成动态代理要求自标类必须能够被继承,即不能是 final的类。
对比:
- 使用
JDK
的Proxy
实现代理,要求目标类与代理类实现相同的接口。若目标类不存在接口,则无法使用该方式实现 - 对于无接口的类,要为其创建动态代理,就要使用
CGLIB
来实现。 cglib
经常被应用在框架中,例如Spring
,Hibernate
,mybatis
等。cglib
的代理效率高于jdk
。对于cglib
一般的开发中并不使用。
jdk
的动态代理
jdk
动态代理是基于Java 的反射机制实现的。使用 jdk
中接口和类实现代理对象的动态创建。
jdk
的动态要求目标对象必须实现同一接口,这是Java 设计上的要求。
从jdk1.3
以来,Java语言通过java.lang.reflect
包提供三个类支持代理模式Proxy
, Method
和 InovcationHandler
.
反射包 java.lang.reflect ,里面有三个类: InvocationHandler , Method,Proxy。
InvocationHandler接口
(调用处理器)
需要重写方法invoke ()
- invoke() : 表示代理对象要执行的功能代码。你的代理类要完成的功能就写在invoke ()方法中。
- 代理类完成的功能:
- 调用目标方法,执行目标方法的功能
- 功能增强,在目标方法调用时, 增加功能。
方法原型:
public object invoke (object proxy, Method method, Object[] args)
参数:
+ object proxy: jdk创建的代理对象,无需赋值。
+ Method method: 目标类中的方法,jdk提供method对象的
+ Object[] args: 目标类中方法的参数,jdk提 供的。
Method类
表示方法的,确切 的说就是目标类中的方法。
作用:
-
通过Method可以执行某个目标类的方法,Method. invoke() ;
-
method.invoke (目标对象,方法的参数)
反射中
method.invoke (helloService,"何鼎东") ;
-
说明:
-
method.invoke()
就是用来执行目标方法的,等同于静态代理中的//向厂家发送订单,告诉厂家,我买了u盘,厂家发货 float price = factory . sell (amount) ; //厂家的价格。
-
Proxy类
Proxy类: 核心的对象,创建代理对象。之前创建对象都是new类的构造方法()
现在我们是使用Proxy类的方法,代替new的使用。
方法:
- 静态方法
newProxyInstance()
- 作用是 : 创建代理对象,等同于静态代理中的
TaoBaotaoBao=newTaoBao();
方法原型:
public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数:
1.ClassLoaderloader 目标对象的类加载器,负贲向内存中加载对象的。使用反射获取对象的
2. Class<?>[] interfaces: 接口,目标对象实现的接口,也是反射获取的。
3. InvocationHandler h : 自己写的代理类要完成的功能。
返回值:就是代理对象
实现动态代理的步骤
- 创建接口,定义目标类要完成的功能.
- 创建目标类实现接口
- 创建
InvocationHandler
接口的实现类,在invoke方法中完成代理类的功能- 调用目标方法
- 增强功能
- 创建代理对象,使用Proxy类的静态方法。并把返回值转为接口类型。
public interface UsbSell {
float sell(int amount);
}
import com.dproxy.service.UsbSell;
// 目标类
public class UsbKingFactory implements UsbSell {
@Override
public float sell(int amount) {
// 目标方法
System.out.println("目标类中的sell目标方法执行了...");
return 85.0f;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 必须实现InvocationHandler接口来完成代理类的功能
public class MySellHandler implements InvocationHandler {
private Object target;
// 动态代理:目标对象是活动的,需要传入目标对象,给它创建代理。
public MySellHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res;
// 向厂家发送订单,告诉厂家我卖了U盘,厂家发货
// float price = factory.sell(amount); // 厂家的价格
res = method.invoke(target, args); // 执行目标方法
if (res!=null){
float price = (float)res;
// 商家加价,赚取代理费,增加价格
price += 25;
res = price;
}
// 增加后的价格
return res;
}
}
import com.dproxy.factory.UsbKingFactory;
import com.dproxy.handler.MySellHandler;
import com.dproxy.service.UsbSell;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MainShop {
public static void main(String[] args) {
// 创建代理对象,使用Proxy
// 1.创建目标对象
UsbSell factory = new UsbKingFactory();
// 2.创建InvocationHandler对象
InvocationHandler handler = new MySellHandler(factory);
// 3.创建代理对象
UsbSell porxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),
handler);
// 4.通过代理执行方法
float price = porxy.sell(1);
System.out.println("通过动态代理调用方法的:" + price);
}
}