Java互聯網企業架構技術VIP課程(1)【騰訊課堂每特】

 

設計 模式的分類

Java互聯網企業架構技術VIP課程【騰訊課堂每特】java

提娶馬:xb2k 設計模式

課程內容緩存

站在架構角度,基於裝飾模式純手寫設計多級緩存框架架構

 

 

  1. 如何理解多級緩存框架設計
    裝飾模式與代理模式之間的區別
    3.裝飾模式如何在Mybatis、IO流運用
    4.基於裝飾設計多級緩存框架
    5.自定義緩存註解,輕鬆搞定多級緩存問題

本節課須要知識:動態代理技術+Aop+裝飾模式+Redis緩存概念app

注意:由於該設計模式比較接近真實案例,須要有SpringBootV(cmL46679910)基礎。框架

建立型模式

工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。ide

結構型模式

適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式函數

行爲模式

策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。工具

完全搞懂代理模式實現原理

什麼是代理模式

代理模式主要對咱們方法執行以前與以後實現加強源碼分析

代理模式應用場景

  1. 日誌的採集
  2. 權限控制
  3. 實現aop
  4. Mybatis mapper
  5. Spring的事務
  6. 全局捕獲異常
  7. Rpc遠程調用接口
  8. 代理數據源

 

代理模式建立方式

靜態代理

靜態代理須要本身人工編寫代理類代碼

基於接口實現方式

public class OrderServiceProxy  implements  OrderService{
    private OrderService orderService;

    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }

    public String addOrder(String userName, String userPwd) {
        System.out.println("使用靜態代理類打印日誌開始:userName:" + userName + "," + userPwd);
        String result = orderService.addOrder(userName, userPwd);
        System.out.println("使用靜態代理類打印日誌結束:userName:" + userName + "," + userPwd);
        return result;
    }
}

 

 

public interface OrderService {
    /**
     * 須要被代理的方法
     * @return
     */
     String addOrder(String userName,String userPwd);
}

 

 

public class Test001 {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
        orderService.addOrder("mayikt","123456");
    }
}

 

 

基於繼承的實現方式

public class OrderServiceProxy  extends OrderServiceImpl {
    private OrderService orderService;

    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }

    public String addOrder(String userName, String userPwd) {
        System.out.println("使用靜態代理類打印日誌開始:userName:" + userName + "," + userPwd);
        String result = super.addOrder(userName, userPwd);
        System.out.println("使用靜態代理類打印日誌結束:userName:" + userName + "," + userPwd);
        return result;
    }
}

 

動態代理與靜態代理的區別

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

動態代理不須要寫代理類對象,經過程序自動生成,而靜態代理須要咱們本身寫代理類對象。

 

動態代理

動態代理是在實現階段不用關心代理類,而在運行階段才指定哪個對象。

動態代理類的源碼是在程序運行期間由JVM根據反射等機制動態的生成 。

 

Jdk動態代理

JDK動態代理的通常步驟以下:

1.建立被代理的接口和類;

2.實現InvocationHandlerV(cmL46679910)接口,對目標接口中聲明的全部方法進行統一處理;

3.調用Proxy的靜態方法,建立代理類並生成相應的代理對象;

實現原理:利用攔截器機制必須實現InvocationHandler接口中的invoke方法實現對

咱們的目標方法加強。

 

public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目標對象
     */
    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * @param proxy  使用jdk程序生成的代理類
     * @param method 目標方法
     * @param args   方法須要傳遞的參數
     * @return
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk動態代理打印日誌開始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk動態代理打印日誌結束" + args[1]);
        return result;
    }

    /**
     * 生成代理類
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

}

 

 

public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目標對象
     */
    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * @param proxy  使用jdk程序生成的代理類
     * @param method 目標方法
     * @param args 方法須要傳遞的參數
     * @return
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk動態代理打印日誌開始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk動態代理打印日誌結束" + args[1]);
        return result;
    }

    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

}

 

 

JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderServiceImpl orderService = jdkInvocationHandler.getProxy();
orderService.addOrder("mayikt", "meite");

 

 

加上該代碼:

 

  1. 獲取代理的生成的class文件

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

 

  1. 使用反編譯工具該Pclass

 

 

