1. 什么是代理模式??作用是什么??有哪几种代理模式??
1.1 什么是代理模式
代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。
换句话说,使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑。
例如1: 有 A,B,C 三个类, A 原来可以调用 C 类的方法, 现在因为某种原因 C 类不允许A 类调用其方法,但 B 类可以调用 C 类的方法。A 类通过 B 类调用 C 类的方法。这里 B 是 C的代理。 A 通过代理 B 访问 C.
例如2:在实际开发过程中,有A和C两个类,我们通过A类去调用C类中的某个功能,但是现在C类中的功能不够用了(即,需要在C类功能的基础上添加部分功能),在这种状态中,我们所想到的是更改C类的功能,但在实际开发中不允许更改C类。因此,可以通过类B去对C类的功能进行扩展。即功能增强。
注意:在代理模式中主要包含三种类关系:
- 客户类
- 代理类
- 目标类
代理类完成的功能:
- 目标类中方法的调用
- 功能增强
1.2 代理模式 的作用
- 控制访问:控制客户端 访问目标类
- 增强功能:增强目标类的功能
1.3 代理模式分类
- 静态代理
- 动态代理:分为JDK动态代理和Cglib代理
2 静态代理
2.1 什么是静态代理
- 代理类是自己手工实现的,自己创建一个java类,表示代理类。
- 同时你所要代理的目标类是确定的。
特点:
- 1)实现简单
- 2)容易理解。
缺点: 当你的项目中,目标类和代理类很多时候,有以下的缺点:
- 代码复杂,难于管理
代理类和目标类实现了相同的接口,每个代理都需要实现目标类的方法,这样就出现了大量的代码重复。如果接口增加一个方法,除了所有目标类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
- 代理类依赖目标类,代理类过多
代理类只服务于一种类型的目标类,如果要服务多个类型。势必要为每一种目标类都进行代理,静态代理在程序规模稍大时就无法胜任了,代理类数量过多。
2.2 静态代理实例
-
需求:
用户需要购买 u 盘,u 盘厂家不单独接待零散购买,厂家规定一次最少购买 1000 个以上,用户可以通过淘宝的代理商,或者微商哪里进行购买。 淘宝上的商品,微商都是 u 盘工厂的代理商, 他们代理对 u 盘的销售业 务。 用户购买-------代理商(淘宝,微商)----- u 厂家(金士顿,闪迪等不同的厂家)
-
分析:
用户是客户端类 商家:代理,代理某个品牌的u盘。 厂家:目标类。 三者的关系: 用户(客户端)---商家(代理)---厂家(目标) 商家和厂家都是卖u盘的,他们完成的功能是一致的,都是卖u盘。
实现步骤:
- 创建一个接口,定义卖u盘的方法, 表示你的厂家和商家做的事情。
- 创建厂家类,实现1步骤的接口
- 创建商家,就是代理,也需要实现1步骤中的接口。
- 创建客户端类,调用商家的方法买一个u盘。
第一步:定义业务接口 UsbSell(目标接口),其中含有抽象方法 sell(int amount), sell 是目标方法。
package com.bipowernode.service;
//表示功能,厂家和商家都要完成的功能
public interface UsbSell {
//定义方法
/**
* @param amount :表示一次购买的数量
* @return 表示一个u盘的价格
*/
float sell(int amount);
}
第二步: 目标类 UsbKingFactory(金士顿 u 盘),该类实现了业务接口。
package com.bipowernode.factory;
import com.bipowernode.service.UsbSell;
public class UsbKingFactory implements UsbSell {
@Override
public float sell(int amount) {
//一个128G的U盘是85元
return 85.0f;
}
}
第三步:TaoBao 就是一个代理类, 代理厂家销售 u 盘
package com.bipowernode.shangjia;
import com.bipowernode.factory.UsbKingFactory;
import com.bipowernode.service.UsbSell;
public class TaoBao implements UsbSell {
//声明 商家代理的厂家具体是谁
private UsbKingFactory factory = new UsbKingFactory();
@Override
//实现销售u盘功能
public float sell(int amount) {
//向厂家发送订单,告诉厂家,请求发货。
float price = factory.sell(amount);
//商家需要加价,也就是代理要增加价格
price = price + 25; //增强功能,代理类在完成目标类方法调用后增强了功能。
//在目标类方法调用之后,商家所做的其他功能都是增强的意思。
System.out.println("淘宝商家,返给你一个优惠券,或者红包");
//增加后的价格
return price;
}
}
第四步:创建客户端类,调用商家的方法买一个u盘。
package com.bipowernode;
import com.bipowernode.shangjia.TaoBao;
public class ShopMain {
public static void main(String[] args) {
//创建代理的商家淘宝对象
TaoBao taoBao = new TaoBao();
float price = taoBao.sell(1);
System.out.println("通过淘宝的商家,购买u盘单价:" + price);
}
}
3 动态代理
3.1 什么是动态代理
在静态代理中目标类很多时候,可以使用动态代理,避免静态代理的缺点。
动态代理是指代理类对象在程序运行时由 JVM 根据反射机制动态生成的。动态代理不需要定义代理类的.java 源文件。
动态代理: 在程序执行过程中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类。
换句话说: 动态代理是一种创建java对象的能力,让你不用创建TaoBao类,就能创建代理类对象。
动态代理的分类:
- Jdk动态代理: 使用java反射包中的类和接口实现动态代理的功能。反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy.
- cglib动态代理: cglib的原理是继承, cglib通过继承目标类,创建它的子类,在子类中 重写父类中同名的方法, 实现功能的修改。因为cglib是继承,重写方法,所以要求目标类不能是final的, 方法也不能是final的。 cglib的要求目标类比较宽松, 只要能继承就可以了。cglib在很多的框架中使用,比如 mybatis ,spring框架中都有使用。
3.2 JDK动态代理
jdk 动态代理是基于 Java 的反射机制实现的。使用 jdk 中接口和类实现代理对象的动态创建。
Jdk 的动态要求目标对象必须实现接口,这是 java 设计上的要求。
从 jdk1.3 以来,java 语言通过 java.lang.reflect 包提供三个类支持代理模式 Proxy, Method和 InovcationHandler。
-
InvocationHandler 接口
InvocationHandler 接口叫做调用处理器,负责完调用目标方法,并增强功能。 通 过 代 理 对 象 执 行 目 标 接 口 中 的 方 法 , 会 把 方 法 的 调 用 分 派 给 调 用 处 理 器 (InvocationHandler)的实现类,执行实现类中的 invoke()方法,我们需要把功能代理写在 invoke()方法中 。 public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } public Object invoke ( Object proxy, Method method, Object[] args) proxy:代表生成的代理对象 method:代表目标方法 args:代表目标方法的参数 第一个参数 proxy 是 jdk 在运行时赋值的,在方法中直接使用, 第二个参数后面介绍, 第三个参数是方法执行的参数, 注意:这三个参数都是 jdk 运行时赋值的,无需程序员给出。
-
Method 类
invoke()方法的第二个参数为 Method 类对象,该类有一个方法也叫 invoke(),可以调用 目标方法。这两个 invoke()方法,虽然同名,但无关。 public Object invoke ( Object obj, Object... args) obj:表示目标对象 args:表示目标方法参数,就是其上一层 invoke 方法的第三个参数 该方法的作用是:调用执行 obj 对象所属类的方法,这个方法由其调用者 Method 对象确定。 在代码中,一般的写法为 method.invoke(target, args); 其中,method 为上一层 invoke 方法的第二个参数。这样,即可调用了目标类的目标方法。
-
Proxy 类
通 过 JDK 的 java.lang.reflect.Proxy 类 实 现 动 态 代 理 , 会 使 用 其 静 态 方 法newProxyInstance(), 依据目标对象、业务接口及调用处理器三者,自动生成一个动态代理对象。 public static newProxyInstance ( ClassLoader loader, Class<?>[]interfaces, InvocationHandler handler) loader:目标类的类加载器,通过目标对象的反射可获取 interfaces:目标类实现的接口数组,通过目标对象的反射可获取 handler:调用处理器。
JDK动态代理实现步骤:jdk 动态代理是代理模式的一种实现方式,其只能代理接口。
- 新建一个接口,作为目标接口
- 为接口创建一个实现类,是目标类
- 创建类实现 java.lang.reflect.InvocationHandler 接口,调用目标方法并增加其他功能代码.
- 创建动态代理对象,使用 Proxy.newProxyInstance()方法,并把返回值强制转为接口类型。
第一步: 新建一个接口,作为目标接口
package com.bjpowernode.service;
//目标接口
public interface UsbSell {
/**
* @param amount :表示一次购买的数量
* @return 表示一个u盘的价格
*/
float sell(int amount);
}
第二步: 为接口创建一个实现类,是目标类
package com.bjpowernode.factory;
import com.bjpowernode.service.UsbSell;
//目标类
public class UsbKingFactory implements UsbSell {
@Override
//目标方法
public float sell(int amount) {
System.out.println("目标类中,执行了sell目标方法");
return 85.0f;
}
}
第三步: 创建类实现 java.lang.reflect.InvocationHandler 接口,调用目标方法并增加其他功能代码.
package com.bjpowernode.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//必须实现InvocationHandler接口,完成代理类要做的功能(1.调用目标方法,2. 功能增强)
public class MySellHandler implements InvocationHandler {
private Object target = null;
//动态代理:目标对象时活动的,不是固定的,需要传入进来
//传入是谁就给谁创建代理
public MySellHandler(Object target) {
//给目标对象赋值
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
res = method.invoke(target,args);
//商家需要加价,也就是代理要增加价格
if (res != null){
Float price = (Float) res;
price = price + 25;
res = price;
}
//在目标类方法调用之后,商家所做的其他功能都是增强的意思。
System.out.println("淘宝商家,返给你一个优惠券,或者红包");
//增加后的价格
return res;
}
}
第四步: 创建动态代理对象,使用 Proxy.newProxyInstance()方法,并把返回值强制转为接口类型。
package com.bjpowernode;
import com.bjpowernode.factory.UsbKingFactory;
import com.bjpowernode.handler.MySellHandler;
import com.bjpowernode.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 proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(), handler);
// 4. 通过代理执行方法
float price = proxy.sell(1);
System.out.println("通过动态代理,调用方法:" + price);
}
}