Javaのデザインパターン----プロキシモードでは、JDKの動的プロキシとCGLIBダイナミックプロキシの違いを理解します

プロキシモード:

これは、オブジェクトへのアクセスを制御するために、他のオブジェクトのための薬剤を提供することを指します。

顧客とターゲットオブジェクト間のプロキシオブジェクトを仲介します。

これは、構造的なデザインパターンです。

Proxyパターンの分類:

静的なエージェント。

動的プロキシ。

 

帯電防止剤

さて、私たちは、静的なプロキシを起動します。

役割の分析:

1.抽象的役割:解決するために、一般的な用途のインタフェースや抽象クラス

2.真の役割:エージェントの役割

3.演技の役割:本当の役割、エージェントの本当の役割は、我々はいくつかの一般的な事業子会社を行います

4.お客様:プロキシオブジェクトにアクセスする人

コードのステップ:

1.インタフェース

/**
 * @Author Darker
 * @Descrption 租房
 * @Date : Created in 10:00 2020-3-11
 */
public interface Rent {
    public void rent();
}

2、本当の役割

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 10:01 2020-3-11
 */
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}

3、演じる役割

/**
 * @Author Darker
 * @Descrption 代理,中介
 * @Date : Created in 10:02 2020-3-11
 */
public class Proxy{

    private Host host;

    public Proxy(){}

    public Proxy(Host host){
        this.host = host;
    }

    public void rent(){
        seeHouse();
        host.rent();
        signContract();
        fare();
    }

    public void seeHouse(){
        System.out.println("中介带你看房子");
    }

    public void signContract(){
        System.out.println("签租房合同");
    }

    public void fare(){
        System.out.println("收中介费");
    }
}

4、クライアントアクセス

/**
 * @Author Darker
 * @Descrption 客户端租房
 * @Date : Created in 10:02 2020-3-11
 */
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

静的プロキシモードの利点:

1.あなたは、操作がより現実の文字が純粋に保つことができます!公共の事業のいくつかに注意を払う必要はありません。

2.エージェントの役割への公共的な役割!事業の部門を達成するために!

3.公共サービスを延長、簡単な集中管理を発生します。

短所:

役割は、それがコードの量を倍増する、本当の演技役割を持つことになります。

 

もちろん、上記の例では、当社の実際の開発では一般的ではないかもしれないが、主に意義代理店モデルを理解するために、我々は今、アプリケーションにコードを与えるLaijiangjiang豆腐ます。

1、最初にすべての私たちの会社の事業は、ユーザのカードの動作である持っています。

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 10:19 2020-3-11
 */
public interface UserService {
    public void add();

    public void delete();
}

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 10:20 2020-3-11
 */
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加一个角色");
    }

    @Override
    public void delete() {
        System.out.println("删除一个角色");
    }
}

2、まあ、今の質問は、私たちの会社は、ログを追加するには、すべてのステップが必要です。

まず、ログ操作内の各メソッドプラスのUserImplに、それはあまりにも愚かだと思うが、また、それらを身に着けていない、といないに沿って開閉原則。

だから、我々は完全にプロキシを使用し、代理店モデルを考えました。

/**
 * @Author Darker
 * @Descrption 静态代理模式
 * @Date : Created in 10:26 2020-3-11
 */
public class Proxy implements UserService{
    
    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    public void log(String msg){
        System.out.println("调用了"+msg+"方法");
    }
    
}

/**
 * @Author Darker
 * @Descrption 客户端
 * @Date : Created in 10:20 2020-3-11
 */
public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        Proxy proxy = new Proxy();
        proxy.setUserService(userService);
        proxy.add();
    }
}

静的プロキシモードのアプリケーションであるカップリングを、削減しながらこのように、我々は、ログ機能を追加する必要がありましたが、開閉の原則に沿ってきました。

上記サービスの各プログラマは確かに、単純な工場をリフレクションを使用してと思うだろうとして、それを解決する方法を、プロキシ、またはあまりにも面倒であることをしかし、これは私は、各実役のプロキシでなければなりませんそこを考えて、私たちはプロキシにそれをしたいサブクラス区分そこに文字列を渡したくない、と私はストレートパスクラスを行く、その後、自動的に工場のようなものを生成するためにリフレクションを使用しています、そして、我々はまた、直接反映されません自動的に生成されたプロキシクラス、具体的には、どのように達成するために?

動的プロキシ

図1に示すように、分析の役割:静的および動的プロキシエージェントの役割

2、動的プロキシクラスが自動的に書き込ま当社の直接ではない、動的に生成されます

図3に示すように、動的プロキシが2つのカテゴリに分類:動的エージェントベースのインタフェース、動的プロキシクラスベース

    * ---- JDKの動的プロキシベースのインタフェース

    *クラスベース:CGLIB

    * Javaバイトコード:javasist(わずかとなっ下げるこちら)

 

JDKダイナミックプロキシ

ここでは、最初にすべての私たちが最初の2つのクラスを理解する必要があり、JDKの下での動的プロキシの観点に焦点を当てる:プロキシ(代理)、InvocationHandlerです(呼処理を)