注意:繼承了Proxy類,實現了代理的接口,因爲java不能多繼承,這裏已經繼承了Proxy類了,不能再繼承其餘的類,因此JDK的動態代理不支持對實現類的代理,只支持接口的代理。

純手寫Jdk動態代理

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

思路分析:

  1. 定義InvocationHandler類 回調方法
  2. 使用java反射技術獲取接口下全部的方法,拼接 $Proxy0.java代碼
  3. 在將$Proxy0.java編譯成class文件,讀取到內存中V(cmL46679910)

public class $Proxy0 implements com.mayikt.service.OrderService {

    private MayiktJdkInvocationHandler h;
    private static Method m3;

    public $Proxy0(MayiktJdkInvocationHandler mayiktJdkInvocationHandler) {
        this.h = mayiktJdkInvocationHandler;
    }

    @Override
    public String addOrder(String ver1, String var2) {
        try {
            return (String) h.invoke(this, m3, new Object[]{ver1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    static {
        try {
            m3 = Class.forName("com.mayikt.service.OrderService").getMethod("addOrder", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 

 

public class MyJdkInvocationHandler implements MayiktJdkInvocationHandler {
    /**
     * 目標對象
     */
    private Object target;

    public MyJdkInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk動態代理打印日誌開始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk動態代理打印日誌結束" + args[1]);
        return result;
    }
    public <T> T getProxy() {
        return (T) new $Proxy0(this);
    }

}

 

MyJdkInvocationHandler myJdkInvocationHandler = new MyJdkInvocationHandler(new OrderServiceImpl());
OrderService orderService = myJdkInvocationHandler.getProxy();
orderService.addOrder("mayikt", "meite");

 

public class MyProxy {
    private static String rt = "\r\t";

    public static Object newProxyInstance(JavaClassLoader classLoader, Class classInfo, MayiktInvocationHandler mayiktInvocationHandler) {
        try {
            // 1.拼接java代理代理源代碼
            Method[] methods = classInfo.getMethods();
            String proxyClass = "package com.mayikt.service;" + rt
                    + "import java.lang.reflect.Method;" + rt
                    + "import com.mayikt.service.proxy.MayiktInvocationHandler;" + rt
                    + "import java.lang.reflect.UndeclaredThrowableException;" + rt
                    + "public class $Proxy0 implements " + classInfo.getName() + "{" + rt
                    + "MayiktInvocationHandler h;" + rt
                    + "public $Proxy0(MayiktInvocationHandler h)" + "{" + rt
                    + "this.h= h;" + rt + "}"
                    + getMethodString(methods, classInfo) + rt + "}";
            // 2.將該源代碼寫入到本地文件中
            String filename = "d:/code/$Proxy0.java";
            File f = new File(filename);
            FileWriter fw = new FileWriter(f);
            fw.write(proxyClass);
            fw.flush();
            fw.close();
            // 3.編譯爲class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(filename);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            // 4.class文件加入到內存中
            Class proxy0Class = classLoader.findClass("$Proxy0");
            //5.使用java反射機制給函數中賦值
            Constructor m = proxy0Class.getConstructor(MayiktInvocationHandler.class);
            Object object = m.newInstance(mayiktInvocationHandler);
            return object;
        } catch (Exception e) {
            return null;
        }
    }

    public static String getMethodString(Method[] methods, Class intf) {
        String proxyMe = "";

        for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < parameterTypes.length; i++) {
                sb.append(parameterTypes[i].getName() + " ver" + (i + 1));
                if (i < parameterTypes.length - 1) {
                    sb.append(" ,");
                }
            }
            String parameterStr = sb.toString();
            proxyMe = "public " + method.getReturnType().getName() + " " + method.getName() + " ( " + parameterStr + " ) { " +
                    "try {   Method m3 = Class.forName(\"com.mayikt.service.OrderService\").getMethod(\"addOrder\", Class.forName(\"java.lang.String\"), Class.forName(\"java.lang.String\"));" +
                    "return (String) h.invoke(this, m3, new Object[]{ver1, ver2}); } catch (RuntimeException | Error var4) {  throw var4;  } catch (Throwable var5) {   throw new UndeclaredThrowableException(var5); } " +
                    "" +
                    " } ";

        }
        return proxyMe;
    }

    public static void main(String[] args) {
        newProxyInstance(null, OrderService.class, null);
    }
}

 

 


public class JavaClassLoader extends ClassLoader {

    private File classPathFile;

    public JavaClassLoader(){
//        String classPath=JavaClassLoader.class.getResource("").getPath();
        String classPath="D:\\code";
        this.classPathFile=new File(classPath);
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String className= JavaClassLoader.class.getPackage().getName()+"."+name;
        if(classPathFile!=null){
          File classFile=new File(classPathFile,name.replaceAll("\\.","/")+".class");
          if(classFile.exists()){
              FileInputStream in=null;
              ByteArrayOutputStream out=null;
              try {
                  in=new FileInputStream(classFile);
                  out=new ByteArrayOutputStream();
                  byte[] buff=new byte[1024];
                  int len;
                  while ((len=in.read(buff))!=-1){
                     out.write(buff,0,len);
                  }
                  return defineClass(className,out.toByteArray(),0,out.size());
              }catch (Exception e){
                  e.printStackTrace();
              }finally {
                  if(in!=null){
                      try {
                          in.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if(out!=null){
                      try {
                          out.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
        }
        return null;
    }
}

 

 

public class MyJdkInvocationHandler implements MayiktJdkInvocationHandler {
    /**
     * 目標對象
     */
    private Object target;

    public MyJdkInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk動態代理打印日誌開始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk動態代理打印日誌結束" + args[1]);
        return result;
    }


    public <T> T getProxy() {
        return (T) MyProxy.newProxyInstance(new JavaClassLoader(), target.getClass().getInterfaces()[0], this);
    }
}

 

 

CGLIB動態代理

利用asm字節碼技術,生成子類實現對目標方法實現加強

實現方式
Maven依賴

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
</dependencies>

 

 

核心代碼

public class CglibMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("<<<<<日誌收集開始...>>>>>>>");
        Object reuslt = proxy.invokeSuper(obj, args);
        System.out.println("<<<<<日誌收集結束...>>>>>>>");
        return reuslt;
    }
}

 

 

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
Enhancer enhancer = new Enhancer();
// 設置代理類的付類
enhancer.setSuperclass(MemberServiceImpl.class);
// 設置回調對象
enhancer.setCallback(cglibMethodInterceptor);
// 建立代理對象
MemberServiceImpl orderServiceImpl = (MemberServiceImpl) enhancer.create();
orderServiceImpl.getMember();

 

 

 

Cglib動態代理底層源碼分析

Cglib依賴於ASM字節碼技術,直接生成class文件,在採用類加載器讀取到程序中,

使用fastclass對被代理類的方法創建索引文件不須要依賴於反射查找到目標方法,因此效率比Jdk動態代理要高。

public class OrderServiceImpl$$EnhancerByCGLIB$$1dd3a71c extends OrderServiceImpl {

    static void CGLIB$STATICHOOK1() throws ClassNotFoundException {
        Method amethod[];
        Method amethod1[];
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class class1 = Class.forName("com.mayikt.service.impl.OrderServiceImpl$$EnhancerByCGLIB$$1dd3a71c");
        Class class2;
        amethod = ReflectUtils.findMethods(new String[]{
                "addOrder", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"
        }, (class2 = Class.forName("com.mayikt.service.impl.OrderServiceImpl")).getDeclaredMethods());
        Method[] _tmp = amethod;
        CGLIB$addOrder$0$Method = amethod[0];
        CGLIB$addOrder$0$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", "addOrder", "CGLIB$addOrder$0");
        amethod1 = ReflectUtils.findMethods(new String[]{
                "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"
        }, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        Method[] _tmp1 = amethod1;
        CGLIB$equals$1$Method = amethod1[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = amethod1[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = amethod1[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = amethod1[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    }

    final String CGLIB$addOrder$0(String s, String s1) {
        return super.addOrder(s, s1);
    }


    final boolean CGLIB$equals$1(Object obj) {
        return super.equals(obj);
    }


    final String CGLIB$toString$2() {
        return super.toString();
    }


    final int CGLIB$hashCode$3() {
        return super.hashCode();
    }


    public static void CGLIB$SET_THREAD_CALLBACKS(Callback acallback[]) {
        CGLIB$THREAD_CALLBACKS.set(acallback);
    }
V(cmL46679910)
    public final String addOrder(String paramString1, String paramString2) {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null) {

            CGLIB$BIND_CALLBACKS(this);
        }
        try {
            MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
            if (tmp17_14 != null) {
                return (String) tmp17_14.intercept(this, CGLIB$addOrder$0$Method, new Object[]{paramString1, paramString2}, CGLIB$addOrder$0$Proxy);
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return super.addOrder(paramString1, paramString2);
    }


    public Object newInstance(Callback acallback[]) {
        CGLIB$SET_THREAD_CALLBACKS(acallback);
        CGLIB$SET_THREAD_CALLBACKS(null);
        return new OrderServiceImpl$$EnhancerByCGLIB$$1dd3a71c();
    }

    public Object newInstance(Callback callback) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{
                callback
        });
        CGLIB$SET_THREAD_CALLBACKS(null);
        return new OrderServiceImpl$$EnhancerByCGLIB$$1dd3a71c();
    }


    public void setCallback(int i, Callback callback) {
        switch (i) {
            case 0: // '\0'
                CGLIB$CALLBACK_0 = (MethodInterceptor) callback;
                break;
        }
    }


    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static ThreadLocal CGLIB$THREAD_CALLBACKS = null;
    private static final Callback CGLIB$STATIC_CALLBACKS[] = null;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static Method CGLIB$addOrder$0$Method = null;
    private static MethodProxy CGLIB$addOrder$0$Proxy = null;
    private static Object[] CGLIB$emptyArgs = null;
    private static Method CGLIB$equals$1$Method = null;
    private static MethodProxy CGLIB$equals$1$Proxy = null;
    private static Method CGLIB$toString$2$Method = null;
    private static MethodProxy CGLIB$toString$2$Proxy = null;
    private static Method CGLIB$hashCode$3$Method = null;
    private static MethodProxy CGLIB$hashCode$3$Proxy = null;
    private static Method CGLIB$clone$4$Method = null;
    private static MethodProxy CGLIB$clone$4$Proxy = null;

    static {
        try {
            CGLIB$STATICHOOK1();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public OrderServiceImpl$$EnhancerByCGLIB$$1dd3a71c() {
        CGLIB$BIND_CALLBACKS(this);
    }

    private void CGLIB$BIND_CALLBACKS(OrderServiceImpl$$EnhancerByCGLIB$$1dd3a71c orderServiceImpl$$EnhancerByCGLIB$$1dd3a71c) {
    }
}

 

FastClass機制

public class MayiktFastclass {

    /**
     * 根據索引查找到目標方法
     *
     * @param index
     * @param obj
     * @param args
     * @return
     */
    public static Object invoke(int index, Object obj, Object[] args) {
        OrderServiceImpl orderService = (OrderServiceImpl) obj;
        switch (index) {
            case 1:
                return orderService.addOrder(String.valueOf(args[0]), String.valueOf(args[1]));
        }
        return null;
    }

    /**
     * 根據簽名簡歷索引文件
     *
     * @param sign
     * @return
     */
    public static int getIndex(String sign) {
        switch (sign.hashCode()) {
            case 1763340254:
                return 1;
            case 20:
                return 2;
        }
        return -1;
    }

    public static void main(String[] args) {
        System.out.println("addOrder()String,String".hashCode());
        Object result = invoke(getIndex("addOrder()String,String"), new OrderServiceImpl(), new String[]{"mayikt", "meite"});
    }
}

 

 

Jdk與Cglib動態代理的區別

  1. Jdk動態代理利用反射技術生成匿名的代理類走InvokeHandler回調方法實現加強,同時也是一種基於接口的方式實現代理。
  2. Cglib動態代理利用asm字節碼技術生成一個子類覆蓋其中的方法實現加強,同時採用fastClass機制對整個代理類創建索引比反射效率要高
  3. 在Spring中若是須要被代理的對象若是實現了接口採用Jdk動態代理,沒有實現接口則使用Cglib動態代理。
相關文章
相關標籤/搜索