Proxyモード


Proxyモード


Spring AOPの学習過程を熟知するために,Proxyについて再度理解した.エージェントを検索する過程で、エージェントモデルの位置づけを見ました.これは実際のAOPがどのように働いているのかを理解するのに役立つ良い文章だと思います.そのため、重複したコードを書き直す必要があります.

Proxyとは?


Proxyは実際にはエージェントであり,動作を実行するオブジェクトではない.要するに、Wrappingクラスは、実際に呼び出す必要があるメソッドを含む実際のターゲットのクラスであると考えられます.
Proxyモードを使用すると、Proxyステップでオブジェクトを許可し、必要に応じてオブジェクトを作成または使用することができ、メモリを節約できます.Proxyモードで行う作業の1つは、保護されたオブジェクトへのアクセス権を制御することです.
実際のインスタンスを使用してメモリを節約する方法をコードで説明します.
まず、実際に使用されているコアコードを作成しましょう.1つはインタフェースで、1つはインタフェースを継承するクラスです.

インタフェース

public interface CommandExcutor {

    public void runCommand(String cmd) throws Exception;

}

インタフェースを実装するクラス

public class CommandExcutorImpl implements CommandExcutor {

    @Override
    public void runCommand(String cmd) throws Exception {
        Runtime.getRuntime().exec(cmd);
        System.out.println("cmd: " + cmd);
    }
}
上記のコードはインタフェースとインプリメンテーションを別々に記述し、実際にはCommandExcutorImpleを呼び出すことでオブジェクトを作成します.runCommand()メソッドはコストが高いため、メモリの浪費を招く可能性があります.
Runtime.getRuntime().exec()メソッドは、プロセスを実行したりosを制御したりする際に使用されるクラスです.

proxy class


上記の欠点を補うために、エージェントクラスを作成しました.まず,同じインタフェースを継承することによってインタフェースの一貫性を保つ.
次に、ジェネレータでCommandExcutorImplクラスをインスタンス化し、プロキシクラスのrunCommandメソッドでexecutorオブジェクトをインスタンス化するrunCommand()メソッドを決定します.
public class CommandExcutorProxy implements CommandExcutor {

    // isAdmin 값에 따라서 객체에 대한 엑세스 권한을 제어합니다.    
    private boolean isAdmin;
    private CommandExcutor excutor;


    public CommandExcutorProxy(String user, String pwd) {
        if ("sa1341".equals(user) && "1234".equals(pwd)) {
            isAdmin = true;
        }
        excutor = new CommandExcutorImpl();
    }

    @Override
    public void runCommand(String cmd) throws Exception {
    
        if (isAdmin) {
            excutor.runCommand(cmd);
        } else {
            if (cmd.trim().startsWith("rm")) {
                throw new Exception("rm is only admin");
            } else {
                excutor.runCommand(cmd);
            }
        }
    }
}

実行クラス

public class ProxyPatternTest {

    public static void main(String[] args) {

       CommandExcutor excutor = new CommandExcutorProxy("sa1341","1111");

       try {
           excutor.runCommand("ls -al");
           excutor.runCommand("rm -rf *");
       } catch (Exception e){
           System.out.println(e.getMessage());
       }
    }
}
上記のセクションでは、特定の用語を例外とするために異なるpasswordを示します.これは、端末上のすべてのディレクトリを強制的に削除するコマンドです.そのため、リスクがあるため、認証されたユーザーのみがこの操作を実行できるテストケースに入れます.
実行結果

前述したように、プロキシを作成することで、インタフェースを一貫させながらアクセス権を付与できます.また、メモリを節約できます.

2. Spring AOP Proxy


注意:付加機能モジュールは、オブジェクト向けのオブジェクトのようにAOP機能を持つモジュールです.スプリングには@Asspectノット化により実施できる.
スプリングは、「救済」(Weaving)を使用して、割り当てられたビューを持つ新しいプロキシオブジェクトを作成します.
スプリングAOPエージェントの作成方法
1. JDK Dynamic Proxy
2. CGLIB Proxy
@Transactional
public void deleteById(final Long id){
    boardRepository.deleteById(id);
}
@Service
@RequiredArgsConstructor
@Slf4j
public class BoardService {

    private final BoardRepository boardRepository;

    @Transactional
    public void deleteById(final Long id){
        boardRepository.deleteById(id);
    }
}
上記のサービスを呼び出すと、BoardServiceに直接アクセスしません.

CGIBエージェント



編み込みによって生成されたCGIBエージェントの間接アクセス$$は、BySpringCGIBを強化します.トランザクションまたはレコードに関連する処理(たとえば、トランザクションまたはレコード)は、低プロキシオブジェクトによって行われます.
内部エージェントのコードが何なのかはわかりませんが、次のコードはトラムがアップロードしたブログの位置付けを参考にしています.
public class BoardServiceEnhancerBySpringCGLIB{

    private BoardService boardService;


    public BoardServiceEnhancerBySpringCGLIB(BoardService boardService){
        this.boardService = boardService;
    }

    public void save(final Long id){

        TransactionManager transactionManager = new TransactionManager();

        try{
            this.boardService.save(id);
            //BoardService를 호출해 로직 실행 후, 에러가 없으면 comit 실행
            transactionManager.comit();

        }catch(Exception e){
            transactionManager.rollBack();
            throw new Exception("에러발생");           
        }
    }
}
従来,デフォルトではインタフェースが用いられ,実装インタフェースのクラスではJDK動的エージェントが用いられ,インタフェースがない場合はCGIBエージェントが用いられていた.
CGIB Proxyは継承によりProxyを実現するため,最終クラスであればProxyを作成できない.
デフォルトでは、スプリングガイドはギャップのあるCGIB Proxyを作成します.Spring Boot開発チーム長も、CGIB Proxyは内部でバイトコードをJVMで処理するため、JDK法よりも性能が速いと話しています.そこでCGIB方式を提案します.
ただし、spring-data-jpaは、次のコードに加えてJDKダイナミックエージェントを使用してレポートを作成します.
public interface BoardRepository extends JpaRepository<Board, Long>, QuerydslPredicateExecutor<Board>{
}
Jpa Repositoryインタフェースを実装したクラスSimpleJpa Repositoryが存在しますが、なぜCGIB方式を使用しないのでしょうか...気になりますが、また聞けば頭が爆発するかも…これから機会があればご紹介します
参照サイト:https://tram-devlog.tistory.com/entry/Spring-AOP-weaving-proxyhttps://blog.seotory.com/post/2017/09/java-proxy-pattern