APIのInvocationHandlerは、このクラスを見て、私たちは、その後、我々はそれが反射技術を使用しないことを知って、それがパッケージ内の反射界面であることがわかります。

私たちは、1つの方法見つけることに行くと、それを参照してくださいポイント

 さて、次のクラスのプロキシでの外観をしましょう

 

同様に、それはまた、次のクラスのパッケージを反映したものであるが、それはあなたのプロキシを作成する2つの方法を伝えます 

この方法は、getProxyClassが最初Constructorオブジェクトを取得することをメソッドを呼び出すことで、そのオブジェクトのnewInstanceは、オブジェクトを構築するために、このコンストラクタを使用して、あなたが望む最後のリターンプロキシクラスに渡されたハンドラを渡します。

方法2は、オブジェクトに対してコールnewProxcyInstance直接プロキシクラスを返すために、よりシンプルかつ簡単な方法です。 

まず、私たちの目的は、私は私がお返しに来て好きなものを説く手段は、プロキシオブジェクト、我々はあなたがプロキシクラスを操作したい少し操作を行うための方法を持っている必要がありますオブジェクトを与える必要があることを、動的プロキシを得ることですそれだけで、これは、我々が実装する必要がinvocationHadler抽象インタフェースに相当し、APIのから見ることができ賃借人の行動として、私たちの最初の静的なエージェント(プロキシクラスと実際のクラスの一般的な方法)。

、任意のインターフェイスを表現したいプロキシクラスのオブジェクトを返すことができます。1.ユニバーサルプロキシ生成。

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 11:42 2020-3-11
 */
public class DynamicProxyProvider implements InvocationHandler {
    //被代理的接口
    private Object target;

    public void setObject(Object target) {
        this.target = target;
    }

    //第一个参数是为了加载类在哪个位置classLoader
    //代理类的接口
    //自己本身
    public Object getProxy(){
      return  Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }


    //从参数可以看出我们需要一个代理类,所以我们来一个方法生成代理类
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object invoke = method.invoke(target, args);
        return invoke;
    }

    public void log(String msg){
        System.out.println("调用了"+msg+"方法");
    }
}

2.インタフェースおよび実体エージェントの必要性。

/**
 * @Author Darker
 * @Descrption 租房
 * @Date : Created in 10:00 2020-3-11
 */
public interface Rent {
    public void rent();
}


/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 10:01 2020-3-11
 */
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 10:19 2020-3-11
 */
public interface UserService {
    public void add();

    public void delete();
}

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 10:20 2020-3-11
 */
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加一个角色");
    }

    @Override
    public void delete() {
        System.out.println("删除一个角色");
    }
}

エージェントの需要に応じて3.クライアントコール

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 11:50 2020-3-11
 */
public class Client {
    public static void main(String[] args) {
        Rent rent = new Host();
        DynamicProxyProvider proxyProvider = new DynamicProxyProvider();
        proxyProvider.setObject(rent);
        Rent proxyRent =(Rent) proxyProvider.getProxy();
        proxyRent.rent();

        UserServiceImpl userService = new UserServiceImpl();
        proxyProvider.setObject(userService);
        UserService proxyUserService = (UserService) proxyProvider.getProxy();
        proxyUserService.add();
    }
}

 

 それはあなたがそのような操作レコードを追加し、ログの追加などの機能強化を、インターフェイスする必要があり、プロキシを使用し、魔法ではありません。

利点:

1.静的エージェントのすべての利点を持っています

2. A動的プロキシプロキシクラスは、一般的にサービスのクラスに対応するインタフェースであります

3. A動的プロキシクラスは、インターフェイスが同じを達成する限り、複数のクラスを表すことができます

しかし!しかし!しかし!この2つのクラスの後、それが最終的にそれを動的に生成されたプロキシを行う方法で、なぜ私は中のinvokeメソッドメソッドを追加する、各エージェントクラスのメソッドは、私はそれを変更するのだろうか?

さて、私たちは今、動的プロキシ実装原理を分析します。

1.参照を取得するプロキシクラス、およびそのすべてのインタフェースの取得(取得した反射)。注:なぜ直接プロキシクラス、それはinterfceを得るのすべてのコード反射法であるため、エージェントへのインタフェースを形成しなければなりません。

インタフェースの実装すべてのメソッドという新しいクラスを再生成する2.jdk Proxyクラスはプロキシクラスです。

3.動的に生成されたJavaコードは、強化ロジックは、新たに生成されたコードに追加されます。

4.新しいJavaコードコンパイル済みのクラスファイル。

5.負荷と新しいクラスを再実行して、クラスが新しいクラスを取得することです。

我々はそれの全く新しいクラスを取得なぜ、私たちはクラスの外観の出力を見ている、我々は代わりに$ PROXY1にそのタイプUserServiceImplがあります。

 

JDKは、最終的にそれは我々がそれがそうしたいと、私たちが見るために、そのアプローチを入力していません。

 

