代理模式详解

第一节 代理模式介绍

1. 什么是代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.

代理模式分为静态代理和动态代理两种

2. 静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者继承相同的父类

2.1 案例

使用静态代理完成苹果手机专卖店代理苹果手机批发商卖手机的这一过程

  • 定义接口

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
    ​
    <span style="color:#770088">public</span> <span style="color:#770088">interface</span> <span style="color:#0000ff">Seller</span> {
        <span style="color:#aa5500">/**</span>
         <span style="color:#aa5500">* 卖手机</span>
         <span style="color:#aa5500">* @param price</span>
         <span style="color:#aa5500">*/</span>
        <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>);
    }</span>
  • 定义苹果手机批发商

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* 苹果手机批发商A</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneWholesaler</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
    ​
        <span style="color:#555555">@Override</span>
        <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"苹果手机批发商A卖手机价格为:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">price</span>);
        }
    }</span>
  • 定义苹果手机专卖店

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* 苹果手机专卖店A代理卖苹果手机</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreA</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
    ​
        <span style="color:#770088">private</span>  <span style="color:#000000">IPhoneWholesaler</span> <span style="color:#000000">wholesaler</span>;
    ​
        <span style="color:#770088">public</span> <span style="color:#000000">IPhoneSpecialtyStoreA</span>(<span style="color:#000000">IPhoneWholesaler</span> <span style="color:#000000">wholesaler</span>){
            <span style="color:#770088">this</span>.<span style="color:#000000">wholesaler</span> <span style="color:#981a1a">=</span> <span style="color:#000000">wholesaler</span>;
        }
    ​
        <span style="color:#555555">@Override</span>
        <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
            <span style="color:#008855">double</span> <span style="color:#000000">profit</span> <span style="color:#981a1a">=</span> <span style="color:#116644">1000</span>;
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理盈利:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>);
            <span style="color:#000000">wholesaler</span>.<span style="color:#000000">sellPhoneX</span>(<span style="color:#000000">price</span>);
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"卖出价格:"</span> <span style="color:#981a1a">+</span> (<span style="color:#000000">price</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>));
        }
    }</span>
  • 编写测试案例

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
    ​
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreATest</span> {
    ​
        <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
            <span style="color:#000000">IPhoneWholesaler</span> <span style="color:#000000">wholesaler</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">IPhoneWholesaler</span>();
            <span style="color:#000000">IPhoneSpecialtyStoreA</span> <span style="color:#000000">specialtyStoreA</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">IPhoneSpecialtyStoreA</span>(<span style="color:#000000">wholesaler</span>);
            <span style="color:#000000">specialtyStoreA</span>.<span style="color:#000000">sellPhoneX</span>(<span style="color:#116644">12000</span>);
        }
    }</span>

结论: 静态代理可以在不改变原有功能的基础上,对功能进行增强。

2.2 静态代理引发的问题

假如现在又有一家专卖店准备代理售卖苹果手机,那么会出现如下的编码

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 苹果手机专卖店A代理卖苹果手机</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreB</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
​
    <span style="color:#770088">private</span>  <span style="color:#000000">IPhoneWholesaler</span> <span style="color:#000000">wholesaler</span>;
​
    <span style="color:#770088">public</span> <span style="color:#000000">IPhoneSpecialtyStoreB</span>(<span style="color:#000000">IPhoneWholesaler</span> <span style="color:#000000">wholesaler</span>){
        <span style="color:#770088">this</span>.<span style="color:#000000">wholesaler</span> <span style="color:#981a1a">=</span> <span style="color:#000000">wholesaler</span>;
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
        <span style="color:#008855">double</span> <span style="color:#000000">profit</span> <span style="color:#981a1a">=</span> <span style="color:#116644">1200</span>;
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理盈利:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>);
        <span style="color:#000000">wholesaler</span>.<span style="color:#000000">sellPhoneX</span>(<span style="color:#000000">price</span>);
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"卖出价格:"</span> <span style="color:#981a1a">+</span> (<span style="color:#000000">price</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>));
    }
}</span></span>

如果有更多的专卖店来代理售卖苹果手机,那么这样的编码会反复出现多次。一旦批发商发生改变或者售卖产品发生改变,代理商也必须跟随者做出相应的改变。

如何应对这种变化呢?

使用动态代理。

2.3 静态代理 >> 动态代理

改变代理对象的方法调用思路

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">InvocationTargetException</span>;
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">Method</span>;
​
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 苹果手机专卖店A代理卖苹果手机</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreA</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
​
    <span style="color:#770088">private</span>  <span style="color:#000000">IPhoneWholesaler</span> <span style="color:#000000">wholesaler</span>;
