今天给大家详细的介绍一下23中常用的设计模式中的代理设计模式,代理模式分为两种,分别为静态代理和动态代理。
意图:为其他对象提供一种代理以控制对这个对象的访问。
解析:
1,其他对象:目标对象,想要访问的对象,常被称为被委托对象或被代理对象。
2,提供一种代理:这里"一种"两个字比较重要,为什么不是提供一个呢?一种代表了某一类,即代理类和被 代理类必须实现同一接口,这个接口下的所有实现类都能被代理访问到,其实只是单纯的为了 实现代理访问功能,代理类不实现任何接口也能完成,不过针对于面向接口的编程,这种方式 更易于维护和扩展,代理类实现接口,不管目标对象怎么改或改成谁,代理类不需要任何修改 ,而且任何目标对象能使用的地方都能用代理去替换。
3,通过代理访问目标对象:代理类需要持有目标对象的引用,这样用户可以通过代理类访问目标对象,实现 了用户与目标对象的解耦。
4,为什么要通过代理来访问:设计模式都是为了解决某一类的问题,可能目标对象不想让该用户访问或者是 该用户无法访问到目标对象,这样就需要一个第三者来建立他们的联系。
5,代理类功能增强(思考装饰设计模式的思想):代理对象能直接访问到目标对象,这样它就能在调用目标对象的某个方法之前做一个预 处理,在调用方法之后进行一些结尾工作,这样就对目标对象的方法进行了增强,但是这并不是代理模式的核心思想。
先讲一个真实场景,博主在的一家公司代理打印机产品,于是找到了一家做打印机的公司(Postek),然后将打印机卖给百丽集团,我们就成了代理者,postek成了被代理者。
1,先看一下这个例子的uml图
这个新版的staruml不太会用,实现怎么成了直线了。画的太丑了...PostekProxy的salePrinter()方法,其实是调用了PostekCompany的salePrinter()方法
2,PostekCompany和PostekCompanyProxy有共同的方法salePrinter(),就是都出售打印机,根据uml图编写代码如下:
IProxy.java
package proxy;
public interface IProxy {
void salePrinter();
}
PostekCompany.java
package proxy;
public class PostekCompany implements IProxy{
@Override
public void salePrinter() {
// TODO Auto-generated method stub
System.out.println("postek sale printer");
}
}
PostekCompanyProxy.java
package proxy;
public class PostekCompanyProxy implements IProxy {
private PostekCompany mPostekCompany;
public PostekCompanyProxy() {
// TODO Auto-generated constructor stub
mPostekCompany = new PostekCompany();//控制对象的访问
}
//这个方法就是我们公司代理出售打印机的代理方法,也就是最终由我们面向客户,
//可能客户并不知道打印机最终是由谁生产的,
@Override
public void salePrinter() {
// TODO Auto-generated method stub
//我们可以直接出售打印机,当然我们也可以做一些自己的事情
//第一件事情,当然我们得赚差价了,要不然做代理干啥
System.out.println("赚100块钱");
//当然还可以做一些其他的事情,就是比如可能改打印机的logo啊,
System.out.println("修改打印机的logo");
//出售打印机
mPostekCompany.salePrinter();//当然最终打印机还是得由博思得来真正的出售啦
//我们赚了钱,当然要提供服务了,比如7*24小时在线解决打印机问题,这样才能长久嘛
System.out.println("提供7*24小时处理打印机问题");
}
}
ProxyClient.java
package proxy;
public class ProxyClient {
public static void main(String[] args) {
//直接面向客户卖打印机,客户根本不知道真正生产打印机的地方
//这里是区别静态代理模式和装饰设计模式很重要的一个地方
PostekCompanyProxy proxy = new PostekCompanyProxy();
proxy.salePrinter();
}
}
运行结果:
静态代理非常简单,我们来看下动态代理,动态代理有两种实现方式,一种是JDK自带,一种是CGLIB,使用上其实差不多,分别来看一下。
动态代理有两个非常重要的接口(InvocationHandler)和类(Proxy)。
Proxy主要使用newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)于生成动态代理的对象我看来看下这个方法以及参数的描述
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序,这个指定的调用处理程序就是第三个参数InvocationHandler中的invoke()方法,等下再讲
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
InvocationHandler:主要是当动态代理类调用代理的方法的时候,其实运行的是该类中的invoke()方法。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
功能:在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
参数:
proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
接下来通过代码分析如何使用:
IDynamicProxy.java
package dynamicproxy;
public interface IDynamicProxy {
void salePrinter();
}
PostekCompany.java
package dynamicproxy;
public class PostekCompany implements IDynamicProxy {
@Override
public void salePrinter() {
// TODO Auto-generated method stub
System.out.println("postek sale printer");
}
}
DynamicProxyHandler.java
package dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler{
private Object mObj;//需要代理的对象
public DynamicProxyHandler(Object obj) {
// TODO Auto-generated constructor stub
this.mObj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("proxy = "+proxy.getClass().getName());
System.out.println("赚钱100");
method.invoke(this.mObj, args);
System.out.println("提供很好的服务");
return null;
}
}
DynamicClient.java
package dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class DynamicClient {
public static void main(String[] args) {
IDynamicProxy iProxy = new PostekCompany();
//IDynamicProxy iProxy = new DSCompany();
InvocationHandler handler = new DynamicProxyHandler(iProxy);
IDynamicProxy iDynamicProxy = (IDynamicProxy)Proxy
.newProxyInstance(handler.getClass().getClassLoader(),
iProxy.getClass().getInterfaces() ,
handler);
//上述代码为什么能够转换成IDynamicProxy,这是因为newProxyInstance方法的第二个参数,
//要求生成的代理类实现了IDynamicProxy接口
System.out.println("iDynamicProxy = "+iDynamicProxy.getClass().getName());
iDynamicProxy.salePrinter();
}
}
打印结果:
这是一个简单的动态代理的例子,细心的朋友可能看到控制台中打印的$Proxy0是啥,这个其实是代码在运行时期动态生成的代理对象。有些博客上面描述InvocationHandler的实现类是动态的代理类,其实并不是这样子的,我们在InvocationHandler中的invoke()方法中打印了第一个参数,观察第一个参数的返回值和DynamicClient中的生成的Proxy.newProxyInstance()生成的对象是一样的。这个才是真正的动态代理类。
好了关于代理模式暂时写到这里吧,后面有时间另外再更新!如有不正确的地方欢迎指正