zoukankan      html  css  js  c++  java
  • java设计模式(一)动态代理模式,JDK与CGLIB分析

    -本想着这个知识点放到Spring Aop说说可能更合适一点,但因为上一篇有所提到就简单分析下,不足之处请多多评论留言,相互学习,有所提高才是关键!

    什么是代理模式:

      记得有本24种设计模式的书讲到代理模式的经典例子,说的是西门庆、王婆、潘金莲的故事,比如西门庆找潘金莲,不好意思啊,则么办,找那个王婆做代理, 代理模式就是找个东西代替自己完成自己的活,这个就称之为代理。如宋哲经纪人,找房子中介......具体看看代码怎么写

    区别

    • JDK动态代理只能对实现了接口的类生成代理,而不能针对类
    • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)

    --JDK动态代理

    • 定义一个接口,保存和修改方法
      public interface UserDao {
          public void save();
          public void update();
      
      }
      public class UserDaoImpl implements UserDao {
      
          @Override
          public void save() {
              System.out.println("sava doing .........");
          }
          @Override
          public void update() {
              System.out.println("update doing .........");
          }
      
      }
    • 测试一把:
      public class TestProxy {
          @Test
          public void run1(){
              //创建对象方式调用方法执行。
              UserDao dao = new UserDaoImpl();
              dao.save();
              dao.update();
              
              System.out.println("=============================");
          
          }
      }
      sava doing .........
      update doing .........
      =============================
    • 加入代理模式   java.lang.reflect.Proxy
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      import com.kk.Dao.UserDao;
      /**
       *  使用JDK的方式生成代理对象
       * @author Administrator
       */
      public class MyProxyUtils {
      
          public static UserDao getProxy(final UserDao dao) {
              // 使用Proxy类生成代理对象
              UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(),
                      dao.getClass().getInterfaces(), new InvocationHandler() {
      
                          // 代理对象方法一直线,invoke方法就会执行一次
                          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              //在此可以利用代理对象来增强方法  比如在执行方法前记录一条日志。 当然进行权限校验,日志记录,性能监控,事务控制等都可以在此加强。这也是aop思想
                              if("save".equals(method.getName())){
                                  System.out.println("记录日志...");
                              }
                              //让dao类的save或者update方法正常的执行下去
                              UserDao invoke =(UserDao) method.invoke(dao, args);
                              return invoke;
                          }
                      });
              // 返回代理对象
              return proxy;
          }
      
      }
    • 测试第二把:利用代理对象测试
      public class TestProxy {
          @Test
          public void run1(){
              UserDao dao = new UserDaoImpl();
              // 使用工具类,获取到代理对象
              UserDao proxy = MyProxyUtils.getProxy(dao);
              // 通过代理对象调用方法
              proxy.save();
              proxy.update();
          }
      }
      记录日志...
      sava doing .........
      update doing ......... 

    由此可以看出来,在我方法执行前,我在没有改变原方法前加了一段记录日志/代码,这就是一种aop思想,降低代码耦合度。

    CGLIB代理方式:

    • 加入cglib-2.2.2.jar: 及三方依赖  asm-3.3.1.jar

    • 编写BookDaoImpl实现类

      public class BookDaoImpl {
          public void save() {
              System.out.println("sava doing .........");
          }
          public void update() {
              System.out.println("update doing .........");
          }
      }
    • CGLIB创建代理方式工具类
      package com.kk.Utils;
      import java.lang.reflect.Method;
      
      import com.kk.DaoImpl.BookDaoImpl;
      import com.kk.DaoImpl.UserDaoImpl;
      
      import net.sf.cglib.proxy.Enhancer;
      import net.sf.cglib.proxy.MethodInterceptor;
      import net.sf.cglib.proxy.MethodProxy;
      
      public class MyCglibUtils {
      
          /**
           * 使用CGLIB方式生成代理的对象
           * 
           * @return
           */
          public static BookDaoImpl getProxy( BookDaoImpl bookDaoImpl) {
              Enhancer enhancer = new Enhancer();
              // 设置父类
              enhancer.setSuperclass(bookDaoImpl.getClass());
              // 设置回调函数
              enhancer.setCallback(new MethodInterceptor() {
                  // 代理对象的方法执行,回调函数就会执行
                  public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                      if(method.getName().equals("save")){
                          System.out.println("记录日志...");
                      }
                      // 正常执行
                      return methodProxy.invokeSuper(obj, args);
                  }
              });
              // 生成代理对象
              BookDaoImpl proxy = (BookDaoImpl) enhancer.create();
              return proxy;
          }
      }
    • 测试一把:
      @Test
          public void run1(){
              //目标对象
              BookDaoImpl dao = new BookDaoImpl();
              // 通过代理对象调用方法
              dao.save();
              dao.update();
              System.out.println("================================");
              BookDaoImpl proxy = MyCglibUtils.getProxy(dao);
              proxy.save();
              proxy.update();
          }
      
      sava doing .........
      update doing .........
      ================================
      记录日志...
      sava doing .........
      update doing .........

      同理:通过代理对象可以帮我们完成代码增强等功能,注意:JDK代理是通过接口模式,而CGLIB则通过实现类方式帮我们创建代理对象。后期Spring会更深了解。

      Spring在选择用JDK还是CGLiB的依据:

    • 当Bean实现接口时,Spring就会用JDK的动态代理
    • 当Bean没有实现接口时,Spring使用CGlib是实现
    • 可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)

      CGlib比JDK快?

    • 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
    • 在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。

       

  • 相关阅读:
    python
    爬虫
    python 自动登录
    day22 cookie session 中间件 Form
    day10进程、异步IO、
    python第五课
    day21
    day20 Django
    day 19
    day18
  • 原文地址:https://www.cnblogs.com/douyu2580860/p/8372285.html
Copyright © 2011-2022 走看看