​
    <span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#000000">Method</span> <span style="color:#000000">m</span>;
    <span style="color:#770088">static</span> {
        <span style="color:#770088">try</span> {
            <span style="color:#000000">m</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Class</span>.<span style="color:#000000">forName</span>(<span style="color:#aa1111">"com.qf.proxy._static.Seller"</span>).<span style="color:#000000">getMethod</span>(<span style="color:#aa1111">"sellPhoneX"</span>, <span style="color:#008855">double</span>.<span style="color:#770088">class</span>);
        } <span style="color:#770088">catch</span> (<span style="color:#000000">NoSuchMethodException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        } <span style="color:#770088">catch</span> (<span style="color:#000000">ClassNotFoundException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        }
    }
​
    <span style="color:#770088">public</span> <span style="color:#000000">IPhoneSpecialtyStoreA</span>(<span style="color:#000000">IPhoneWholesaler</span> <span style="color:#000000">wholesaler</span>){
        <span style="color:#770088">this</span>.<span style="color:#000000">wholesaler</span> <span style="color:#981a1a">=</span> <span style="color:#000000">wholesaler</span>;
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
        <span style="color:#008855">double</span> <span style="color:#000000">profit</span> <span style="color:#981a1a">=</span> <span style="color:#116644">1000</span>;
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理盈利:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>);
        <span style="color:#770088">try</span> {
            <span style="color:#000000">m</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">wholesaler</span>, <span style="color:#000000">price</span>);
        } <span style="color:#770088">catch</span> (<span style="color:#000000">IllegalAccessException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        } <span style="color:#770088">catch</span> (<span style="color:#000000">InvocationTargetException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        }
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"卖出价格:"</span> <span style="color:#981a1a">+</span> (<span style="color:#000000">price</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>));
    }
}</span></span>

这里就出现了反射中 Method 对象,如果能够将 Method 对象的调用及前后实现的功能增强交给用户来实现,那么代理模式就可以为任何对象做代理了。

要实现用户自己定义增强功能,只能以接口的形式来定义,因为接口就是定义规则,用户遵守这个规则即可。那么如何来定义规则呢?分析代码不难发现,Method 对象的调用无非需要两个参数,一个是代理对象,一个是方法执行的参数。因为代理对象会变化,由用户自行决定,因此可以不考虑。那么就只剩下方法的参数了。而 Method 对象的调用也应该在用户的实现过程中,因此,Method对象也应该作为接口考虑的点。所以接口可以按下面的方式来定义:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">interface</span> <span style="color:#0000ff">MethodInvocationHandler</span> {
​
    <span style="color:#008855">Object</span> <span style="color:#000000">invoke</span>(<span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>) <span style="color:#770088">throws</span> <span style="color:#000000">IllegalArgumentException</span>;
}</span></span>

此时使用 MethodInvocationHandler 来实现代理

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">Method</span>;
​
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 苹果手机专卖店A代理卖苹果手机</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreA</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
​
    <span style="color:#770088">private</span>  <span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>;
​
    <span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#000000">Method</span> <span style="color:#000000">m</span>;
    <span style="color:#770088">static</span> {
        <span style="color:#770088">try</span> {
            <span style="color:#000000">m</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Class</span>.<span style="color:#000000">forName</span>(<span style="color:#aa1111">"com.qf.proxy._static.Seller"</span>).<span style="color:#000000">getMethod</span>(<span style="color:#aa1111">"sellPhoneX"</span>, <span style="color:#008855">double</span>.<span style="color:#770088">class</span>);
        } <span style="color:#770088">catch</span> (<span style="color:#000000">NoSuchMethodException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        } <span style="color:#770088">catch</span> (<span style="color:#000000">ClassNotFoundException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        }
    }
​
    <span style="color:#770088">public</span> <span style="color:#000000">IPhoneSpecialtyStoreA</span>(<span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>){
        <span style="color:#770088">this</span>.<span style="color:#000000">handler</span> <span style="color:#981a1a">=</span> <span style="color:#000000">handler</span>;
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
        <span style="color:#000000">handler</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">m</span>, <span style="color:#770088">new</span> <span style="color:#008855">Object</span>[]{ <span style="color:#000000">price</span> });
    }
}</span></span>

修改测试案例

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">InvocationTargetException</span>;
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">Method</span>;
​
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreATest</span> {
​
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
        <span style="color:#000000">IPhoneWholesaler</span> <span style="color:#000000">wholesalerA</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">IPhoneWholesaler</span>();
        <span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">MethodInvocationHandler</span>() {
            <span style="color:#555555">@Override</span>
            <span style="color:#770088">public</span> <span style="color:#008855">Object</span> <span style="color:#000000">invoke</span>(<span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>) <span style="color:#770088">throws</span> <span style="color:#000000">IllegalArgumentException</span> {
                <span style="color:#008855">double</span> <span style="color:#000000">profit</span> <span style="color:#981a1a">=</span> <span style="color:#116644">1000</span>;
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理盈利:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>);
                <span style="color:#008855">Object</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#221199">null</span>;
                <span style="color:#770088">try</span> {
                    <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">wholesalerA</span>, <span style="color:#000000">args</span>);
                } <span style="color:#770088">catch</span> (<span style="color:#000000">IllegalAccessException</span> <span style="color:#981a1a">|</span> <span style="color:#000000">InvocationTargetException</span> <span style="color:#000000">e</span>) {
                    <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
                }
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"卖出价格:"</span> <span style="color:#981a1a">+</span> ((<span style="color:#008855">double</span>)<span style="color:#000000">args</span>[<span style="color:#116644">0</span>] <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>));
                <span style="color:#770088">return</span> <span style="color:#000000">result</span>;
            }
        };
        <span style="color:#000000">IPhoneSpecialtyStoreA</span> <span style="color:#000000">specialtyStoreA</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">IPhoneSpecialtyStoreA</span>(<span style="color:#000000">handler</span>);
        <span style="color:#000000">specialtyStoreA</span>.<span style="color:#000000">sellPhoneX</span>(<span style="color:#116644">12000</span>);
    }
}</span></span>

