设计模式——代理模式2
动态代理
什么是动态代理?动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。
常见的切面编程AOP(Abspect Oriented Programming),其核心就是采用了动态代理机制。
从类图中看到增加了一个InvocationHandler接口和GamePlayIH类,作用就是产生一个对象的代理对象,其中InvocationHandle是JDK提供的动态代理接口,对被代理类的方法进行代理。
动态代理类
public class GamePlayH implements InvocationHandler {
//被代理者
Class cls = null;
//被代理的实例
Object obj = null;
//代理的目标
public GamePlayH(Object _obj) {
this.obj = _obj;
}
//调用被代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(this.obj, args);
return res;
}
}
其中invoke方法是接口InvocationHandle定义必须实现的,它完成对真实方法的调用。动态代理是根据被代理的接口生成所有的方法,也就是给定一个接口,动态代理会宣称,我们已经实现该接口下的所有方法了。默认情况下所有方法的返回值都是空的,代理已经实现了它了,通过InvocationHandle接口,所有方法都由该Handle来进行处理,即所有被代理的方法都由InvocationHandle接管实际的处理任务。
动态代理的场景
public class Client {
public static void main(String[] args) {
//定义一个玩家
IGamePlayer player = new GamePlayer("张三");
//定义一个Handle
InvocationHandler handle = new GamePlayH(player);
//开始游戏 ,记下时间
System.out.println("开始时间:2018-8-2 13:10");
//获得类 的 class loader
ClassLoader cl = player.getClass().getClassLoader();
//动态产生一个代理类
IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl, new Class[]{IGamePlayer.class}, handle);
//登录
proxy.login("yangfan", "123123");
//操作
proxy.killBoss();
proxy.upgrade();
//记录结束游戏时间
System.out.println("结束时间:2018-8-2 15:10");
}
}
结果:
开始时间:2018-8-2 13:10
登录名为yangfan的用户张三 登录成功
张三在击杀小怪
张三又升一级
结束时间:2018-8-2 15:10
既没有创建代理类,也没有实现IGamePlayer接口,这就是动态代理。进阶,在游戏登录后发送一个信息告知登录,防止盗号异地登录。
修改后的动态代理
public class GamePlayH2 implements InvocationHandler {
//被代理者
Class cls = null;
//被代理的实例
Object obj = null;
//代理的目标
public GamePlayH2(Object _obj) {
this.obj = _obj;
}
//调用被代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(this.obj, args);
//如果是登录方法那么进行信息反馈
if(method.getName().equalsIgnoreCase("login")){
System.out.println("有人在用我的账号登录");
}
return res;
}
}
结果:
开始时间:2018-8-2 13:10
登录名为yangfan的用户张三 登录成功
有人在用我的账号登录
张三在击杀小怪
张三又升一级
结束时间:2018-8-2 15:10
这就可以看作AOP编程,AOP编程没有使用新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不用考虑,在后期通过AOP的方式切过去。
动态代理通用类图.jpg通用动态代理的模型:
两条发展的路线。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的互相耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。
抽象主题
public interface Subject{
//业务操作
public void doSomething(String str);
}
真实主题
public class RealSubject implements Subject {
//业务操作
@Override
public void doSomething(String str) {
System.out.println("do something"+str);
}
}
动态代理的Handle类
public class MyInvocationHandler implements InvocationHandler {
//被代理的对象
private Object target = null;
//通过构造函数传递一个对象
public MyInvocationHandler(Object _obj) {
this.target=_obj;
}
//代理方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行被代理的方法
return method.invoke(this.target, args);
}
}
动态代理类
public class DynamicProxy<T> {
public static <T> T newProxyInstance(ClassLoader loader,Class<?>[] interfaces ,InvocationHandler h){
//寻找JoinPoint连接点,AOP框架使用元数据定义
if(true){
//执行一个前置通知
(new BeforeAdvice()).exec();
}
//执行目标返回结果:
return (T) Proxy.newProxyInstance(loader, interfaces, h);
}
}
通知接口及其实现
public interface IAdvice {
//通知的一个方法
public void exec();
}
public class BeforeAdvice implements IAdvice{
@Override
public void exec() {
System.out.println("我是前置通知,我被执行了!");
}
}
场景
public class Client {
public static void main(String[] args) {
//定义一个主题
Subject subject = new RealSubject();
//定义一个Handle
InvocationHandler handler = new MyInvocationHandler(subject);
//定义主题的代理
Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
//代理的行为
proxy.doSomething("完成");
}
}
结果:
我是前置通知,我被执行了!
do something完成
在DynamicProxy中我们有这样的方法:
this.obj = Proxy.newProxyInstance( c.getClassLoader() , c.getInterfaces() , new MyInvocationHandler(_obj) )
该方法是重新生成了一个对象,为什么要重新生成,因为需要使用代理。c.getInterfaces()
是说查找到该类的所有接口,然后实现接口的所有方法。方法是空的,是由new MyInvocationHandler(_obj)
这个对象负责接管。于是我们知道一个类的动态代理类是这样一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有的方法的实现,其动态调用过程如下图:
DynamicProxy它是一个通用类,不具有业务意义,可以产生一个它的实现类
DynamicProxy的具体业务实现类
import java.lang.reflect.InvocationHandler;
public class SubjectDynamicProxy extends DynamicProxy {
public static <T> T newProxyInstance(Subject subject){
//获得ClassLoader
ClassLoader loader = subject.getClass().getClassLoader();
//获得接口数组
Class<?>[] classes = subject.getClass().getInterfaces();
//获得handler
InvocationHandler handler = new MyInvocationHandler(subject);
return newProxyInstance(loader, classes, handler);
}
}
扩展以后,高层模块对代理的访问会更简单
具体业务的场景
public class Client_具体业务的场景 {
public static void main(String[] args) {
//定义一个主题
Subject subject = new RealSubject();
//定义主题的代理
Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
//代理的行为
proxy.doSomething("Finish");
}
}
结果:
我是前置通知,我被执行了!
do somethingFinish
动态代理的主要意图就是解决我们常说的横切面编程,在不改变我们已有代码的结构的情况下增强或控制对象的行为。