代理模式(Proxy Pattern)中,一个类代表另一个类的功能。代理模式是一种结构型设计模式,主要解决的问题是:在直接访问时带来的问题。
代理模式的介绍
作用 :为其他对象提供一种代理以控制对这个对象的访问;
解决 :在直接访问时带来的问题,代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。
实例 :1.去火车票代售点买火车票;2.spring aop;3.调用具体类之前需要日志打印,可以再代理类中添加日志打印的功能;
1.静态代理
1.1具体的实现类
/**
* 静态代理模式
* @author gakki22
*
*/
//具体的实现类
class RealObject implements Interface {
public void doSomething() {
System.out.println("doSomething");
}
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
}
}
1.2代理类
//代理类
class SimpleProxy implements Interface {
//目标对象
private Interface proxied;
//提供构造器传入对象
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
public void doSomething() {
System.out.println("SimpleProxy doSomething");
proxied.doSomething();
}
public void somethingElse(String arg) {
System.out.println("SimpleProxy somethingElse " + arg);
proxied.somethingElse(arg);
}
}
1.3客户端的调用
//客户端的调用
public class SimpleProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
}
输出结果:
1.4总结
1.4.1静态代理的优点
代理使客户端和具体的实现类隔离开来,这样客户端只需要知道代理类即可,解耦;具有高的拓展性。
1.4.2静态代理的缺点
1)代理类和委托类实现相同的方法,这样会出现代码重复;此外,接口方法的增加也会迫使代理类和委托类代码的增加,增加代码的维护难度;
2)一个代理类服务一个委托类,对于多类型的委托对象,势必要对每一种对象都进行代理;当代码规模变大时,静态代理已不适应业务需求;
2.动态代理
上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象。
动态代理的实现需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持
2.1动态代理实现的java.lang.reflect.InvocationHandler接口
public interface InvocationHandler {
//Object proxy:具体的实现类 //Method method:要调用的方法 //Object[] args:方法调用时所需要的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
2.2java.lang.reflect.Proxy 类
//ClassLoader loader:类的加载器
//Class> interface:得到的全部的接口
//InvocationHandler h:得到的InvocationHandler接口的子类的实例
public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces, InvocationHandler h)
throws IllegalArgumentException
2.3代理类
//代理类
class DynamicProxyHandler implements InvocationHandler {
// 目标对象
private Object proxied;
//通过构造器传入对象
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
//打印代理类、调用的方法以及参数
System.out.println("**** proxy: " + proxy.getClass() +", method: " + method + ", args: " + args);
//如果有参数遍历参数
if(args != null)
for(Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
2.4客户端调用
//客户端调用
class SimpleDynamicProxy {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
//ClassLoader loader:类的加载器
//Class> interface:得到的全部的接口
//InvocationHandler h:得到的InvocationHandler接口的子类的实例
Interface proxy = (Interface)Proxy.newProxyInstance(Interface.class.getClassLoader(),
new Class[]{ Interface.class },new DynamicProxyHandler(real));
consumer(proxy);
}
}
通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器(通常可以从已经加载的对象中获取其类加载器),一个你希望该代理实现的接口列表(不是类或者抽象类),以及InvocationHandler接口的一个实现。动态代理将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递一个“实际对象”的引用,从而使得调用处理器在执行其中介任务时,可以请求转发。
输出结果:
可以看到,我们可以通过DynamicProxyHandler代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke()的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理。
2.5动态代理的优点
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。