批发商改变

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 苹果手机批发商A</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneWholesalerNew</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
        <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"苹果手机批发商[NEW]卖手机价格为:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">price</span>);
    }
}</span></span>

再次修改测试案例

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">InvocationTargetException</span>;
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">Method</span>;
​
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreATest</span> {
​
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
<span style="color:#aa5500">//        IPhoneWholesaler wholesalerA = new IPhoneWholesaler();</span>
        <span style="color:#000000">IPhoneWholesalerNew</span> <span style="color:#000000">wholesalerNew</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">IPhoneWholesalerNew</span>();
        <span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">MethodInvocationHandler</span>() {
            <span style="color:#555555">@Override</span>
            <span style="color:#770088">public</span> <span style="color:#008855">Object</span> <span style="color:#000000">invoke</span>(<span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>) <span style="color:#770088">throws</span> <span style="color:#000000">IllegalArgumentException</span> {
                <span style="color:#008855">double</span> <span style="color:#000000">profit</span> <span style="color:#981a1a">=</span> <span style="color:#116644">1000</span>;
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理盈利:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>);
                <span style="color:#008855">Object</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#221199">null</span>;
                <span style="color:#770088">try</span> {
                    <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">wholesalerNew</span>, <span style="color:#000000">args</span>);
                } <span style="color:#770088">catch</span> (<span style="color:#000000">IllegalAccessException</span> <span style="color:#981a1a">|</span> <span style="color:#000000">InvocationTargetException</span> <span style="color:#000000">e</span>) {
                    <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
                }
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"卖出价格:"</span> <span style="color:#981a1a">+</span> ((<span style="color:#008855">double</span>)<span style="color:#000000">args</span>[<span style="color:#116644">0</span>] <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>));
                <span style="color:#770088">return</span> <span style="color:#000000">result</span>;
            }
        };
        <span style="color:#000000">IPhoneSpecialtyStoreA</span> <span style="color:#000000">specialtyStoreA</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">IPhoneSpecialtyStoreA</span>(<span style="color:#000000">handler</span>);
        <span style="color:#000000">specialtyStoreA</span>.<span style="color:#000000">sellPhoneX</span>(<span style="color:#116644">12000</span>);
    }
}</span></span>

到此,批发商改变问题就已经得到解决。

那么接下来需要考虑的就是大量的专卖店开进行代理售卖苹果手机的问题。那么就会产生大量的代理类。例如:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">Method</span>;
​
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 苹果手机专卖店B代理卖苹果手机</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreB</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
​
    <span style="color:#770088">private</span>  <span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>;
​
    <span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#000000">Method</span> <span style="color:#000000">m</span>;
    <span style="color:#770088">static</span> {
        <span style="color:#770088">try</span> {
            <span style="color:#000000">m</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Class</span>.<span style="color:#000000">forName</span>(<span style="color:#aa1111">"com.qf.proxy._static.Seller"</span>).<span style="color:#000000">getMethod</span>(<span style="color:#aa1111">"sellPhoneX"</span>, <span style="color:#008855">double</span>.<span style="color:#770088">class</span>);
        } <span style="color:#770088">catch</span> (<span style="color:#000000">NoSuchMethodException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        } <span style="color:#770088">catch</span> (<span style="color:#000000">ClassNotFoundException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        }
    }
​
    <span style="color:#770088">public</span> <span style="color:#000000">IPhoneSpecialtyStoreB</span>(<span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>){
        <span style="color:#770088">this</span>.<span style="color:#000000">handler</span> <span style="color:#981a1a">=</span> <span style="color:#000000">handler</span>;
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
        <span style="color:#000000">handler</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">m</span>, <span style="color:#770088">new</span> <span style="color:#008855">Object</span>[]{ <span style="color:#000000">price</span> });
    }
}</span></span>

这些类都是属于代理,都使用了 MethodInvocationHandler 来完成代理的功能。可以将这些共有的功能抽取到父类中。

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
<span style="color:#aa5500">//因为是给代理继承使用,因此父类命名带有Proxy,创建出来的对象自然称为代理</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">MyProxy</span> {
​
    <span style="color:#770088">protected</span> <span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>;
