当前位置:   article > 正文

GOF23-代理模式_代理模式 gof

代理模式 gof

一、初识代理模式

代理模式:控制目标对象的访问。可以详细的控制方法的前置,后置等附加功能。由代理对象引用或者调用目标类。客户端不直接与目标类联系。

举个例子:在Spring aop中事物管理就是一个典型的代理例子,当把单个数据源事物交给spring管理时,我们对数据库的增删改操作时不会写数据库事物的开启、提交和回滚。当我们获取一个Service时是获取的一个代理对象,使用方法时,会做一个前置通知开启事物,接着执行我们的业务代码,最后提交或者回滚。 

画一个示意图

在这每一个带事物的service都有一个对应的代理类,在执行我们所写的业务代码之前和之后都会对事物进行处理。 而这个事物处理对所有数据库操作都生效。在这个例子中,service是目标类,Controller是客户端,中间包裹目标类的为代理类。

二、代理模式实现

代理模式分为静态代理,动态代理。然而动态代理又有多种实现方式。

1、静态代理模式

静态代理,动态代理差别在于代理类是否是自动生成。 顾名思义静态代理模式就是代理类为程序猿手码代码。

1)、UML类图

实现步骤:

①动态代理对象实现目标对象的接口

②代理对象持有目标对象的引用

2)代码实现

目标类

  1. package proxy.staticproxy;
  2. /**
  3. * 目标接口
  4. */
  5. public interface ITestService {
  6. void execute();
  7. }

静态代理类

  1. package proxy.staticproxy;
  2. public class TestServiceProxy implements ITestService {
  3. private ITestService testService;
  4. public TestServiceProxy(ITestService testService) {
  5. this.testService = testService;
  6. }
  7. public void execute() {
  8. System.out.println("真实方法执行前"+this.toString());
  9. this.testService.execute();
  10. System.out.println("真实方法执行后"+this.toString());
  11. }
  12. }

代码测试就不放了,很简单。理解静态代理就行

2、动态代理模式

动态代理模式,要求在程序运行时自动生成代理类,提高程序的灵活性。动态代理的实现方式有JDK动态代理,cglib动态代理,javassist动态代理,asm(不在本文)。

其实动态代理实现的代理模式和静态代理实现的代理模式本质上是没啥区别的,只是动态代理模式,代理类是靠java代码操作字节码动态在内存中生成。而生成的代码中主要使用反射实现功能。

1)JDK动态代理

JDK动态代理:此动态代理是基于接口,代理类实现目标类的接口,所以目标类必须有接口,才可以使用此代理方式。实现JDK动态代理需要一个统一处理器,而这个处理器需要实现一个接口InvocationHandler,并实现invoke方法。

①目标类不变

②处理器类

  1. package proxy.dynamic.jdk;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. public class TestServiceHandler implements InvocationHandler {
  6. private ITestService testService;
  7. public TestServiceHandler(ITestService testService) {
  8. this.testService = testService;
  9. }
  10. public ITestService getProxy(ITestService testService){
  11. this.testService =testService;
  12. ITestService proxy = (ITestService) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
  13. new Class[]{ITestService.class}, this);
  14. return proxy;
  15. }
  16. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  17. System.out.println("真实方法执行之前"+this.toString());
  18. method.invoke(testService,args);
  19. System.out.println("真实方法执行之后"+this.toString());
  20. return null;
  21. }
  22. }

③测试

  1. package proxy.dynamic.jdk;
  2. import java.lang.reflect.Proxy;
  3. public class Client {
  4. public static void main(String[] args) {
  5. ITestService testService = new TestService();
  6. TestServiceHandler handler = new TestServiceHandler(testService);
  7. handler.getProxy(testService).execute();
  8. }
  9. }

此代理模式,只对接口中存在的方法代理,对非接口中方法是调用不了的。

2)cglib动态代理

cglib动态代理:此代理方式不需要有接口的存在,此代理方式是通过集成目标对象,成为其子类的方式实现。所以如果目标类为final类那就会失败。实现cglib动态代理需要方法拦截器并且实现MethodInterceptor接口