案の定、それはそれは我々のアプローチを強化することができる理由を説明するクラスを、再構築、およびそれが抗コンパイル済みのクラスファイルであるかどうかを確認するために、再びそれをリライトするすべてのメソッドを入れ、あなたもそれを見つけるだろう方法は、それが唯一の代理であることができることを意味、最終的なものである(また、なぜ上記実現することは、私は他の人からのブログで答えを見つけることが少ないインタフェース65535の長さよりも)。

 CGLIBのダイナミックプロキシ

上に、JDKの動的プロキシの話を、私たちならば、その後の質問は、あくまで一般的なインターフェイスのすべてのサブクラスは、このインターフェイス内でスキャンできるすべての方法を通じて、新しいクラスを書き換えるためのエージェントを持つことができ、問題を発見しました行うにはどのように何のインタフェース、私は一つのクラスを持っていない、もちろん、CGLIBのダイナミックプロキシが実現するというソリューションがあります。

1. CGLIBが最初のプロキシクラスを書くために(CGLIB実装はMethodInterceptorの下のパケットインターセプタを使用すると、継承の原理を利用、サブクラスである親クラスの形でそれを設定することをプロキシクラスがプロキシで、あります親クラスと親クラスの書き換えを継承することができます)

/**
 * @Author Darker
 * @Descrption 实现一个cglib的拦截器
 * @Date : Created in 16:32 2020-3-11
 */
public class CGlibProxyPriveder implements MethodInterceptor {

    public Object getProxy(Class<?> clazz){
        //相当于cglib给你写了个生成代理的工具类,你只要把class传进来就可以了
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) {
        log(method.getName());
        try {
            methodProxy.invokeSuper(o,objects);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    public void log(String msg){
        System.out.println("调用了"+msg+"方法");
    }
}

2.プロキシクラスはもちろん、実施することが、主にJDKダイナミックプロキシを区別するために、インタフェースを実装していません。

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 17:34 2020-3-11
 */
public class UserImpl {
    public void add(){
        System.out.println("添加一个用户");
    }
}

3.ファイル名を指定して実行

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 17:36 2020-3-11
 */
public class CGlibTest {
    public static void main(String[] args) {
        CGlibProxyPriveder cGlibProxyPriveder = new CGlibProxyPriveder();
        UserImpl cGlibProxy = (UserImpl)cGlibProxyPriveder.getProxy(UserImpl.class);
        cGlibProxy.add();
    }
}

[OK]を、成功した、それはそうCGLIBはまた、当社のプロキシ機能を完了しました。

 

要約:

1.jdk動的プロキシ機構を向いている反射、インタフェースを使用して実装される。CGLIB達成するために、プロキシクラスの継承の根底にあるバイトコードベースの動的剤、CGLIBは、強力な、高性能コードライブラリ生産され、達成され得ます実行時の動的拡張Javaクラス(達成継承されるので、最終的にキーワードで変更した場合ので、プロキシクラスの場合、それは失敗します)。

両方のパフォーマンスの問題に関しては2:

それが呼び出されるメソッドが含まれていFastClassが含まれているすべてのロジックを、生成するので、CGLIBダイナミックプロキシオブジェクトは、高JDKダイナミックプロキシ多くの実際の実行時間のパフォーマンスで作成し、そこよりも反射によって呼び出される必要はありません。研究では、速度が約10倍高いことが示されています。

CGLIBより複雑な、研究はその約8倍のギャップを示しているので、それはより多くのJDKのダイナミックプロキシ、プロキシ生成ロジックがかかるよりも、しかし、時がCGLIBは、オブジェクトを作成します。

したがって、頻繁にせずにプロキシオブジェクトを作成するので、それはとにかく、CGLIBの動的プロキシに適している、プロキシオブジェクトまたはシングルトンプールのプロキシ例えば、JDKの動的プロキシに適しています。

 

プロキシモードと春

さて、私たちは常にエージェンシーモデルを使用することですspringAopが達成言う、あなたはそれが動的なものを達成するために、エージェントとは何か知っている、そして今、私たちは春-AOP次ProxyFactoryBeanを見つける必要がありgetObjectメソッドを見つけるために、見た目の内部を取ります方法。

あなたがそこ判断は、あるプロキシクラスはシングルトンであるかどうマルチケース、より1ケースよりも、プロキシを返した場合、それは、プロキシのシングルトン型を返す、ここでそれを参照してくださいよ、我々はリターンシングルトンを見てA。

 

上記判断が知っているかもしれないが、コンフィギュレーションファイルはCGLIBプロキシは、それが自動的にJDK動的プロキシを使用するときに使用力に設定されていない、またはこのBeanインタフェースでない場合、それは動的にCGLIBに使用される一方ビーン実装インタフェースがあるから薬。

添付ファイル:

開いた構成CGLIBの設定(それは見ることができ、実際には、設定するには、ソースに由来し、ここでスキャンMyBatisのクラスに次の4つの方法は、同じ目的を持っているされています)。

 

公開された27元の記事 ウォンの賞賛1 ビュー3644

おすすめ

転載: blog.csdn.net/qq_40111437/article/details/104789444