public class Client {
public static void main(String[] args) throws Exception {
Player player = new Player("郝武辽");
Partner partner = new Partner("肖洁洁");
partner.receiveMoney(200);
partner.playWith(player);
}
}
运行 main 方法,控制台打印出了 肖洁洁 陪 郝武辽 玩游戏:
肖洁洁收到佣金:200元 ~
肖洁洁与郝武辽一起愉快地玩耍 ~
这样场景的基本代码也就编写完毕了,下面咱引入动态代理。
3. jdk动态代理的使用
仔细观察一下上面的代码,先思考一个问题:这个 郝武辽 是怎么找到的 肖洁洁 呢?
很明显,是在 Client 的 main 方法中,由 main 方法把他们俩搞到一起的吧,那如果不看 Client 的话,那就可以理解为,郝武辽 直接去 肖洁洁 的家里找的她,然后让她陪她一起玩游戏。
但现实情况中,通常都是玩家去陪玩的平台上找陪玩,下面我们就来搞一个陪玩平台。
3.1 陪玩平台的设计
陪玩平台中入驻了一些陪玩的选手,这里咱可以使用静态代码块来初始化一下:
public class PartnerPlatform {
private static List<Partner> partners = new ArrayList<>();
static {
partners.add(new Partner("肖洁洁"));
partners.add(new Partner("田苟"));
partners.add(new Partner("高总裁"));
}
}
public class Client {
public static void main(String[] args) throws Exception {
Player player = new Player("郝武辽");
Partner partner = PartnerPlatform.getPartner(50);
partner.receiveMoney(20);
partner.playWith(player);
}
}
使用 Cglib 时有几个小小的前提:被代理的类不能是 final 的( Cglib 动态代理会创建子类,final 类型的 Class 无法继承),被代理的类必须有默认的 / 无参构造方法(底层反射创建对象时拿不到构造方法参数)。
4.2 改造代码
首先,给原来的 Partner 添加一个无参构造方法:
public Partner() {
}
然后,修改 PartnerPlatform 获取 Partner 的方式:
public static Partner getPartner(int money) {
Partner partner = partners.remove(0);
// 使用Cglib的Enhancer创建代理对象
return (Partner) Enhancer.create(partner.getClass(), new MethodInterceptor() {
private int budget = money;
private boolean status = false;
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
// 如果在付钱时没给够,则标记budget为异常值
if (method.getName().equals("receiveMoney")) {
int money = (int) args[0];
this.status = money >= budget;
}
if (status) {
return method.invoke(partner, args);
}
return null;
}
});
}
之后,在 Client 中修改获取代理的方式:
public class Client {
public static void main(String[] args) throws Exception {
Player player = new Player("郝武辽");
// 此处的Partner是a_basic包下的,不是接口 是类
Partner partner = PartnerPlatform.getPartner(50);
partner.receiveMoney(20);
partner.playWith(player);
partner.receiveMoney(200);
partner.playWith(player);
}
}
运行 main 方法,控制台只会打印拿到 200 之后的玩耍,证明已经成功构造了代理。
肖洁洁收到佣金:200元 ~
肖洁洁与郝武辽一起愉快地玩耍 ~
4.3 Cglib动态代理的核心API
同 jdk 动态代理相似,Cglib 动态代理的内容相对较少,它只需要传入两个东西:
Class type :被代理的对象所属类的类型
Callback callback :增强的代码实现
由于一般情况下我们都是对类中的方法增强,所以在传入 Callback 时通常选择这个接口的子接口 MethodInterceptor (所以也就有了上面代码中 new 的 MethodInterceptor 的匿名内部类)。