①目标类不变

②方法拦截器类

  1. package proxy.dynamic.cglib;
  2. import net.sf.cglib.proxy.Enhancer;
  3. import net.sf.cglib.proxy.MethodInterceptor;
  4. import net.sf.cglib.proxy.MethodProxy;
  5. import java.lang.reflect.Method;
  6. public class TestServiceInterceptor implements MethodInterceptor {
  7. public static int sum = 0;
  8. public Object getProxy(){
  9. Enhancer enhancer = new Enhancer();
  10. enhancer.setSuperclass(TestService.class);
  11. enhancer.setCallback(this);
  12. return enhancer.create();
  13. }
  14. public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  15. ++sum;
  16. System.out.println("真实方法执行之前"+this.toString()+"----"+sum);
  17. methodProxy.invokeSuper(o,args);
  18. System.out.println("真实方法执行之后"+this.toString()+"----"+sum);
  19. return null;
  20. }
  21. }

③测试代码

  1. package proxy.dynamic.cglib;
  2. public class Client {
  3. public static void main(String[] args) {
  4. TestServiceInterceptor interceptor = new TestServiceInterceptor();
  5. ITestService proxy = (ITestService) interceptor.getProxy();
  6. proxy.execute("yanghs");
  7. proxy.toString();
  8. }
  9. }

cglib实现动态代理方式会把所有public对象代理。

3)javassist动态代理

javassist动态代理:此方式是通过很强大,也不需要目标类需要接口。 宁外还可以直接手工添加需要的代买在动态代理类中。实现此方式需要方法处理器类并且实现MethodInterceptor接口。

①目标类不变

②方法处理器类

  1. package proxy.dynamic.javassist;
  2. import javassist.util.proxy.MethodHandler;
  3. import javassist.util.proxy.ProxyFactory;
  4. import javassist.util.proxy.ProxyObject;
  5. import java.lang.reflect.Method;
  6. public class TestServiceMethodHander implements MethodHandler {
  7. private Object object;
  8. public Object getProxy(Object object) throws IllegalAccessException, InstantiationException {
  9. this.object = object;
  10. ProxyFactory proxyFactory = new ProxyFactory();
  11. proxyFactory.setInterfaces(new Class[]{ITestService.class});
  12. Class<?> clazz = proxyFactory.createClass();
  13. ITestService proxy = (ITestService) clazz.newInstance();
  14. ((ProxyObject)proxy).setHandler(this);
  15. return proxy;
  16. }
  17. public Object invoke(Object o, Method method, Method method1, Object[] args) throws Throwable {
  18. System.out.println("真实方法执行之前"+this.toString());
  19. method.invoke(object,args);
  20. System.out.println("真实方法执行之后"+this.toString());
  21. return null;
  22. }
  23. }

③测试代码

  1. package proxy.dynamic.javassist;
  2. public class Client {
  3. public static void main(String[] args) throws InstantiationException, IllegalAccessException {
  4. ITestService testService = new TestService();
  5. TestServiceMethodHander methodHander = new TestServiceMethodHander();
  6. ITestService proxy = (ITestService) methodHander.getProxy(testService);
  7. proxy.execute();
  8. }
  9. }

三、性能测试

1)测试代理对象生成

  1. package proxy;
  2. import proxy.dynamic.cglib.TestServiceInterceptor;
  3. import proxy.dynamic.javassist.TestServiceMethodHander;
  4. import proxy.dynamic.jdk.ITestService;
  5. import proxy.dynamic.jdk.TestService;
  6. import proxy.dynamic.jdk.TestServiceHandler;
  7. public class PerformanceTest {
  8. static final int MAX_CYC = 1000;
  9. public static void main(String[] args) throws InstantiationException, IllegalAccessException {
  10. ITestService testService = new TestService();
  11. Long start = System.currentTimeMillis();
  12. TestServiceHandler jdkhandler = new TestServiceHandler(testService);
  13. for(int i=0;i<MAX_CYC;i++){
  14. ITestService proxy = jdkhandler.getProxy();
  15. }
  16. Long end = System.currentTimeMillis();
  17. System.out.println("jdk动态代理创建"+MAX_CYC+"个代理对象用时"+(end-start));
  18. start = System.currentTimeMillis();
  19. TestServiceInterceptor cglibInterceptor = new TestServiceInterceptor();
  20. for(int i=0;i<MAX_CYC;i++){
  21. Object proxy = cglibInterceptor.getProxy();
  22. }
  23. end = System.currentTimeMillis();
  24. System.out.println("cglib动态代理创建"+MAX_CYC+"个代理对象用时"+(end-start));
  25. TestServiceMethodHander javassistHander = new TestServiceMethodHander();
  26. proxy.dynamic.javassist.ITestService javassistService = new proxy.dynamic.javassist.TestService();
  27. start = System.currentTimeMillis();
  28. for(int i=0;i<MAX_CYC;i++){
  29. Object proxy = javassistHander.getProxy(javassistService);
  30. }
  31. end = System.currentTimeMillis();
  32. System.out.println("javassist动态代理创建"+MAX_CYC+"个代理对象用时"+(end-start));
  33. }
  34. }

结果

可见jdk代理方式对代理对象生成性能最高,所用时间最少。

2)代理对象方法调用

  1. package proxy;
  2. import proxy.dynamic.cglib.TestServiceInterceptor;
  3. import proxy.dynamic.javassist.TestServiceMethodHander;
  4. import proxy.dynamic.jdk.ITestService;
  5. import proxy.dynamic.jdk.TestService;
  6. import proxy.dynamic.jdk.TestServiceHandler;
  7. public class PerformanceTest {
  8. static final int MAX_CYC = Integer.MAX_VALUE;
  9. public static void main(String[] args) throws InstantiationException, IllegalAccessException {
  10. ITestService testService = new TestService();
  11. TestServiceHandler jdkhandler = new TestServiceHandler(testService);
  12. ITestService jdkhandlerProxy = jdkhandler.getProxy();
  13. Long start = System.currentTimeMillis();
  14. for(int i=0;i<MAX_CYC;i++){
  15. jdkhandlerProxy.execute();
  16. }
  17. Long end = System.currentTimeMillis();
  18. System.out.println("jdk动态代理对象调用"+MAX_CYC+"次方法用时用时"+(end-start));
  19. TestServiceInterceptor cglibInterceptor = new TestServiceInterceptor();
  20. proxy.dynamic.cglib.ITestService cglibInterceptorProxy = (proxy.dynamic.cglib.ITestService) cglibInterceptor.getProxy();
  21. start = System.currentTimeMillis();
  22. for(int i=0;i<MAX_CYC;i++){
  23. cglibInterceptorProxy.execute();
  24. }
  25. end = System.currentTimeMillis();
  26. System.out.println("cglib动态代理对象调用"+MAX_CYC+"次方法用时用时"+(end-start));
  27. TestServiceMethodHander javassistHander = new TestServiceMethodHander();
  28. proxy.dynamic.javassist.ITestService javassistService = new proxy.dynamic.javassist.TestService();
  29. proxy.dynamic.javassist.ITestService javassistHanderProxy = (proxy.dynamic.javassist.ITestService) javassistHander.getProxy(javassistService);
  30. start = System.currentTimeMillis();
  31. for(int i=0;i<MAX_CYC;i++){
  32. javassistHanderProxy.execute();
  33. }
  34. end = System.currentTimeMillis();
  35. System.out.println("javassist动态代理对象调用"+MAX_CYC+"次方法用时"+(end-start));
  36. }
  37. }

结果

以上测试结果仅供参考,如有不对地方请指出。谢谢

4、总结

代理模式在很多地方都有用,是一个很实用的模式,而用来实现动态代理的技术更加有趣。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/562331
推荐阅读
相关标签
  

闽ICP备14008679号