​
    <span style="color:#770088">protected</span> <span style="color:#000000">MyProxy</span>(<span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>) {
        <span style="color:#770088">this</span>.<span style="color:#000000">handler</span> <span style="color:#981a1a">=</span> <span style="color:#000000">handler</span>;
    }
}
​
<span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">Method</span>;
​
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 苹果手机专卖店A代理卖苹果手机</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreA</span> <span style="color:#770088">extends</span> <span style="color:#000000">MyProxy</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
​
    <span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#000000">Method</span> <span style="color:#000000">m</span>;
    <span style="color:#770088">static</span> {
        <span style="color:#770088">try</span> {
            <span style="color:#000000">m</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Class</span>.<span style="color:#000000">forName</span>(<span style="color:#aa1111">"com.qf.proxy._static.Seller"</span>).<span style="color:#000000">getMethod</span>(<span style="color:#aa1111">"sellPhoneX"</span>, <span style="color:#008855">double</span>.<span style="color:#770088">class</span>);
        } <span style="color:#770088">catch</span> (<span style="color:#000000">NoSuchMethodException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        } <span style="color:#770088">catch</span> (<span style="color:#000000">ClassNotFoundException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        }
    }
​
    <span style="color:#770088">public</span> <span style="color:#000000">IPhoneSpecialtyStoreA</span>(<span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>){
        <span style="color:#770088">super</span>(<span style="color:#000000">handler</span>);
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
        <span style="color:#000000">handler</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">m</span>, <span style="color:#770088">new</span> <span style="color:#008855">Object</span>[]{ <span style="color:#000000">price</span> });
    }
}
​
<span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">Method</span>;
​
<span style="color:#aa5500">/**</span>
 <span style="color:#aa5500">* 苹果手机专卖店B代理卖苹果手机</span>
 <span style="color:#aa5500">*/</span>
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">IPhoneSpecialtyStoreB</span> <span style="color:#770088">extends</span> <span style="color:#000000">MyProxy</span> <span style="color:#770088">implements</span> <span style="color:#000000">Seller</span>{
​
    <span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#000000">Method</span> <span style="color:#000000">m</span>;
    <span style="color:#770088">static</span> {
        <span style="color:#770088">try</span> {
            <span style="color:#000000">m</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Class</span>.<span style="color:#000000">forName</span>(<span style="color:#aa1111">"com.qf.proxy._static.Seller"</span>).<span style="color:#000000">getMethod</span>(<span style="color:#aa1111">"sellPhoneX"</span>, <span style="color:#008855">double</span>.<span style="color:#770088">class</span>);
        } <span style="color:#770088">catch</span> (<span style="color:#000000">NoSuchMethodException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        } <span style="color:#770088">catch</span> (<span style="color:#000000">ClassNotFoundException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        }
    }
​
    <span style="color:#770088">public</span> <span style="color:#000000">IPhoneSpecialtyStoreB</span>(<span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>){
        <span style="color:#770088">super</span>(<span style="color:#000000">handler</span>);
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">sellPhoneX</span>(<span style="color:#008855">double</span> <span style="color:#000000">price</span>) {
        <span style="color:#000000">handler</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">m</span>, <span style="color:#770088">new</span> <span style="color:#008855">Object</span>[]{ <span style="color:#000000">price</span> });
    }
}</span></span>

共有的功能抽取只是解决代码冗余问题,并没有解决上面提到的代理繁多的问题。如果这些代理类能够

使用代码来生成,然后再编译,再加载至 JVM 中,那么再多的代理也就不是问题了。

