[SSM-Spring Chapter 04] AOPアスペクト指向プログラミング-プロキシモード-jdbc動的プロキシ-CGLIB動的プロキシ

AOPコンセプト

  AOPは水平抽出メカニズムを採用しています。これは、各メソッドに散在する反復コードを抽出し、抽出したコードを実行する必要がある場所に適用します(通常、ロギング、パフォーマンス統計、セキュリティに使用されます)制御、トランザクション処理、例外処理など)

AOPの用語

  1. アスペクト
      (アスペクト)は、システム機能(トランザクション処理など)クラスへのクロスカッティングのカプセル化を指します。
  2. 接続ポイント
      (ジョインポイント)は、メソッド呼び出しや例外など、実行中のプログラムのいくつかのポイントを指します。
  3. ポイントカット
      ポイントカット(ポイントカット)処置を必要とするそれらの接続点です。Spring AOPでは、すべてのメソッド実行が接続ポイントであり、エントリポイントは接続ポイントを変更する説明情報であり、エントリポイントはどの接続ポイントを処理する必要があるかを決定するために使用されます。
  4. 通知(拡張処理)
      は、アスペクトから特定の接続ポイント(ポイントカットルールを満たす)に追加されたコードの一部です。つまり、定義されたポイントカットで実行されるプログラムコードです。これは、表面を開いた後に表面を切断する方法として理解できます。したがって、通知はアスペクトの具体的な実現です。
  5. 導入
      (導入)では、既存の実装クラスにカスタムメソッドと属性を追加できます。
  6. Audience
      Audience(Target Object)は、通知されるすべてのオブジェクトを指します。AOPフレームワークがランタイムプロキシ(動的AOP)を使用してアスペクトを実装する場合、通知オブジェクトは常にプロキシオブジェクトです。
  7. 演技の
      プロキシ(プロキシ)が対象オブジェクトに通知された後、オブジェクトが動的に作成されます。
  8. Group into
      (Weaving)は、ターゲットオブジェクトのコードセクションに挿入され、プロキシオブジェクトを生成するプロセスです。異なる実装テクノロジーによると、AOPウィービングには3つの方法があります。コンパイラーウィービングには特別なJavaコンパイラーが必要です。クラスロード期間のウィービングには特別なクラスローダーが必要です。動的プロキシーウィービング(実行時に対象となる)クラスに通知を追加して、サブクラスを生成します。Spring AOPフレームワークはデフォルトで動的プロキシーウィービングを使用しますが、AspectJ(JavaベースのAOPフレームワーク)はコンパイラーウィービングとクラスロード期間ウィービングを使用します。



代理店モデル(仲介者の存在と同様)

プロキシクラスは、委任されたクラスのメッセージの前処理、メッセージのフィルタリングと転送、および委任されたクラスによるメッセージの実行後の後続処理の実行を担当します。(追加機能を追加)

(仮想)プロキシ

  仮想エージェントは、作成にコストがかかるオブジェクトの代表です多くの場合、仮想エージェントは、本当に必要になるまでオブジェクトを作成しません。オブジェクトが作成される前と作成されるとき、仮想エージェントはオブジェクトの代理として機能します。オブジェクトが作成された後、プロキシはリクエストをオブジェクトに直接委任します。
ここに画像の説明を挿入



静的プロキシ

  ターゲットオブジェクトクラス(RealSubject)と同じインターフェイスを明示的に実装する必要があり、プログラムの実行前にプロキシクラスが作成されている

ケースの実現

  1. プロキシクラスのインターフェイスと実装を作成する
public interface Math {
    
    
    public int jia(int a, int b);//加
    public int jian(int a, int b);//减
}
public class MathImpl implements Math{
    
    

    public int jia(int a,int b){
    
    
        return a+b;
    }
    public int jian(int a,int b){
    
    
        return a-b;
    }
}
  1. 一般的なメソッドを実装するツールクラスを作成する
public class Tools {
    
    

    public void check(){
    
    
        System.out.println("权限检查......");
    }
    public void log(){
    
    
        System.out.println("日志记录......");
    }

}

  1. プロキシクラスを作成する
