代理模式分为静态代理和动态代理,所谓静态就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了,若使用静态代理,代理对象需要与目标对象实现一样的接口,所以会有很多代理类,同时一旦接口增加方法,目标对象与代理对象都要维护,解决方案–动态代理,动态代理和静态代理的区别为动态代理不用我们去手动编写代理类,在运行时,动态的在内存中生产代理类。(字节码对象级别的代理对象)摘自《图解设计模式》【日】结城浩 著)
在java.lang.reflect包中有一个代理类: Proxy
在Proxy中有方法newProxyInstance()
参数
loader - 类加载器来定义代理类
interfaces - 代理类实现的接口列表
h - 调度方法调用的调用处理函数
结果
具有由指定的类加载器定义并实现指定接口的代理类的指定调用处理程序的代理实例
注意:JDK的Proxy方式实现的动态代理目标对象必须有接口没有接口不能实现动态代理
此处附上代理模式的类图(摘自《图解设计模式》【日】结城浩 著):
参与代理模式的类
抽象角色(Subjet):该角色定义了使Proxy角色和RealSubject角色之间具有一致性的接口,这样代理角色能够用于所有真实角色所能够使用的地方。
代理角色(Proxy):Proxy角色会尽量处理来自Client角色的请求。只有当自己不能处理时,它才会将工作交给RealSubject角色。Proxy角色只有在必要时才会生成RealSubject角色,Proxy角色实现了在Subject角色中定义的接口。它维护了能够允许代理角色访问真实角色的引用。代理角色与真实角色都实现了同一个接口,这样代理角色就能够代替真实角色,实现对真实角色的访问控制并负责数据的创建和删除。根据代理的类型,其还能负责其他的职责。
真实角色(RealSubject):“本人”RealSubject角色会在“代理人”Proxy角色无法胜任工作时出场,它与Proxy角色一样也实现了在Subjet角色中定义的接口。
请求者(Client):使用Proxy模式的角色,但该角色并不包含在Proxy模式中。(本段角色分析摘自《图解设计模式》中国工信出版集团 人民邮电出版社 【日】结城浩 著)
//完成接口的定义
public interface DoHomework {
public void done() ;
public String game(String name) ;
}
//实现接口
public class Domyself implements DoHomework {
@Override
public void done() {
System.out.println("每天都有新的需求");
}
@Override
public String game(String name) {
System.out.println(name);
return "来局游戏提提神!!!!";
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
1.动态代理类必须实现InvocationHandler接口
2.定义目标对象
3.通过默认的构造方法创建动态代理对象来关联目标对象
4.重写invoke方法,(该方法类似于多线程或者过滤器一旦触发自动调用)激活此方法时必须关联目标对象否则会报异常
5.调用 getProxy()能够动态地获取到代理对象,切忌将其强转为具体的实现,必须为接口
*/
public class DongProxy implements InvocationHandler {
//定义目标对象
Object target ;
//创建构造方法,关联目标对象
public DongProxy(Object target){
this.target = target ;
}
//当代理对象调用方法的时候,将监听到以下方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target , args);//此处必须关联目标对象target,否则会报异常
}
//创建一个方法用来生成一个代理对象
public Object getProxy(){
//生成一个代理对象需要依赖Proxy
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),this) ;//this为当前对象DongProxy
}
}
/**
在动态代理对象中获取到目标对象根据目标对象获取到代理对象
*/
public class TestPro {
public static void main(String[] args) {
//创建目标对象实例
DoHomework doHomework = new Domyself() ;
//创建代理对象
DongProxy dp = new DongProxy(doHomework) ;
//生成一个动态代理
DoHomework doHomework1 = (DoHomework) dp.getProxy() ;//此处必须为接口不能强转为具体的实现类
//对对象中的方法进行调用
doHomework1.done();
System.out.println(doHomework1.game("斗地主")); ;
}
}
使用代理模式的注意事项以及使用场景(摘自《设计模式精解及面试攻略》【印度】纳拉西母哈.卡鲁曼希 著
《设计模式解析》 【美】Alan Shalloway JamesR.Troot 著
):
注意事项:
之所以对某个对象进行访问控制的一个原因是,需要推迟对象的创建和初始化,以避免大量的资源开销,直到我们真的需要使用它。
当需要对一个复杂对象进行引用时,使用代理模式更合适,而不是简单地使用指针。
通过提供与真实对象相同的接口,代理对象能够代替真实对象。
使用场景:
代理模式主要用于当我们需要用一个简单对象来表示一个复杂对象的情形。如果创建对象的开销很大,那么可以推迟其创建,并使用一个简单对象来代理其功能直到必须立即创建的时候。这个简单对象就可以称为复杂对象的代理。