参照 IPhoneSpecialtyStoreA 来编写代理类生成代码,在 MyProxy 中编写如下方法:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#008855">String</span> <span style="color:#0000ff">generateProxyClass</span>(<span style="color:#000000">Class</span><span style="color:#981a1a"><?></span> <span style="color:#000000">clazz</span>){
    <span style="color:#770088">if</span>(<span style="color:#981a1a">!</span><span style="color:#000000">clazz</span>.<span style="color:#000000">isInterface</span>()) <span style="color:#770088">throw</span> <span style="color:#770088">new</span> <span style="color:#000000">IllegalArgumentException</span>(<span style="color:#000000">clazz</span>.<span style="color:#000000">getName</span>() <span style="color:#981a1a">+</span> <span style="color:#aa1111">" 不是接口"</span>);
    <span style="color:#008855">StringBuilder</span> <span style="color:#000000">builder</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#008855">StringBuilder</span>();
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"package "</span>).<span style="color:#000000">append</span>(<span style="color:#000000">clazz</span>.<span style="color:#000000">getPackage</span>().<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">";\n"</span>);
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"import "</span>).<span style="color:#000000">append</span>(<span style="color:#000000">Method</span>.<span style="color:#770088">class</span>.<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">";\n"</span>);
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"import "</span>).<span style="color:#000000">append</span>(<span style="color:#000000">MethodInvocationHandler</span>.<span style="color:#770088">class</span>.<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">";\n"</span>);
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"import "</span>).<span style="color:#000000">append</span>(<span style="color:#000000">MyProxy</span>.<span style="color:#770088">class</span>.<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">";\n"</span>);
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"public class $proxy0 extends MyProxy implements "</span>).<span style="color:#000000">append</span>(<span style="color:#000000">clazz</span>.<span style="color:#000000">getSimpleName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">"{\n"</span>);
    <span style="color:#008855">StringBuilder</span> <span style="color:#000000">staticBuilder</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#008855">StringBuilder</span>();
    <span style="color:#000000">staticBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"static {\n"</span>);
    <span style="color:#000000">staticBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"try {\n"</span>);
    <span style="color:#008855">StringBuilder</span> <span style="color:#000000">overrideMethodBuilder</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#008855">StringBuilder</span>();
    <span style="color:#000000">Method</span>[] <span style="color:#000000">methods</span> <span style="color:#981a1a">=</span> <span style="color:#000000">clazz</span>.<span style="color:#000000">getMethods</span>();
    <span style="color:#770088">for</span>(<span style="color:#008855">int</span> <span style="color:#000000">i</span><span style="color:#981a1a">=</span><span style="color:#116644">0</span>; <span style="color:#000000">i</span><span style="color:#981a1a"><</span><span style="color:#000000">methods</span>.<span style="color:#000000">length</span>; <span style="color:#000000">i</span><span style="color:#981a1a">++</span>){
        <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"private static Method m"</span>).<span style="color:#000000">append</span>(<span style="color:#000000">i</span>).<span style="color:#000000">append</span>(<span style="color:#aa1111">";\n"</span>);
        <span style="color:#000000">staticBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"m"</span>).<span style="color:#000000">append</span>(<span style="color:#000000">i</span>).<span style="color:#000000">append</span>(<span style="color:#aa1111">"=Class.forName(\""</span>).<span style="color:#000000">append</span>(<span style="color:#000000">clazz</span>.<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">"\").getMethod(\""</span>)
            .<span style="color:#000000">append</span>(<span style="color:#000000">methods</span>[<span style="color:#000000">i</span>].<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">"\","</span>);
        <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"\n@Override\n"</span>);
        <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"public "</span>).<span style="color:#000000">append</span>(<span style="color:#000000">methods</span>[<span style="color:#000000">i</span>].<span style="color:#000000">getReturnType</span>().<span style="color:#000000">getSimpleName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">" "</span>).<span style="color:#000000">append</span>(<span style="color:#000000">methods</span>[<span style="color:#000000">i</span>].<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">"("</span>);
        <span style="color:#000000">Parameter</span>[] <span style="color:#000000">parameters</span> <span style="color:#981a1a">=</span> <span style="color:#000000">methods</span>[<span style="color:#000000">i</span>].<span style="color:#000000">getParameters</span>();
        <span style="color:#770088">for</span>(<span style="color:#000000">Parameter</span> <span style="color:#000000">parameter</span> : <span style="color:#000000">parameters</span>){
            <span style="color:#000000">staticBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#000000">parameter</span>.<span style="color:#000000">getType</span>().<span style="color:#000000">getSimpleName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">".class,"</span>);
            <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#000000">parameter</span>.<span style="color:#000000">getType</span>().<span style="color:#000000">getSimpleName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">" "</span>).<span style="color:#000000">append</span>(<span style="color:#000000">parameter</span>.<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">","</span>);
        }
        <span style="color:#000000">staticBuilder</span>.<span style="color:#000000">deleteCharAt</span>(<span style="color:#000000">staticBuilder</span>.<span style="color:#000000">length</span>()<span style="color:#981a1a">-</span><span style="color:#116644">1</span>);
        <span style="color:#000000">staticBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">");\n"</span>);
        <span style="color:#770088">if</span>(<span style="color:#000000">parameters</span>.<span style="color:#000000">length</span> <span style="color:#981a1a">></span> <span style="color:#116644">0</span>)
            <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">deleteCharAt</span>(<span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">length</span>()<span style="color:#981a1a">-</span><span style="color:#116644">1</span>);
        <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"){\n"</span>);
        <span style="color:#000000">Class</span> <span style="color:#000000">returnType</span> <span style="color:#981a1a">=</span> <span style="color:#000000">methods</span>[<span style="color:#000000">i</span>].<span style="color:#000000">getReturnType</span>();
        <span style="color:#770088">if</span>(<span style="color:#000000">returnType</span> <span style="color:#981a1a">!=</span> <span style="color:#008855">Void</span>.<span style="color:#770088">class</span> <span style="color:#981a1a">&&</span> <span style="color:#000000">returnType</span> <span style="color:#981a1a">!=</span> <span style="color:#008855">void</span>.<span style="color:#770088">class</span>)
            <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"return ("</span>).<span style="color:#000000">append</span>(<span style="color:#000000">methods</span>[<span style="color:#000000">i</span>].<span style="color:#000000">getReturnType</span>().<span style="color:#000000">getSimpleName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">")"</span>);
        <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"handler.invoke(m"</span>).<span style="color:#000000">append</span>(<span style="color:#000000">i</span>).<span style="color:#000000">append</span>(<span style="color:#aa1111">",new Object[]{"</span>);
        <span style="color:#770088">for</span>(<span style="color:#000000">Parameter</span> <span style="color:#000000">parameter</span> : <span style="color:#000000">parameters</span>){
            <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#000000">parameter</span>.<span style="color:#000000">getName</span>()).<span style="color:#000000">append</span>(<span style="color:#aa1111">","</span>);
        }
        <span style="color:#770088">if</span>(<span style="color:#000000">parameters</span>.<span style="color:#000000">length</span> <span style="color:#981a1a">></span> <span style="color:#116644">0</span>)
            <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">deleteCharAt</span>(<span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">length</span>()<span style="color:#981a1a">-</span><span style="color:#116644">1</span>);
        <span style="color:#000000">overrideMethodBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"});\n}"</span>);
    }
    <span style="color:#000000">staticBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"} catch (NoSuchMethodException e) {\ne.printStackTrace();\n}catch (ClassNotFoundException e) {\ne.printStackTrace();\n}\n"</span>);
    <span style="color:#000000">staticBuilder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"}\n"</span>);
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#000000">staticBuilder</span>);
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"protected $proxy0(MethodInvocationHandler handler) {\nsuper(handler);\n}\n"</span>);
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#000000">overrideMethodBuilder</span>);
    <span style="color:#000000">builder</span>.<span style="color:#000000">append</span>(<span style="color:#aa1111">"\n}"</span>);
    <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#000000">builder</span>);
    <span style="color:#770088">return</span> <span style="color:#000000">builder</span>.<span style="color:#000000">toString</span>();
}</span></span>

