即Proxy Pattern,23种java常用设计模式之一。代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
定义:
为其他对象提供一种代理以控制对这个对象的访问。
结构:
说明:
Proxy:代理对象,通常具有如下功能:
实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象
保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象
可以控制对具体目标对象的访问,并可能负责创建和删除它
Subject:目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象
RealSubject:具体的目标对象,真正实现目标接口要求的功能。
具体代码:
/** * 抽象的目标接口,定义具体的目标对象和代理公用的接口 */ public interface Subject { /** * 示意方法:一个抽象的请求方法 */ public void request(); }
/** * 具体的目标对象,是真正被代理的对象 */ public class RealSubject implements Subject { public void request() { //执行具体的功能处理 } }
/** * 代理对象 */ public class Proxy implements Subject { /** * 持有被代理的具体的目标对象 */ private RealSubject realSubject = null; /** * 构造方法,传入被代理的具体的目标对象 * @param realSubject 被代理的具体的目标对象 */ public Proxy(RealSubject realSubject) { this.realSubject = realSubject; } public void request() { //在转调具体的目标对象前,可以执行一些功能处理 //转调具体的目标对象的方法 realSubject.request(); //在转调具体的目标对象后,可以执行一些功能处理 } }
代理(Proxy)有两种:
静态代理-Static proxy
动态代理-Dynamic proxy
具体案例:
1. 静态代理
为实现静态代理需要为HelloSpeaker写一个HelloProxy类,同样实现IHello接口,并在hello方法执行log,并执行HelloSpeaker的hello()方法。
/** * 此处可以为接口也可以为抽象类 * @author ljn * */ public interface IHelloSpeaker { public abstract void hello(); } /** * 接口实现类日志功能 简单的打印一句话 * @author ljn * */ public class HelloSpeaker implements IHelloSpeaker { public void hello(){ System.out.println("............this is Log"); } } /** * 代理类实现IHelloSpeaker接口,同时拥有实际对象的引用(private HelloSpeaker helloObj;//代理类内部引用真实类) * 代理必须完成委托对象的动作,也可以添加自己的动作(doBefore,doAfter)。 * * @author ljn * */ public class HelloProxy implements IHelloSpeaker { private HelloSpeaker helloObj;// 代理类内部引用委托类 public HelloProxy(HelloSpeaker helloSpeaker) { this.helloObj = helloSpeaker; } private void doBefore() { System.out.println("method doBefore invoke!"); } private void doAfter() { System.out.println("method doAfter invoke!"); } @Override public void hello() { // TODO Auto-generated method stub doBefore();// 其他业务逻辑 helloObj = new HelloSpeaker(); helloObj.hello();// 委托类具体的业务逻辑。 doAfter();// 其他业务逻辑 } } /** *测试类 */ public class Test { public static void main(String[] args) { //其中HelloProxy中helloObject为需要代理的对象,在其它地方如下来使用代理机制 IHelloSpeaker proxy = new HelloProxy(new HelloSpeaker()); proxy.hello(); } }
2:动态代理:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class LogHandler implements InvocationHandler { private Object delegate; public Object bind(Object delegate) { this.delegate = delegate; return Proxy.newProxyInstance( delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { System.out.println("method starts..."+method); result = method.invoke(delegate, args); System.out.println("method starts..."+method); } catch (Exception e){ e.printStackTrace(); } return result; } } //测试程序 public static void main(String[] args) { LogHandler logHandler = new LogHandler(); IHelloSpeaker helloProxy = (IHelloSpeaker) logHandler.bind(new HelloSpeaker()); helloProxy.hello(); }
使用场景:
当客户端代码需要调用某个对象时,客户端实际上也不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)。
实际用例:
看到此处,相信读者应该对Spring的AOP框架有点感觉了:当Spring容器中的被代理Bean实现了一个或多个接口时,Spring所创建的AOP代理就是这种动态代理。Spring AOP与此示例应用的区别在哪里呢?Spring AOP更灵活,当Sping定义InvocationHandler类的invoke()时,它并没有以硬编码方式决定调用哪些拦截器,而是通过配置文件来决定在invoke()方法中要调用哪些拦截器,这就实现了更彻底的解耦——当程序需要为目标对象扩展新功能时,根本无须改变Java代理,只需要在配置文件中增加更多的拦截器配置即可。
优点和缺点:
1) 优点: 向客户端隐藏了访问某个对象的细节及复杂性;可以动态地调用一个对象中的方法,且无需实现固定的接口。
留给自己的问题:
spring的aop,hibernate,mybatis的懒加载应用代理模式