游戏大家可能都玩过,但是一些游戏升级就很难或者说是很耗费时间,所以就有好多人去找游戏代练进行升级。
public interface IGamePlayer {
void play();
}
public class GamePlayer implements IGamePlayer {
@Override
public void play() {
System.out.println("玩游戏,升级...");
}
}
好了我们已经定义好了自己玩游戏的接口和类了,现在我们再去找个代理帮我们玩
public class JdkProxy implements InvocationHandler {
//目标对象
private Object target;
/**
* 建立代理对象和真实对象的代理关系,并返回代理对象
* @param object 真实对象
* @return 代理对象
*
* 其中newProxyInstance方法包含了3个参数:
* (1)第一个是类加载器,采用了target本身的类加载器
* (2)第二个是把生成的动态代理对象挂在哪些接口下,这个就是挂在target实现的接口下
* (3)第三个定义实现方法逻辑的代理类,this表示当前对象
*/
public Object getProxy(Object object){
this.target = object;
Object proxy = Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
return proxy;
}
/**
* @param proxy 代理对象
* @param method 当前调度方法
* @param args 当前方法参数
* @return 代理结果返回
* @throws Throwable 异常
*
* invoke方法可以实现代理逻辑,invoke方法3个参数含义如下:
* (1)proxy:代理对象,就是bind生成的对象
* (2)method:当前调度的方法。
* (3)args:调度方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target,args);
}
}
我们可以看到上述动态代理类,在代码中并不用向我们的静态代理一样,需要把GamePlayer类指定进行,我们在运行过程中,确定了要代理GamePlayer类时,再指定就可以啦
下面我们就在场景类中测试一下
public class Client {
public static void main(String[] args) {
IGamePlayer gamePlayer = new GamePlayer();
JdkProxy jdkProxy = new JdkProxy();
//获取代理对象,因为挂在接口IGamePlayer下,所以声明代理对象IGamePlayer
IGamePlayer proxy = (IGamePlayer) jdkProxy.getProxy(gamePlayer);
proxy.play();
}
}
打印结果
玩游戏,升级...
那动态代理有什么好处了,我们可以看看我们之前 静态代理模式 中只指定代理了一个接口,万一我们想要代理两个怎么办?
要么我们写两个类分别继承两个接口
public interface class1 implements interface1{
}
public interface class2 implements interface2{
}
目标对象这样,那么我们的代理对象肯定要要分为两个类,分别代理这两个对象,这样类就太多了,那么我们就只能使用继承多个接口的方法
public interface class1 implements interface1, interface1{
@Override
public void method1(){
}
@Override
public void method2(){
}
}
这样但是万一我们在重写接口方法 (method1, method2) 中invoke目标方法时在调用前、调用后分别进行一些处理,那就会造成代码冗余。
那我们的动态代理可以解决吗?
比如说游戏代练除了帮人代练游戏,还帮人代卖的游戏账号
public interface ISellAccount {
void sell();
}
那我们的游戏者再多实现一个ISellAction的接口
public class GamePlayer implements IGamePlayer, ISellAccount {
@Override
public void play() {
System.out.println("玩游戏,升级...");
}
@Override
public void sell() {
System.out.println("出售游戏账号...");
}
}
至于我们的动态代理类都不需要变动,只要在场景类中指定我们想要代理的接口就好
public class Client {
public static void main(String[] args) {
IGamePlayer gamePlayer = new GamePlayer();
JdkProxy jdkProxy = new JdkProxy();
//如想要出售游戏账号
ISellAccount proxy1 = (ISellAccount) jdkProxy.getProxy(gamePlayer);
proxy1.sell();
//如想要找代练升级
IGamePlayer proxy2 = (IGamePlayer) jdkProxy.getProxy(gamePlayer);
proxy2.play();;
}
}
如上我们也应该清楚了,在我们的Jdk动态代理中,被代理的目标对象必须要有接口,因为我们Proxy.newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)的第二个参数需要传入目标对象实现的接口,用于我们将生成的代理对象挂在哪一个接口之下(可多个接口,使用方法如上)。
jdk动态代理我们发现被代理的目标对象必须要有接口,但是有的类没有接口怎么办呢,就不能使用代理了么,可以,那就需要使用到我们的Cglib动态代理啦
要是我们不是在Spring环境下,使用Cglib需要引入相应的jar包,依赖如下,也可以下载相应的 .jar文件导入项目中
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
cglib动态代理类如下
public class CglibProxy implements MethodInterceptor {
public Object getProxy(Class clazz){
//CGLIB enhancer增强类对象
Enhancer enhancer = new Enhancer();
//设置增强类型(父类)
enhancer.setSuperclass(clazz);
//定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
enhancer.setCallback(this);
//生成并返回代理对象
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//proxy是代理后的子类
return methodProxy.invokeSuper(proxy, args);
}
}
在Client类中,用cglib动态代理为GamePlayer生成一个代理对象,并调用其方法
public class Client {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
GamePlayer proxy = (GamePlayer) cglibProxy.getProxy(GamePlayer.class);
proxy.play();
}
}
玩游戏,升级...
我们在上述过程中就会发现,cglib动态代理就没有像jdk动态代理上代理的目标对象有着接口的要求。