代理模式能够说是生活中到处可见。好比说在携程上定火车票,携程在这里就起到了一个代理的做用,比起咱们在官网上或者直接去柜台订票,携程能够为用户提供更多人性化的选择。再好比代购,我本身的mbp就是委托别人从香港买回来的,那么那个代购人就至关于代理,免去了我来回的车费以及办签证的麻烦。直观的理解,代理是这么一个对象,咱们能够把工做委托给它,由它帮咱们去执行工做同时解决工做相关的各类麻烦,最后把工做成果交给咱们。这样一来咱们只须要关注问题的核心,并告知代理,而不须要为其余琐碎的细节操心,这正是代理模式最大的好处,让客户端专一于真正的事务处理。具体而言,代理模式分为静态代理和动态代理,它们的设计思想相似,实现却截然不同。它们的实现方式以及它们的区别是面试时常常会被问到的。下面咱们就来详细介绍它们。html
为另外一个对象提供一个替身或占位符以控制对这个对象的访问
定义简单明了。但仍是有些没有解释清楚的地方,控制对对象的访问是为了干什么?实际上是为了对真实的业务方法做某种形式的加强,好比在业务方法调用前做前置处理,在方法调用后做后置处理,而这些对客户端都是透明的。java
被代理类和代理类实现同一接口,根据面向接口编程原则,可使用被代理类的地方,通通能够用代理类代替,同时类内部持有被代理类的引用,真正的业务处理逻辑能够交给被代理类去做。面试
假设我要写一个日志代理,在真实的业务方法调用先后各记一条日志,看看静态代理是怎么作的。编程
public interface IService {
void service1();
void service2();
}
接口含有两个抽象业务方法数组
public class RealService implements IService {
@Override
public void service1() {
System.out.println("service1");
}
@Override
public void service2() {
System.out.println("service2");
}
}
被代理类实现业务接口,而且重写了业务方法。ide
public class StaticLogProxy implements IService {
private IService iService;
public StaticLogProxy(IService iService) {
this.iService = iService;
}
@Override
public void service1() {
System.out.println("service1 start!");
iService.service1();
System.out.println("service1 end!");
}
@Override
public void service2() {
System.out.println("service2 start!");
iService.service2();
System.out.println("service2 end!");
}
}
如上图所示,在业务方法执行先后分别记了一条日志,表示业务方法执行的开始和结束。咱们的代理类成功对原有业务方法作了加强。可是静态代理存在如下问题:
1.代理类和被代理类耦合,适用性差。试想若是我但愿为全部的业务类添加日志加强逻辑,那么岂不是要为几乎每一个业务类编写代理类?这是不现实也是开发时没法接受的。
2.代理类的加强逻辑和业务逻辑过于耦合,不利于后期维护和扩展。从service1和service2中就能够看出,除了中间的业务处理不同,代理类的处理逻辑是同样的,而咱们居然没有将其分离。
以上两点其实反映的是同一个问题:咱们但愿本身编写的代理类对全部业务类,全部业务方法都适用,而静态代理的泛用性太差了。问题的关键在于编写代理逻辑和业务逻辑分离的代理类,运行时才将其和具体的业务类绑定,对其业务方法作加强。为此,咱们须要动态代理。动态代理的实现方式不少,下面以jdk自带的代理方式作说明。this
public class LogHandler implements InvocationHandler {
//被代理对象
private Object target;
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + " start!");//前置处理
Object res = method.invoke(this.target, args);//执行业务方法
System.out.println(method.getName() + " end!");//后置处理
return res;
}
}
每个代理对象都有一个与之关联的方法调用处理器,该处理器实现了InvocationHandler接口并重写了invoke方法。当咱们调用代理对象的方法的时候,对该方法的调用会转交给方法调用处理器的invoke方法来执行,因此方法调用处理器的invoke方法是动态代理的核心。该方法内是通用的代理逻辑。在咱们经过反射的方式经过被代理对象target执行业务逻辑的先后,能够对其做前置和后置加强。spa
public class Client {
public static void main(String[] args) {
//1.建立被代理对象
RealService realService = new RealService();
//2.建立动态代理的方法调用处理器
LogHandler logHandler = new LogHandler(realService);
//3.建立动态代理对象
IService service=(IService)Proxy.newProxyInstance(logHandler.getClass().getClassLoader(),
realService.getClass().getInterfaces(),logHandler);
service.service1();
System.out.println("---------------");
service.service2();
}
}
第一步咱们建立了被代理对象realService;第二步咱们建立了动态代理的核心:方法调用处理器。由于处理器内部须要委托被代理对象去执行真正的业务方法,因此须要传入被代理对象做参数。第三步咱们经过调用反射包下的Proxy类的静态方法去生成真正的代理对象。该方法的方法签名以下设计
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
该方法含有三个参数
ClassLoader loader:指定加载的代理类的类加载器
Class<?>[] interfaces:指定代理类要实现的接口
InvocationHandler h:指定方法调用处理器
关于第二个参数可能要说明下,由于jdk的动态代理是针对接口的动态代理,代理类须要实现指定的业务接口,这里就是被代理类实现的那些接口。这样就能充分利用java多态特性,代码中全部能使用被代理对象的地方都能用代理对象进行替换。代理
和静态代理的运行结果同样。若是咱们要对其余业务类使用日志代理,只须要修改下客户端代码就行,这是静态代理办不到的。具备良好的扩展性是动态代理相比静态代理最大的优点。
代理用以控制对对象的访问,本质上是对其功能提供某种形式的加强。按实现又可分为静态代理和动态代理。动态代理因其代理逻辑和业务逻辑相分离的特色,具备良好的适用性和可扩展性,是Spring中AOP的底层实现。