public class MathProxy implements Math {
    
    

//    MathImpl mathImpl = new MathImpl();  //业务逻辑
//    Tools tools = new Tools();  //工具类
    MathImpl mathImpl;  //业务逻辑
    Tools tools;  //工具类

//    public MathProxy(){}
    public MathProxy(MathImpl mathImpl, Tools tools) {
    
    
    	//在构造器中直接初始化了
        this.mathImpl = mathImpl;
        this.tools = tools;
    }

    @Override
    public int jia(int a, int b) {
    
    
        tools.check();
        int result = mathImpl.jia(a,b);
        tools.log();
        return result;
    }

    @Override
    public int jian(int a, int b) {
    
    
        tools.check();
        tools.log();
        return mathImpl.jian(a,b);
    }
}
  1. テストクラスを作成する
public class Test {
    
    

    public static void main(String[] args) {
    
    
        /* 被代理对象--业务类 数学计算类 */
        MathImpl mathImpl = new MathImpl();
		//创建工具类
        Tools tools = new Tools();
        
//      代理对象
        Math math = new MathProxy(mathImpl,tools);
        int result1 = math.jia(2,6);
        System.out.println(result1);
        
        int result2 = math.jia(3,2);
		System.out.println(result2);
    }
}

  1. 演算結果

ここに画像の説明を挿入



動的プロキシー(JDKおよびCGLIB動的プロキシーは、Spring AOPで一般的に使用されていますが、現在は一般的に、aspectjを使用しています)

  ターゲットオブジェクトクラス(RealSubject)と同じインターフェイスを明示的に実装する必要はありませんが、プログラムがJVMによって実行されるまで、この実装を延期します。動的エージェントは、事前ではなく、必要なときに作成されます。

JDKとCGLIB動的プロキシーの主な違い:

JDK動的プロキシは、インターフェースを実装するクラスのインターフェースメソッドのみをプロキシできます
。CGLIB動的プロキシは、継承に基づいてプロキシを実装するため、最終クラス、プライベートメソッド、および静的メソッドをプロキシできません。

Spring AOPのプロキシで使用されるデフォルトの戦略は次のとおりです。

ターゲットオブジェクトがインターフェースを実装する
場合、デフォルトでJDKダイナミックプロキシが使用されます。ターゲットオブジェクトがインターフェースを実装しない場合、CgLibがダイナミックプロキシに使用されます。ターゲットオブジェクトが
インターフェースを実装し、CgLibが強制的にプロキシされる場合、CgLibがダイナミックプロキシに使用されます。


1. JDK動的プロキシはSpring AOPを実装します

  JDKダイナミックプロキシは、java.lang.reflect。*パッケージによって提供されるメソッドであり、プロキシオブジェクトを生成するためにインターフェースを使用する必要があります。したがって、ビジネスインターフェイスを使用するクラスの場合、SpringはJDK動的プロキシを使用して、デフォルトでAOPを実装します
  では春、JDKのダイナミックプロキシをAOPプログラミングを実装するためにデフォルトで使用されますorg.springframework.aop.framework.ProxyFactoryBeanを使用してプロキシを作成することは、Spring AOPを実装する最も基本的な方法です。

ケースの実現

  1. プロキシクラスのインターフェイスと実装クラスを作成する
public interface UserDao {
    
    
    public void save(String name);  //保存
    public void delete();           //删除
}
//被代理类
public class UserDaoImpl implements UserDao{
    
    
    @Override
    public void save(String name) {
    
    
        System.out.println("保存用户成功 :"+name);
    }

    @Override
    public void delete() {
    
    
        System.out.println("删除用户成功:");
    }
}
  1. アスペクトクラスを作成する(パブリックメソッド)
//切面类:定义公共方法
public class MyAspect {
    
    
    public void check(){
    
    
        System.out.println("check user......正在查找用户");
    }

    public void log(){
    
    
        System.out.println("logging.....正在记录日志");
    }
}
  1. プロキシクラスを作成する
//代理类
public class MyProxy implements InvocationHandler {
    
    

    private UserDao userDao;    //被代理类
    private MyAspect myAspect;  //切面类 -- 定义公共方法