将生成的代理类保存至磁盘中,在 MyProxy 中编写如下方法:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#0000ff">saveProxyClass</span>(<span style="color:#008855">String</span> <span style="color:#000000">proxyClass</span>) <span style="color:#770088">throws</span> <span style="color:#000000">IOException</span> {
    <span style="color:#008855">String</span> <span style="color:#000000">path</span> <span style="color:#981a1a">=</span> <span style="color:#aa1111">"d:\\$proxy0.java"</span>;
    <span style="color:#000000">BufferedWriter</span> <span style="color:#000000">writer</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">BufferedWriter</span>(<span style="color:#770088">new</span> <span style="color:#000000">FileWriter</span>(<span style="color:#000000">path</span>));
    <span style="color:#000000">writer</span>.<span style="color:#000000">write</span>(<span style="color:#000000">proxyClass</span>);
    <span style="color:#000000">writer</span>.<span style="color:#000000">flush</span>();
    <span style="color:#000000">writer</span>.<span style="color:#000000">close</span>();
}</span></span>

编译代理类源文件,在 MyProxy 中编写如下方法:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">private</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#0000ff">compile</span>() <span style="color:#770088">throws</span> <span style="color:#000000">IOException</span> {
    <span style="color:#000000">File</span> <span style="color:#000000">javaFile</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">File</span>(<span style="color:#aa1111">"d:\\$proxy0.java"</span>);
    <span style="color:#000000">JavaCompiler</span> <span style="color:#000000">compiler</span> <span style="color:#981a1a">=</span> <span style="color:#000000">ToolProvider</span>.<span style="color:#000000">getSystemJavaCompiler</span>();
    <span style="color:#000000">DiagnosticCollector</span> <span style="color:#000000">diagnostics</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">DiagnosticCollector</span>();
    <span style="color:#000000">StandardJavaFileManager</span> <span style="color:#000000">fileManager</span> <span style="color:#981a1a">=</span> <span style="color:#000000">compiler</span>.<span style="color:#000000">getStandardFileManager</span>(<span style="color:#000000">diagnostics</span>, <span style="color:#221199">null</span>, <span style="color:#221199">null</span>);
    <span style="color:#000000">Iterable</span><span style="color:#981a1a"><?</span> <span style="color:#770088">extends</span> <span style="color:#000000">JavaFileObject</span><span style="color:#981a1a">></span> <span style="color:#000000">javaFileObjects</span> <span style="color:#981a1a">=</span> <span style="color:#000000">fileManager</span>.<span style="color:#000000">getJavaFileObjects</span>(<span style="color:#000000">javaFile</span>);
    <span style="color:#000000">JavaCompiler</span>.<span style="color:#000000">CompilationTask</span> <span style="color:#000000">task</span> <span style="color:#981a1a">=</span> <span style="color:#000000">compiler</span>.<span style="color:#000000">getTask</span>(<span style="color:#221199">null</span>, <span style="color:#000000">fileManager</span>, <span style="color:#221199">null</span>,<span style="color:#221199">null</span>,<span style="color:#221199">null</span>, <span style="color:#000000">javaFileObjects</span>);
    <span style="color:#000000">task</span>.<span style="color:#000000">call</span>();
    <span style="color:#000000">fileManager</span>.<span style="color:#000000">close</span>();
}</span></span>

