静态代理和动态代理
代理,从字面上理解就代表。一个类A代表另外一个类B,通过A类得到B类提供的服务。代理分为两类:静态代理和动态代理。静态代理是在编译时就确定代理关系,如类A代理类B。而动态代理,是在运行时才确定的代理关系。
静态代理的实现
在静态代理的实现中,代理对象与被代理对象必须实现同一个接口。在代理对象中可以增加额外的相关服务,如运行日志,并在需要的时候调用被代理对象。这样被代理对象可以全心全意实现自己的业务功能,而代理类可以负责实现相关的辅助操作。
有代码有真相:
在使用时,IHello proxy=new HelloProxy(new HelloSpeaker()),proxy.hello()就可以既获得了业务服务,也获得了与业务无关的日志服务。实现了日志与业务功能的分离。但是很明显,该代理类HelloProxy只能代表实现IHello接口的类,无法为其他接口提供日志服务了。
动态代理的实现
动态代理就可以使一个实现非业务功能的处理器服务于各个对象,而不再针对特定的接口。在java中的实现要借助反射,提供非业务功能的处理器需要实现接口java.lang.reflect.InvocationHandler。
Code means Truth:
在写具体例子之前,我们再来了解下java.lang.reflect.Proxy类,它提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。动态代理类包含代理接口的实现和调用处理程序。代理接口是代理类实现的一个接口。代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它实现接口InvocationHandler
。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke
方法。
InvocationHandler接口
通过Proxy.newProxyInstance()创建代理实例:
实现运行日志功能的例子:
使用:
就会出现如下结果:
可能会纳闷信息显示的顺序好像不对,日志的动作是由另一个线程来进行,日志动作不介入程序流程。
LogHandler不再服务于特点对象与接口,而HelloSpeaker也不用插入任何有关日志的动作,专心于自己的业务逻辑的实现。接着介绍几个AOP的概念:
将日志等与业务逻辑无关的动作或任务提取出来,设计成独立可重用的对象,如HelloProxy和LogHandler,这样的对象成为切面(Aspect)。日志这样的动作为横切关切点(cross-cuting concern)。将日志、安全检查等这样的动作设计为通用的、不介入特定业务对象的一个职责清楚的Aspect对象,就是所谓的Aspect-oriented programming-AOP。
切面中对cross-cuting concern(如日志动作)的具体实现称为Advice,LogHandler(Aspect)的invoke()方法就是一个Advice。Advice在应用程序执行时加入业务流程的点或时机称为JointPoint,即Advice被执行的时机。
注:在Spring中,只支持方法的jointpoint,即advice的执行时机只可能是某个方法被执行前或执行后(或两者都有),或方法抛出异常时。
Pointcut用于定义JointPoint和advice。当调用的方法符合Pointcut表示时,将advice织入至应用程序上提供服务。
一个advice服务的对象为Target,如HelloSpeaker,advice为HelloSpeaker方法的执行打了日志。
Introduction可以为某个已编写或编译完的类,在执行时期动态地加入一些方法或行为,而不用修改原类。
来个总体介绍就是:
动作cross-cutting concerns被单独封装为Aspects,aspects的advice具体实现这些动作。程序根据pointcut将advice在合适的时机(jointpoint)织入到target上。