    public MyProxy(UserDao userDao, MyAspect myAspect) {
    
    
        this.userDao = userDao;
        this.myAspect = myAspect;
    }

    //创建代理对象
    public Object create(){
    
    
        ClassLoader classLoader = userDao.getClass().getClassLoader();
//      Class<?>[] interfaces  被代理对象的实现接口
        Class<?>[] interfaces = userDao.getClass().getInterfaces();
        UserDao proxy = (UserDao) Proxy.newProxyInstance(classLoader,interfaces,this);
        System.out.println("创建代理对象create");
        return proxy;
    }

//    调用方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
//        织入 advice 加入到 被代理对象方法之前或之后
//        method就是调用的被代理对象的方法
        String methodName = method.getName();
//        Object 被代理对象
//        调用方法
        System.out.println("调用invoke方法");
        myAspect.check();//调用切面方法
        method.invoke(userDao,args);//userDaoImpl.save(参数)
        myAspect.log();//调用切面方法
        return null;
    }
}
  1. テストクラスを作成する
public class Test {
    
    
    public static void main(String[] args) {
    
    
//      被代理类
        UserDao userDao = new UserDaoImpl();
//      切面
        MyAspect myAspect = new MyAspect();

        //创建代理类  参1:被代理类  参2:切面(公共方法)
        MyProxy myProxy = new MyProxy(userDao,myAspect);
        //通过代理类来创建实例 - 代理对象
        UserDao proxy = (UserDao)myProxy.create();
        proxy.save("GF_浪夏一学");
        proxy.delete();
    }
}
  1. 演算結果

ここに画像の説明を挿入


2. CGLIB動的プロキシーはSpring AOPを実装します

  CGLIB(コード生成ライブラリ)は、非常に低レベルのバイトコード技術を使用して、指定されたターゲットクラスのサブクラスを生成し、サブクラスを拡張する、高性能のオープンソースコード生成パッケージです。CGLIBに必要なJARパッケージはSpring Coreパッケージに統合されており、JARパッケージを個別にインポートする必要はありません

ケースの実現

  1. プロキシクラス(ターゲットクラス)を作成する
public class UserService {
    
    
    public void save(){
    
    
        System.out.println("save service ......");
    }
    public void delete(){
    
    
        System.out.println("delete service ......");
    }
}
  1. アスペクトクラスを作成する(パブリックメソッド)
public class MyAspect {
    
    

    public void check(){
    
    
        System.out.println("check方法......检查包");
    }

    public void log(){
    
    
        System.out.println("logging方法......记录日志");
    }
}
  1. プロキシクラスを作成する
public class MyProxy<T> implements MethodInterceptor {
    
    

    private T byProxy;          //被代理类
    private MyAspect myAspect;  //切面

    public MyProxy(T byProxy, MyAspect myAspect) {
    
    
        this.byProxy = byProxy;
        this.myAspect = myAspect;
    }

//    创建代理对象
    public T create(){
    
    
        // 通国Enhancer创建代理类 ----  Proxy
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(byProxy.getClass());
        enhancer.setCallback(this);//实现MethodInterceptor接口的对象,就是当前对象
        T proxy = (T)enhancer.create();
        return proxy;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    

//        Method method,调用的被代理对象方法
        String methodName = method.getName();
        if(methodName.equals("save")){
    
    
            myAspect.check();
            method.invoke(byProxy,objects);
            myAspect.log();
        }
        if(methodName.equals("delete")){
    
    
            myAspect.check();
            method.invoke(byProxy,objects);
            myAspect.log();
        }
        return null;
    }
}

  1. テストクラスを作成する
public class CGLIBTest {
    
    
    public static void main(String[] args) {
    
    
//       被代理对象
        UserService userService = new UserService();
        MyAspect myAspect = new MyAspect();

        //创建代理对象
        MyProxy myProxy = new MyProxy(userService,myAspect);
        //通过代理类创建被代理对象
        UserService service = (UserService)myProxy.create();

        //调用方法
        service.save();
        service.delete();
    }
}
  1. 演算結果
    ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_40542534/article/details/108683231