您的位置:首页 > 其它

静态代理和动态代理

2010-11-22 21:22 309 查看
代理,从字面上理解就代表。一个类A代表另外一个类B,通过A类得到B类提供的服务。代理分为两类:静态代理和动态代理。静态代理是在编译时就确定代理关系,如类A代理类B。而动态代理,是在运行时才确定的代理关系。

静态代理的实现

在静态代理的实现中,代理对象与被代理对象必须实现同一个接口。在代理对象中可以增加额外的相关服务,如运行日志,并在需要的时候调用被代理对象。这样被代理对象可以全心全意实现自己的业务功能,而代理类可以负责实现相关的辅助操作

有代码有真相:

public interface IHello {
void hello(String name);
}
//负责实现业务
public class HelloSpeaker implements IHello {
@Override
public void hello(String name) {
System.out.println("被代理对象,专门负责业务!"+name);
}
}
//代理HelloSpeaker,通过此类调用HelloSpeaker实现的业务逻辑
public class HelloProxy implements IHello {
private Logger logger=
Logger.getLogger(this.getClass().getName());
private IHello hello;
public HelloProxy(IHello object){
hello=object;
}
//代理类中增加非业务的功能,使用被代理对象来完成业务功能
public void hello(String name) {
logger.log(Level.INFO,"hello method starts....");
//被代理类负责业务逻辑的执行
hello.hello(name);
logger.log(Level.INFO, "hello method ends...");
}
}


在使用时,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接口

//调用处理程序需要实现该接口
public interface InvocationHandler{
/**
* 在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
* @param proxy 在其上调用method的代理实例
* @param method 对应于代理实例上的接口方法
* @param args 方法method的参数,如果方法没有参数,args=null
* @return 代理实例的方法调用返回的值
* @throws Throwable
*/
public Object invoke(Object proxy,Method method, Object[] args) throws Throwable;
}


通过Proxy.newProxyInstance()创建代理实例:

/**
* 创建代理实例
* @param loader 定义代理类的类加载器
* @param interfaces 代理类要实现的接口列表
* @param handler 指派方法调用的调用处理程序
* @return 代理实例,它有指定的类加载器loader定义,从而实现了代理接口。并且包含了调用处理程序handler
*/
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)


实现运行日志功能的例子:

/**
* 代理实例的"调用处理程序 " 对代理实例调用其代理接口方法时,将会指派到调用处理程序的 invoke 方法。
*/
public class LogHandler implements InvocationHandler {
private Logger logger = Logger.getLogger(this.getClass().getName());

private Object delegate;

//创建代理实例
public Object bind(Object delegate) {
this.delegate = delegate;
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
delegate.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 方法method的返回结果
Object result = null;
try {
//在invoke方法中,增加日志功能
logger.log(Level.INFO, "method starts..." + method);
//在invoke方法中,调用代理类对接口的实现
result = method.invoke(delegate, args);
logger.log(Level.INFO, "method ends..." + method);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}


使用:

LogHandler logHandler=new LogHandler();
IHello helloProxy=
(IHello)logHandler.bind(new HelloSpeaker());
helloProxy.hello("动态代理");


就会出现如下结果:



可能会纳闷信息显示的顺序好像不对,日志的动作是由另一个线程来进行,日志动作不介入程序流程。

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上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: