<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>动态代理 &#8211; ChaBug安全</title>
	<atom:link href="/tags/%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/feed" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>一个分享知识、结识伙伴、资源共享的博客</description>
	<lastBuildDate>Mon, 13 Apr 2020 01:37:29 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.5.5</generator>
	<item>
		<title>Java代理模式学习</title>
		<link>/audit/1468.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 09 Apr 2020 03:38:56 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[代理模式]]></category>
		<category><![CDATA[动态代理]]></category>
		<category><![CDATA[静态代理]]></category>
		<guid isPermaLink="false">/?p=1468</guid>

					<description><![CDATA[被问到了，就补一下。 简述代理模式 代理是英文(Proxy)翻译过来的，在我们的实际生活中，最常见的代理模式应该是朋友圈中的微商了。在起初的时候，厂商直接对标顾客，没有微商在中间赚...]]></description>
										<content:encoded><![CDATA[<p>被问到了，就补一下。<br />
<span id="more-1468"></span></p>
<h2>简述<span class="wpcom_tag_link"><a href="/tags/%e4%bb%a3%e7%90%86%e6%a8%a1%e5%bc%8f" title="代理模式" target="_blank">代理模式</a></span></h2>
<p>代理是英文(Proxy)翻译过来的，在我们的实际生活中，最常见的代理模式应该是朋友圈中的微商了。在起初的时候，厂商直接对标顾客，没有微商在中间赚差价，结构就是如图。<br />
<img src="https://y4er.com/img/uploads/20200413098650.png" alt="image" /><br />
之后慢慢的微商汇总了优质资源，顾客没必要自己去挑厂商货比三家，只需要从微商那里买就行了。所有就有了如下的结构：<br />
<img src="https://y4er.com/img/uploads/20200413093796.png" alt="image" /><br />
而程序设计都是从生活中的实例出现的，所以Java中也产生了代理模式。</p>
<p>在Java中存在几种代理模式：<br />
&#8211; <span class="wpcom_tag_link"><a href="/tags/%e9%9d%99%e6%80%81%e4%bb%a3%e7%90%86" title="静态代理" target="_blank">静态代理</a></span><br />
&#8211; <span class="wpcom_tag_link"><a href="/tags/%e5%8a%a8%e6%80%81%e4%bb%a3%e7%90%86" title="动态代理" target="_blank">动态代理</a></span></p>
<p>本文将一一介绍</p>
<h2>静态代理</h2>
<p>当我们看到了微商不厌其烦刷朋友圈的广告，耐不住优惠从微商那里买了一双，微商可能还会向我们推一些别的优惠活动之类的，那么这个过程我们用代码模拟一下。</p>
<p>首先我们需要有一个鞋子的接口，通用的接口是代理模式实现的基础。定义一个sell接口，代表鞋子可以被卖出去的能力。</p>
<pre><code class="language-java ">package com.proxy.weishang;

// 定义鞋子接口
public interface Shoes {
    void sell();
}
</code></pre>
<p>然后再来一个真正的鞋子类，需要实现Shoes的接口</p>
<pre><code class="language-java ">package com.proxy.weishang;

// 真正的鞋子类
public class RealShoes implements Shoes {
    @Override
    public void sell() {
        System.out.println("卖出了一双鞋子哦，美汁汁~");
    }
}
</code></pre>
<p>和一个代理类(微商类)</p>
<pre><code class="language-java ">package com.proxy.weishang;

// 微商 代理
public class MicroSell implements Shoes {
    RealShoes realShoes;

    public MicroSell(RealShoes realShoes) {
        this.realShoes = realShoes;
    }

    public void setRealShoes(RealShoes realShoes) {
        this.realShoes = realShoes;
    }


    @Override
    public void sell() {
        beforeSell();
        realShoes.sell();
        afterSell();
    }

    public void beforeSell() {
        System.out.println("买之前宣传：帅哥，买双鞋子吗？高仿阿迪199两双");
    }

    public void afterSell() {
        System.out.println("买之后推销：帅哥，再来双高仿耐克？");
    }
}
</code></pre>
<p>接下来我们测试下</p>
<pre><code class="language-java ">package com.proxy;

import com.proxy.weishang.MicroSell;
import com.proxy.weishang.RealShoes;

public class Main {

    public static void main(String[] args) {
        MicroSell microSell = new MicroSell(new RealShoes());
        microSell.sell();
    }
}
</code></pre>
<p><img src="https://y4er.com/img/uploads/20200413091819.png" alt="image" /><br />
在微商卖出鞋子的前后，执行了<code>beforeSell</code>和<code>afterSell</code>疯狂推销，那么使用代理模式的好处就在于这，<strong>从之前最基本的厂商对顾客，鞋子只有单一的sell能力，而使用代理模式之后，我们并没有改变鞋子的sell能力就可以对其进行功能的拓展和附加</strong>。</p>
<p>总结一下静态代理的优点：<br />
1. 无需修改被代理的对象<br />
2. 无损拓展功能<br />
3. 解耦合</p>
<p>缺点当然也存在：<br />
1. 要为每一个接口实现代理类，一旦接口增加方法，目标对象与代理对象都要维护。</p>
<h2>动态代理</h2>
<p>动态代理也是代理，他和静态代理的功能和目的是没有区别的，唯一的区别就在于动态代理是动态生成的，省去为接口实现代理类的操作。</p>
<h3>何为动态生成</h3>
<p>其实就是Java在内存中创建了一个实现接口的代理，而不需要我们自己定义。多说无益，看代码</p>
<p>我们仍然使用微商的例子来进行讲解。当微商想卖你莆田耐克的时候：</p>
<pre><code class="language-java ">package com.proxy.dynamicProxy;

// 定义鞋子接口
public interface Shoes {
    void sell();
}
</code></pre>
<pre><code class="language-java ">package com.proxy.dynamicProxy;

// Nike鞋
public class NikeShoes implements Shoes {
    @Override
    public void sell() {
        System.out.println("卖出去一双莆田耐克，美滋滋~");
    }
}
</code></pre>
<pre><code class="language-java ">package com.proxy.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class dynamicMicroSell implements InvocationHandler {
    Object shoes;

    public dynamicMicroSell(Object shoes) {
        this.shoes = shoes;
    }

    public void setShoes(Object shoes) {
        this.shoes = shoes;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("推销前：美女莆田阿迪来一双？");
        method.invoke(shoes, args);
        System.out.println("推销后：耐克要吗？");
        return null;
    }
}
</code></pre>
<p>测试下</p>
<pre><code class="language-java ">package com.proxy;

import com.proxy.dynamicProxy.AdidasShoes;
import com.proxy.dynamicProxy.NikeShoes;
import com.proxy.dynamicProxy.Shoes;
import com.proxy.dynamicProxy.dynamicMicroSell;

import java.lang.reflect.Proxy;

public class Main {

    public static void main(String[] args) {
        NikeShoes nikeShoes = new NikeShoes();
        dynamicMicroSell nikeSeller = new dynamicMicroSell(nikeShoes);
        Shoes nikeProxy = (Shoes) Proxy.newProxyInstance(NikeShoes.class.getClassLoader(), NikeShoes.class.getInterfaces(), nikeSeller);
        nikeProxy.sell();
    }
}
</code></pre>
<p><img src="https://y4er.com/img/uploads/20200413098331.png" alt="image" /><br />
可以看到我并没有像静态代理那样重新实现一个代理类，而是实现了 <code>InvocationHandler</code> 接口的invoke方法实现的代理。通过<code>Proxy.newProxyInstance()</code>创建了一个代理类来执行sell方法。</p>
<p>先不说InvocationHandler到底是什么东西，我们此时如果想要拓展一个阿迪鞋子的接口，应该怎么用动态代理实现？很简单，新建一个AdidasShoes还是实现Shoes接口</p>
<pre><code class="language-java ">package com.proxy.dynamicProxy;

public class AdidasShoes implements Shoes {
    @Override
    public void sell() {
        System.out.println("卖出去一双莆田阿迪，美滋滋~");
    }
}
</code></pre>
<p>其他不需要变化，在main中通过dynamicMicroSell和Proxy.newProxyInstance()动态生成代理类就可以了</p>
<pre><code class="language-java ">package com.proxy;

import com.proxy.dynamicProxy.AdidasShoes;
import com.proxy.dynamicProxy.NikeShoes;
import com.proxy.dynamicProxy.Shoes;
import com.proxy.dynamicProxy.dynamicMicroSell;

import java.lang.reflect.Proxy;

public class Main {

    public static void main(String[] args) {
        NikeShoes nikeShoes = new NikeShoes();
        dynamicMicroSell nikeSeller = new dynamicMicroSell(nikeShoes);
        Shoes nikeProxy = (Shoes) Proxy.newProxyInstance(NikeShoes.class.getClassLoader(), NikeShoes.class.getInterfaces(), nikeSeller);
        nikeProxy.sell();

        AdidasShoes adidasShoes = new AdidasShoes();
        dynamicMicroSell adidasSeller = new dynamicMicroSell(adidasShoes);
        Shoes adidasProxy = (Shoes) Proxy.newProxyInstance(AdidasShoes.class.getClassLoader(), NikeShoes.class.getInterfaces(), adidasSeller);
        adidasProxy.sell();

    }
}
</code></pre>
<p>运行如图<br />
<img src="https://y4er.com/img/uploads/20200413094565.png" alt="image" /></p>
<p><strong>动态代理的优点是很明显的，它不需要为每一个接口都创建代理类，大大减少重复工作。</strong></p>
<h3>动态代理的秘密</h3>
<p>在我们使用静态代理的时候，是通过<code>new MicroSell()</code>创建代理实例，动态代理肯定也有创建实例的动作，要找到在哪里创建了代理实例，我们需要跟进到Proxy.newProxyInstance()一探究竟</p>
<pre><code class="language-java ">public static Object newProxyInstance(ClassLoader loader,
                                      Class&lt;?&gt;[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class&lt;?&gt;[] 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&lt;?&gt; cl = getProxyClass0(loader, intfs);

    /*
         * Invoke its constructor with the designated invocation handler.
         */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor&lt;?&gt; cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction&lt;Void&gt;() {
                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);
    }
}
</code></pre>
<p>可以看到通过cl这个class反射调用其构造函数返回了一个实例</p>
<pre><code class="language-java ">private static Class&lt;?&gt; getProxyClass0(ClassLoader loader,
                                       Class&lt;?&gt;... interfaces) {
    if (interfaces.length &gt; 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}
</code></pre>
<p>直接通过缓存获取，如果获取不到，注释说会通过 ProxyClassFactory 生成。</p>
<pre><code class="language-java ">private static final class ProxyClassFactory
    implements BiFunction&lt;ClassLoader, Class&lt;?&gt;[], Class&lt;?&gt;&gt;
{
    // prefix for all proxy class names
    private static final String proxyClassNamePrefix = "$Proxy";

    // next number to use for generation of unique proxy class names
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class&lt;?&gt; apply(ClassLoader loader, Class&lt;?&gt;[] interfaces) {

        Map&lt;Class&lt;?&gt;, Boolean&gt; interfaceSet = new IdentityHashMap&lt;&gt;(interfaces.length);
        for (Class&lt;?&gt; intf : interfaces) {
            /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
            Class&lt;?&gt; interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
                 * Verify that this interface is not a duplicate.
                 */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
        for (Class&lt;?&gt; intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }

        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
             * Choose a name for the proxy class to generate.
             */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
             * Generate the specified proxy class.
             */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
            throw new IllegalArgumentException(e.toString());
        }
    }
}
</code></pre>
<p>可知代理类名为<code>String proxyName = proxyPkg + proxyClassNamePrefix + num</code>，即<strong>包名+$Proxy+id序号</strong></p>
<p>生成代理类的核心代码</p>
<pre><code class="language-java ">byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
</code></pre>
<p><img src="https://y4er.com/img/uploads/20200413094247.png" alt="image" /></p>
<p>通过修改Java字节码的形式定义class，这就是动态代理自动生成的秘密。来看下动态代理的类名<br />
<img src="https://y4er.com/img/uploads/20200413099929.png" alt="image" /></p>
<h2>总结</h2>
<p>代理模式被运用于spring框架的aop面向切面编程中，个人业务需求可以应用在日志记录、性能统计等场景中。</p>
<p>最后几句话总结下：<br />
1. 代理模式的好处在于不修改现有代码的基础上进行拓展功能<br />
2. 不管是动态还是静态代理都要实现接口，本质是面向接口编程<br />
3. 静态代理需要自己实现Proxy类，动态由Proxy.newInstance()反射动态生成<br />
4. 两者区别在于是否需要自己手动实现Proxy类</p>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