加载编译好的代理类,这里需要自定义类加载器

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">io</span>.<span style="color:#981a1a">*</span>;
​
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">MyClassLoader</span> <span style="color:#770088">extends</span> <span style="color:#000000">ClassLoader</span>{
​
    <span style="color:#770088">private</span> <span style="color:#000000">Class</span><span style="color:#981a1a"><?></span> <span style="color:#000000">clazz</span>;
​
    <span style="color:#770088">private</span> <span style="color:#000000">File</span> <span style="color:#000000">dir</span>;
​
    <span style="color:#770088">public</span> <span style="color:#000000">MyClassLoader</span>(<span style="color:#000000">File</span> <span style="color:#000000">dir</span>, <span style="color:#000000">Class</span><span style="color:#981a1a"><?></span> <span style="color:#000000">clazz</span>){
        <span style="color:#770088">this</span>.<span style="color:#000000">dir</span> <span style="color:#981a1a">=</span> <span style="color:#000000">dir</span>;
        <span style="color:#770088">this</span>.<span style="color:#000000">clazz</span> <span style="color:#981a1a">=</span> <span style="color:#000000">clazz</span>;
    }
​
    <span style="color:#555555">@Override</span>
    <span style="color:#770088">public</span> <span style="color:#000000">Class</span><span style="color:#981a1a"><?></span> <span style="color:#000000">findClass</span>(<span style="color:#008855">String</span> <span style="color:#000000">name</span>) <span style="color:#770088">throws</span> <span style="color:#000000">ClassNotFoundException</span> {
        <span style="color:#770088">try</span> {
            <span style="color:#000000">InputStream</span> <span style="color:#000000">is</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">FileInputStream</span>(<span style="color:#770088">new</span> <span style="color:#000000">File</span>(<span style="color:#000000">dir</span>, <span style="color:#000000">name</span>));
            <span style="color:#008855">int</span> <span style="color:#000000">available</span> <span style="color:#981a1a">=</span> <span style="color:#000000">is</span>.<span style="color:#000000">available</span>();
            <span style="color:#008855">byte</span>[] <span style="color:#000000">buffer</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#008855">byte</span>[<span style="color:#000000">available</span>];
            <span style="color:#000000">is</span>.<span style="color:#000000">read</span>(<span style="color:#000000">buffer</span>);
            <span style="color:#000000">is</span>.<span style="color:#000000">close</span>();
            <span style="color:#770088">return</span> <span style="color:#000000">defineClass</span>(<span style="color:#000000">clazz</span>.<span style="color:#000000">getPackage</span>().<span style="color:#000000">getName</span>() <span style="color:#981a1a">+</span> <span style="color:#aa1111">".$proxy0"</span>, <span style="color:#000000">buffer</span>, <span style="color:#116644">0</span>, <span style="color:#000000">buffer</span>.<span style="color:#000000">length</span>);
        } <span style="color:#770088">catch</span> (<span style="color:#000000">FileNotFoundException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        } <span style="color:#770088">catch</span> (<span style="color:#000000">IOException</span> <span style="color:#000000">e</span>) {
            <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
        }
        <span style="color:#770088">return</span> <span style="color:#770088">super</span>.<span style="color:#000000">loadClass</span>(<span style="color:#000000">name</span>);
    }
}</span></span>

编写创建代理实例的方法,在 MyProxy 中编写如下方法:

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">Object</span> <span style="color:#0000ff">newProxyInstance</span>(<span style="color:#000000">Class</span><span style="color:#981a1a"><?></span> <span style="color:#000000">clazz</span>, <span style="color:#000000">MyClassLoader</span> <span style="color:#000000">loader</span>, <span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span>) <span style="color:#770088">throws</span> <span style="color:#000000">IOException</span>, <span style="color:#000000">ClassNotFoundException</span>, <span style="color:#000000">NoSuchMethodException</span>, <span style="color:#000000">IllegalAccessException</span>, <span style="color:#000000">InvocationTargetException</span>, <span style="color:#000000">InstantiationException</span> {
    <span style="color:#008855">String</span> <span style="color:#000000">proxyClass</span> <span style="color:#981a1a">=</span> <span style="color:#000000">generateProxyClass</span>(<span style="color:#000000">clazz</span>);
    <span style="color:#000000">saveProxyClass</span>(<span style="color:#000000">proxyClass</span>);
    <span style="color:#000000">compileProxyClass</span>();
    <span style="color:#000000">Class</span><span style="color:#981a1a"><?></span> <span style="color:#000000">cl</span> <span style="color:#981a1a">=</span> <span style="color:#000000">loader</span>.<span style="color:#000000">findClass</span>(<span style="color:#aa1111">"$proxy0.class"</span>);
    <span style="color:#000000">Constructor</span><span style="color:#981a1a"><?></span> <span style="color:#000000">constructor</span> <span style="color:#981a1a">=</span> <span style="color:#000000">cl</span>.<span style="color:#000000">getDeclaredConstructor</span>(<span style="color:#000000">MethodInvocationHandler</span>.<span style="color:#770088">class</span>);
    <span style="color:#000000">constructor</span>.<span style="color:#000000">setAccessible</span>(<span style="color:#221199">true</span>);
    <span style="color:#770088">return</span> <span style="color:#000000">constructor</span>.<span style="color:#000000">newInstance</span>(<span style="color:#000000">handler</span>);
}</span></span>

