简介
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
角色
- 抽象角色:声明真实对象和代理对象的共同接口。
- 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
- 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
核心思想
- 代理对象和目标对象均实现同一个行为接口。
- 代理类和目标类分别具体实现接口逻辑。
- 在代理类的构造函数中实例化一个目标对象。
- 在代理类中调用目标对象的行为接口。
- 客户端想要调用目标对象的行为接口,只能通过代理类来操作。
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
源码
主题对象
public interface Subject { void staticProxy(); }
目标对象
public class RealSubject implements Subject { public void staticProxy() { System.out.println("real subject"); } }
代理对象
public class ProxySubject implements Subject { private RealSubject realSubject; public ProxySubject() { } public void staticProxy() { System.out.println("proxy subject"); if (realSubject == null) { realSubject = new RealSubject(); } realSubject.staticProxy(); } }
客户端&结果
public class Client { public static void main(String[] args) { ProxySubject ps=new ProxySubject(); ps.staticProxy(); /* 结果:proxy subject real subject */ } }
动态代理
动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader,
:指定当前目标对象使用类加载器,获取加载器的方法是固定的Class<?>[] interfaces,
:目标对象实现的接口的类型,使用泛型方式确认类型InvocationHandler h
:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
源码
主题对象
public interface Subject { void dynamicProxy(); }
目标对象
public class RealSubject implements Subject { public void dynamicProxy() { System.out.println("real subject"); } }
代理对象
实现1
public class ProxySubject1 implements InvocationHandler { private Object realSubject; public ProxySubject1(Object realSubject) { this.realSubject = realSubject; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy subject"); method.invoke(realSubject, args); return null; } }
实现2
public class ProxySubject2 implements InvocationHandler { private Object realSubject = null; public ProxySubject2() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy subject"); if (realSubject == null) { realSubject = new RealSubject(); } method.invoke(realSubject, args); return realSubject; } }
匿名类
public class ProxySubject3 { private Object realSubject = null; public ProxySubject3(Object realSubject) { this.realSubject = realSubject; } public Object getProxyInstance() { return Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy subject"); method.invoke(realSubject, args); return null; } }); } }
客户端&结果
public class Client { public static void main(String[] args) throws Exception { //方法1 RealSubject realSubject1 = new RealSubject(); ProxySubject1 ps = new ProxySubject1(realSubject1); Class cls = realSubject1.getClass(); //分解步骤 /* Class proxyClass = Proxy.getProxyClass(cls.getClassLoader(), cls.getInterfaces()); Constructor<?> constructor = proxyClass.getConstructor(new Class[]{InvocationHandler.class}); Subject subject1 = (Subject) constructor.newInstance(new Object[]{ps}); subject1.dynamicProxy();*/ //简化 Subject subject1 = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),ps); subject1.dynamicProxy(); //方法2 /* Subject subject2 = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),RealSubject.class.getInterfaces(),new ProxySubject2()); subject2.dynamicProxy();*/ //方法3 /* RealSubject realSubject3 = new RealSubject(); ProxySubject3 proxySubject3 = new ProxySubject3(realSubject3); Subject subject3 = (Subject) proxySubject3.getProxyInstance(); subject3.dynamicProxy();*/ /* 结果:proxy subject real subject*/ } }
动态代理的优点:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。
Cglib代理
JDK自从1.3版本开始,就引入了动态代理,JDK的动态代理用起来非常简单,但是它有一个限制,就是使用动态代理的对象必须实现一个或多个接口 。如果想代理没有实现接口的类可以使用CGLIB包。
CGLIB是一个强大的高性能的代码生成包。它被许多AOP的框架(例如Spring AOP)使用,为他们提供方法的interception(拦截)。Hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联。EasyMock通过使用模仿(moke)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理
目标对象
//目标对象 没实现任何接口 public class RealSubject { public void cglibProxy() { System.out.println("real subject"); } }
代理对象
public class ProxyFactory implements MethodInterceptor { private Object target; public ProxyFactory(Object target) { this.target = target; } public Object getProxyInstance() { Enhancer en = new Enhancer(); en.setSuperclass(target.getClass()); en.setCallback(this); return en.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("proxy subject"); Object ret = method.invoke(target, objects); return ret; } }
客户端&结果
public class Client { public static void main(String[] args) throws Exception { RealSubject target = new RealSubject(); ProxyFactory proxyFactory = new ProxyFactory(target); RealSubject proxy = (RealSubject) proxyFactory.getProxyInstance(); proxy.cglibProxy(); /* 结果:proxy subject real subject*/ } }
代理模式和装饰器模式
装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。