java靜態代理和動態代理分析

一直想分享一些技術,爲多變的技術帶來一些分享,之後會慢慢的帶來一些本身學過的技術,和本身的一些心得,最近想分享effect java,設計模式,併發編程,一些java集合源碼和併發包源碼,jvm,mybatis源碼,nio,nio2,netty,grpc,和一些數據結構算法,mysql,githtml

今天就先來分析靜態代理和動態代理java

1、靜態代理模式

        假設有個這樣的場景,想對一個Source類進行增強,好比在不改變Source類的基礎,執行Source類的method()方法執行先後打印一些日記,實現步驟mysql

  • 首先有個接口

    public interface Sourceable {
    
        void method();
    
    }複製代碼

  • 再者實現接口的Source類

    public class Source implements Sourceable {
    
        @Override
        public void method() {
            System.out.println("the original method");
        }
    }複製代碼

  • 而後再有個代理類,一樣也實現Sourceable接口

    public class Proxy implements Sourceable {
    
        private Sourceable source;
    
        public Proxy(Source source) {
            this.source = source;
        }
    
        @Override
        public void method() {
            before();
            source.method();
            after();
        }
        
        private void before() {
            System.out.println("method() run begin");
        }
    
        private void after() {
            System.out.println("method() run end");
        }
    }複製代碼

  • 最後測試下

    public class ProxyTest {
    
        public static void main(String[] args) {
    
            Source source = new Source();
    
            Proxy proxy = new Proxy(source);
    
            proxy.method();
        }
    
    }複製代碼

  • 結果

    method() run begin
    the original method
    method() run end
    複製代碼

2、動態代理模式

          咱們常常在使用mybatis的時候mapper只是個接口,調用接口方法就能執行以下git

IUserMapper userMapper = session.getMapper(IUserMapper.class);//獲取接口,已是代理接口 
userMapper.getById(1);//調用mybatis內部的MapperProxy類的invoke
複製代碼

咱們先來看下核心的Proxy.newProxyInstance(arg1,arg2,arg3)方法算法

(ServiceInterface) Proxy.newProxyInstance(ServiceInterface.class.getClassLoader(), serviceInterface.getClass().getInterfaces(), InvocationHandler);複製代碼

第一個參數arg1是接口的classloader,就是爲了加載動態生成的代理類,arg2是接口,爲了生成的代理類實現此接口,擁有實現此接口的方法,最後一個參數arg3,InvocationHandler,目的是生成的代理類對象,執行方法時,調用此內部的invoke()方法sql

接下來咱們開始實現它,有兩種狀況,一種是接口有實現類,咱們在內部直接用反射進行調用實現類方法,第二種就是mybatis實現方式,只有接口沒有實現類,咱們先實現由實現類的方式編程

  • 首先有個ServiceInterface接口

    public interface ServiceInterface {
    
        void println();
    
    }複製代碼

  • 再者有個接口實現類

    public class ServiceInterfaceImpl implements ServiceInterface {
        @Override
        public void println() {
            System.out.println("ServiceInterfaceImpl");
        }
    }複製代碼

  • 接下來看下重要的handler,invoke有三個參數第一個動態生成的代理類,第二個參數是要執行的方法,第三個參數是方法參數

public class MyInvocationHandler implements java.lang.reflect.InvocationHandler {

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("2222");
        method.invoke(target, args);
        System.out.println("3333");
        return null;
    }
}複製代碼

  • 最後測試

    public class ProxyTest {
    
        public static void main(String[] args) {
            ServiceInterface serviceInterface = new ServiceInterfaceImpl();
            InvocationHandler InvocationHandler = new MyInvocationHandler(serviceInterface);
            ServiceInterface proxy = (ServiceInterface) Proxy.newProxyInstance(ServiceInterface.class.getClassLoader(), serviceInterface.getClass().getInterfaces(), InvocationHandler);
            proxy.println();
        }
    
    }複製代碼

咱們再來看下第二種方式只有接口沒有實現類設計模式

  • 一樣首先有個接口

    public interface ServiceInterface {
    
        void println();
    
    }複製代碼

  • 再者

public class MyInvocationHandler implements java.lang.reflect.InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("2222");
        System.out.println("3333");
        return null;
    }
}複製代碼

  • 最後測試

    public class ProxyTest {
    
        public static void main(String[] args) {
            InvocationHandler InvocationHandler = new MyInvocationHandler();
            ServiceInterface proxy = (ServiceInterface) Proxy.newProxyInstance(ServiceInterface.class.getClassLoader(), new Class[]{ServiceInterface.class}, InvocationHandler);
            proxy.println();
        }
    
    }複製代碼

3、總結

        流程是調用Proxy.newProxyInstance()方法會動態生成實現傳入的第二個參數的接口,而後第三個參數是handler,調用proxy.println(),其實動態類println()方法實際上是調handler的invoke方法,咱們接下來看下反編譯後的動態類bash

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) fieldsfirst 

package com.sun.proxy;

import java.lang.reflect.*;
import proxyStudy.ServiceInterface;

public final class $Proxy0 extends Proxy
    implements ServiceInterface
{

    private static Method m3;
    private static Method m1;
    private static Method m0;
    private static Method m2;

    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

    public final void println()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    static 
    {
        try
        {
            m3 = Class.forName("proxyStudy.ServiceInterface").getMethod("println", new Class[0]);
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}
複製代碼
相關文章
相關標籤/搜索