TIL_Java Spring the Modern Way_8


Spring AOP(Aspect Oriented Programming)
これを観点指向プログラミングと呼ぶ.
ビジネスロジックを実行するための複数のモジュール.
ex)データベース接続、ログ記録、ファイルI/O等
ビジネスロジックが異なると、生産性とメンテナンスが悪くなります.
一つのことを変えるたびに、すべてのことを変えなければなりません.
したがって、ビジネスロジックから分離し、再利用可能にモジュール化することを目標としています.
AOPキーコンセプト
  • Asspect:上記分散した注目点をモジュール化する.主に付加機能をモジュール化する.
  • Target:Asspectの適用先(クラス、メソッド...)
  • Advisor:実際にどのような作業が必要かについて、実際の付加機能を含む実施
  • JointPoint:Adviceは、挿入可能なポイントに適用されます.メソッド入力ポイント、コンストラクション関数呼び出しポイント、およびフィールドから値を抽出したときの異なる時点
  • Point:JointPointの詳細仕様定義.'「メソッドの開始時に呼び出す」というように、より具体的なAddiceランタイム
  • を指定できます.
    Spring AOPの適用
    pom.xml
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    @Before
    @Aspect // aspect라는걸 명시
    @Configuration // bean으로 등록
    public class UseAccessAspect {	
    	private Logger logger = LoggerFactory.getLogger(this.getClass());
        // What kind of method calls I would intercept
    	// execution(* PACKAGE.*.*(..))
    	// Weaving & Weaver
        // joinpoint 에 들어오는 시점 실행전에 수행
        @Before("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.dataLayerExecution()")
    	public void before(JoinPoint joinpoint) {
            //Advice
    		logger.info("Check for user access");
    		logger.info("Allowed execution for - {}", joinpoint);
    	}
    }
    @After関連
    @Aspect
    @Configuration
    public class AfterAopAspect {
    	
    	private Logger logger = LoggerFactory.getLogger(this.getClass());
    	// return 되는 객체 가로챔, 실행이 성공적으로 완료될때만 실행
    	@AfterReturning(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()",
    			returning = "result")
    	public void afterReturning(JoinPoint joinpoint, Object result) {
    		logger.info("{} return with value {}", joinpoint, result);
    	}
        // 예외 발생시에 실행, Exception객체를 해놨으므로 지금은 모든예외 다 적용
    	@AfterThrowing(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()",
    			throwing = "exception")
    	public void afterThrowing(JoinPoint joinpoint, Exception exception) {
    		logger.info("{} return with value {}", joinpoint, exception);
    	}
        // 그냥 joinpoing후에 실행
    	@After(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()")
    	public void after(JoinPoint joinpoint) {
    		logger.info("after execution of {}", joinpoint);
    	}
    }
    @Around
    @Aspect
    @Configuration
    public class MethodExecutionAspect {
    	
    	private Logger logger = LoggerFactory.getLogger(this.getClass());
    	
    	@Around("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.trackTimeAnnotation()")
    	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    		// 실행전에 하는거
    		long startTime = System.currentTimeMillis();
    		// 분기점, 이게 내가 지정한 메서드가 실행되는 시점
    		proceedingJoinPoint.proceed();
    		// 실행후에 하는거
    		long timeTaken = System.currentTimeMillis() - startTime;
    		logger.info("Time Taken by {} is {}", proceedingJoinPoint, timeTaken);
    	}
    }
    関連資料を整理する
    @Before(前):デバイスターゲットメソッドを呼び出す前にデバイス機能を実行
    @After(後続):ターゲットメソッドの結果(成功または異常)を考慮せずに、ターゲットメソッドが完了した後にデバイス機能を実行します.
    @AfterReturning(正常に戻った後)ターゲットメソッドが結果値を正常に返した後にデバイス機能を実行
    @AfterThrowing(異常発生後):ターゲットメソッドが実行中に異常を投げ出した場合、デバイス機能を実行
    @Around(メソッド実行前後):ターゲットメソッド呼び出しの前後で、デバイスはターゲットメソッドを迂回します.
    切り込みポイントの管理
    public class CommonJoinPointConfig {
        // 기본적인 사용법
    	@Pointcut("execution(* com.in28minutes.spring.aop.springaop.data.*.*(..))")
    	public void dataLayerExecution() {}
    	
    	@Pointcut("execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))")
    	public void businessLayerExecution() {}
        
    	// 2개를 묶어서 지정가능
    	@Pointcut("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.dataLayerExecution() "
    			+ "&& com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()")
    	public void allLayerExecution() {}
        
    	// name에 dao가 포함되는것들
    	@Pointcut("bean(*dao*)")
    	public void beanContainingDao() {}
        
    	// 특정 패키지 제외
    	@Pointcut("within(*com.in28minutes.spring.aop.springaop.data..*)")
    	public void dataLayerExecutionWithWithin() {}
        
    	// 특정 어노테이션이 있는 것들
    	@Pointcut("@annotation(com.in28minutes.spring.aop.springaop.aspect.TrackTime)")
    	public void trackTimeAnnotation() {}
    }
    そうでなければ、特定の切り込み点を変えたいなら、方法を適用したすべての場所に行って、一つ一つ変えなければなりません.
    common classなどを作成し、変更があればここを変更するだけでバッチ変更管理が容易になります.