编写测试案例

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">qf</span>.<span style="color:#000000">proxy</span>.<span style="color:#000000">_static</span>;
​
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">io</span>.<span style="color:#000000">File</span>;
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">io</span>.<span style="color:#000000">IOException</span>;
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">InvocationTargetException</span>;
<span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">lang</span>.<span style="color:#000000">reflect</span>.<span style="color:#000000">Method</span>;
​
<span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">MyProxyTest</span> {
​
    <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) <span style="color:#770088">throws</span> <span style="color:#000000">NoSuchMethodException</span>, <span style="color:#000000">IOException</span>, <span style="color:#000000">InstantiationException</span>, <span style="color:#000000">IllegalAccessException</span>, <span style="color:#000000">InvocationTargetException</span>, <span style="color:#000000">ClassNotFoundException</span> {
        <span style="color:#000000">IPhoneWholesalerNew</span> <span style="color:#000000">wholesalerNew</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">IPhoneWholesalerNew</span>();
        <span style="color:#000000">MethodInvocationHandler</span> <span style="color:#000000">handler</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">MethodInvocationHandler</span>() {
            <span style="color:#555555">@Override</span>
            <span style="color:#770088">public</span> <span style="color:#008855">Object</span> <span style="color:#000000">invoke</span>(<span style="color:#000000">Method</span> <span style="color:#000000">method</span>, <span style="color:#008855">Object</span>[] <span style="color:#000000">args</span>) <span style="color:#770088">throws</span> <span style="color:#000000">IllegalArgumentException</span> {
                <span style="color:#008855">double</span> <span style="color:#000000">profit</span> <span style="color:#981a1a">=</span> <span style="color:#116644">1000</span>;
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"代理盈利:"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>);
                <span style="color:#008855">Object</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#221199">null</span>;
                <span style="color:#770088">try</span> {
                    <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#000000">method</span>.<span style="color:#000000">invoke</span>(<span style="color:#000000">wholesalerNew</span>, <span style="color:#000000">args</span>);
                } <span style="color:#770088">catch</span> (<span style="color:#000000">IllegalAccessException</span> <span style="color:#981a1a">|</span> <span style="color:#000000">InvocationTargetException</span> <span style="color:#000000">e</span>) {
                    <span style="color:#000000">e</span>.<span style="color:#000000">printStackTrace</span>();
                }
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"卖出价格:"</span> <span style="color:#981a1a">+</span> ((<span style="color:#008855">double</span>)<span style="color:#000000">args</span>[<span style="color:#116644">0</span>] <span style="color:#981a1a">+</span> <span style="color:#000000">profit</span>));
                <span style="color:#770088">return</span> <span style="color:#000000">result</span>;
            }
        };
        <span style="color:#000000">MyClassLoader</span> <span style="color:#000000">loader</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">MyClassLoader</span>(<span style="color:#770088">new</span> <span style="color:#000000">File</span>(<span style="color:#aa1111">"d:\\"</span>), <span style="color:#000000">Seller</span>.<span style="color:#770088">class</span>);
        <span style="color:#000000">Seller</span> <span style="color:#000000">seller</span> <span style="color:#981a1a">=</span> (<span style="color:#000000">Seller</span>) <span style="color:#000000">MyProxy</span>.<span style="color:#000000">newProxyInstance</span>(<span style="color:#000000">Seller</span>.<span style="color:#770088">class</span>, <span style="color:#000000">loader</span>, <span style="color:#000000">handler</span>);
        <span style="color:#000000">seller</span>.<span style="color:#000000">sellPhoneX</span>(<span style="color:#116644">12000</span>);
    }
}</span></span>

到此代理繁多的问题也就是 解决了,这种实现方式称之为动态代理。

思考:如果实现多个接口,怎么处理?

3. 动态代理

动态代理根据代理的实现又分为 JDK 动态代理和 CGLIB 动态代理。如果是对接口做代理,那么使用的 JDK 动态代理;如果是对类做代理,那么使用的就是 CGLIB 动态代理。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值