代理模式
生活中的代理模式
我们现在生活的世界,是一个信息大爆炸,社会分工及其细致的一个世界。大家只需要将社会给自己的分工做好就可以活的很好,为什么呢?因为我们不会的,可以让更专业的代理来做,最后看到的结果好像使我们自己完成的一样。比如我们常看的一些都市剧,里面经常会有的剧情就是打官司不需要自己上去唇枪舌剑,交给自己的代理律师就可以了。比如现在的互联网造车公司,他们没有自己的工厂,一般都是找代工厂来代理生产,他们负责设计,策划,营销等。代理模式真的是随处可见,当然在我们设计模式中,也是有代理模式的。今天就来学习代理模式。
什么是代理模式?
代理模式:为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
类图:
代理模式分为静态代理,动态代理;动态代理有Jdk代理和Cglib代理
静态代理:
//接口类 public interface Call { public void ring(); } public class Phone implements Call { @Override public void ring() { System.out.println("phone is ringing"); } } //代理 public class StaticProxy implements Call { private Call call; public StaticProxy(Call call){ this.call = call; } @Override public void ring() { System.out.println("before"); call.ring(); System.out.println("after"); } } //测试 public static void main(String[] args){ Call call = new Phone(); StaticProxy sp = new StaticProxy(call); sp.ring(); } //运行结果 before phone is ringing after
这个看起来跟装饰者模式很像。静态代理与装饰者之间的共同点是:①都要实现与目标相同的业务接口 ②在两个类中都要声明目标对象 ③都可以在不修改目标类的前提下增强目标方法
不同点是:①目的不同。装饰者简单来说就是为了增强目标对象,静态代理是为了保护和隐藏对象 ②功能增强的实现者不同 装饰者设计模式中存在装饰者基类,其并不能实现增强,而是由具体的装饰者进行增强的,所以其存在着"装饰者链";而静态代理中,一般不存在父子类的关系,具体的增强,就是由代理类实现的。无需其子类完成,所以不存在 链 的概念。
·优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
上面的缺点可以由动态代理来解决。
Jdk动态代理:
public interface HelloWorld { public void sayHelloWorld(); } public class HelloWorldImpl implements HelloWorld { public void sayHelloWorld() { System.out.println("Hello World!"); } } public class JdkProxyExample implements InvocationHandler { private Object target = null; public Object bind(Object target){ this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("进入代理逻辑"); System.out.println("在调用真实对象之前的服务"); Object obj = method.invoke(target,args); System.out.println("在调用真实对象之后的服务"); return obj; } } public static void main(String[] args){ JdkProxyExample jdkProxyExample = new JdkProxyExample(); HelloWorld proxy = (HelloWorld) jdkProxyExample.bind(new HelloWorldImpl()); proxy.sayHelloWorld(); } 运行结果: 进入代理逻辑 在调用真实对象之前的服务 Hello World! 在调用真实对象之后的服务
代理对象,不需要实现接口;代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
注意Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader
:指定当前目标对象使用的类加载器,获取加载器的方法是固定的Class<?>[] interfaces
:指定目标对象实现的接口的类型,使用泛型方式确认类型InvocationHandler:
指定
动态处理器,
执行目标对象的方法时,会触发事件处理器的方法
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
总结:虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。纵观静态代理与动态代理,它们都能实现相同的功能,而我们看从静态代理到动态代理的这个过程,我们会发现其实动态代理只是对类做了进一步抽象和封装,使其复用性和易用性得到进一步提升而这不仅仅符合了面向对象的设计理念,其中还有AOP的身影,这也提供给我们对类抽象的一种参考。关于动态代理与AOP的关系,个人觉得AOP是一种思想,而动态代理是一种AOP思想的实现!
CGLIB代理:
public class Entity { public void doSomething(){ System.out.println("做事情"); } } public class CglibProxy implements MethodInterceptor { private Object target; public CglibProxy(Object target) { this.target = target; } //给目标对象创建一个代理对象 public Object getProxyInstance(){ //1.工具类 Enhancer en = new Enhancer(); //2.设置父类 en.setSuperclass(target.getClass()); //3.设置回调函数 en.setCallback(this); //4.创建子类(代理对象) return en.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before"); //执行目标对象的方法 Object returnValue = method.invoke(target, args); System.out.println("after"); return returnValue; } } public static void main(String[] args){ Entity target = new Entity(); Entity proxy = (Entity)new CglibProxy(target).getProxyInstance(); proxy.doSomething(); } 运行结果: before 做事情 after
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
- Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar
即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法