본문 바로가기
국비학원

[국비지원] KH 정보교육원 115일차

by 도전하는 개발자 2022. 9. 13.

kh day 115

 

public class LogAdvice {  // POJO : Plain Old Java Object
	
	//  @Before : JoinPoint 메소드 실행 전 동작
	// POINTCUT EXPRESSION 생성은, AsepectJ 언어에서 제공하는 함수를 이용
	// 이 pointcut 설정함수 이름은 "execution()"
	// 이 execution 함수를 호출시, pointcut 설정내역을 인자값으로 매개변수에 전달해야함  
	@Before( "execution( * org.zerock.myapp.service.*Service.*(..) )" )  
	// 모든 접근제한자의 Service로 끝나는 모든 클래스의 모든 메서드의 모든 매개변수(..)에 적용
	public void logBefore() {
		log.trace("==============================");
		log.trace("1. logBefore() invoked.");
		log.trace("==============================");
		
	} // logBefore
	
	// 위는 단순한 호출로그다 인자값을 넣어보자. (&& args() 활용)
    // @Before JoinPoint 메소드 실행 전 동작
	@Before( "execution( * org.zerock.myapp.service.*Service.doAdd(..) ) && args (op1, op2) " )  
	// 모든 접근제한자의 Service로 끝나는 모든 클래스의 doAdd 메서드의 모든 매개변수(..)에 적용 && 인자값 대입
	public void logBeforeWithArgs(String op1, String op2) {
		log.trace("==============================");
		log.trace("2. logBeforeWithArgs({}, {}) invoked.", op1, op2);
		log.trace("==============================");
		
	} // logBeforeWithArgs
	
	
	// @AfterReturning : JoinPoint 메소드 정상 실행 후 동작
//	@AfterReturning( "execution( * org.zerock.myapp.service.*Service.*(..) )" )
	// 모든 접근제한자의 Service로 끝나는 모든 클래스의 모든 메서드의 모든 매개변수(..)에 적용
//	public void logAfterReturning() {
//		log.trace("==============================");
//		log.trace("3. logAfterReturning() invoked.");
//		log.trace("==============================");
//		
//	} // logAfterReturning
	
	
	// 위는 단순 호출로그다 인자 값을 넣어보자
    // @AfterReturning : JoinPoint 메소드 정상 실행 후 동작
	@AfterReturning( pointcut = "execution( * org.zerock.myapp.service.*Service.*(..) )", returning = "result" )
	// 모든 접근제한자의 Service로 끝나는 모든 클래스의 모든 메서드의 모든 매개변수(..)에 적용
	public void logAfterReturning(Object result) { // doAdd의 리턴값 -> Integer 300이 자동형변환 되서 Object 부모에 안김
		log.trace("==============================");
		log.trace("3. logAfterReturning({}, type : {}) invoked.", result, result.getClass().getName());
		log.trace("==============================");
		
	} // logAfterReturning
    
  } // end class


LogAdvice 

JointPoint인 doAdd 메소드 실행 전 후에 실행되는걸 확인하자




--

이번에는 고의로 exception을 발생시키고 이를 @AfterThrowing으로 처리해보자

테스트메소드 매개변수 "이백"으로 변경하셈

 

	  // @AfterThrowing : JoinPoint 메소드에서 예외 발생 후 동작
	@AfterThrowing( pointcut = "execution( * org.zerock.myapp.service.*Service.*(..) )", throwing = "e" )
	// 모든 접근제한자의 Service로 끝나는 모든 클래스의 모든 메서드의 모든 매개변수(..)에 적용
	public void logAfterThrowing(Exception e) {
		log.trace("==============================");
		log.trace("3. logAfterThrowing({}) invoked.", e);
		log.trace("==============================");
		
	} // logAfterThrowing


LogAdvice 
@AfterThrowing 메소드

 

콘솔창 (예외의 스택트레이스가 호출된다)

---

 

	//  @After : JoinPoint 메소드 실행 후 무조건 동작(정상실행 되나 예외발생하나 상관x)
	@After( "execution( * org.zerock.myapp.service.*Service.*(..) )" )
	// 모든 접근제한자의 Service로 끝나는 모든 클래스의 모든 메서드의 모든 매개변수(..)에 적용
	public void logAfter() {
		log.trace("==============================");
		log.trace("4. logAfter({}) invoked.");
		log.trace("==============================");
		
	} // logAfter


LogAdvice 
@After 메소드

 


콘솔창 (200으로 실행한거 하나 이백으로 실행한거 하나)

예외가 발생하던, 정상실행하던 얘는 무조건 실행된다

 



----


*** 
LogAdvice 
@Around 메소드

 

	// @Around를 위한 메소드를 추가해주자
	public abstract void methodForAroundAdvice() throws Exception;


sampleservice.java에 메소드 추가해주자

 

	// @Around를 위한 메소드
	@Override
	public void methodForAroundAdvice() throws Exception {
		log.info("methodForAroundAdvice() invoked.");

		
	} // methodForAroundAdvice


sampleserviceimpl에  구현 메소드? 만들어주자

 

	@Test
	@Order(2)
	@DisplayName("2. SampleService.methodForAroundAdvice")
	@Timeout(value = 10, unit = TimeUnit.SECONDS)
	void testMethodForAroundAdvice() throws Exception  {
		log.trace("testMethodForAroundAdvice() invoked.");
		
		this.service.methodForAroundAdvice();
		
	} // testMethodForAroundAdvice


tests에 두번째 메소드 만들어주자

 

 

	// @Around : Target 객체의 JointPoint의 실행여부까지 책임지며, JoinPoint를 실행시킨경우
	// 실행 전/후에 적용할 기능까지 함께 구현 가능 (혼자 북치고 장구치고 다 한다)
	// @Around 는 ProceedingJoinPoint와 함께 결합하여 동작한다
	@Around( "execution( * org.zerock.myapp.service.*Service.methodForAroundAdvice(..) )" )  
	// 모든 접근제한자의 Service로 끝나는 모든 클래스의 methodForAroundAdvice 메서드의 모든 매개변수(..)에 적용
	public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
		log.trace("==============================");
		log.trace("6. logAround({}, type:{}) invoked.", pjp, pjp.getClass().getName());
		log.trace("==============================");
		
		Object target = pjp.getTarget();          // JoinPoint가 있는 Target 객체
		Signature signature = pjp.getSignature(); // JoinPoint의 Method Signature
		Object[] args = pjp.getArgs();			  // JonPoint의 매개변수들을 배열로 변환
		
		log.info("\t+ 1. target : {}", target);
		log.info("\t+ 2. signature : {}", signature);
		log.info("\t+ 3. args : {}", Arrays.toString(args));
		
		// AOP로 로그뿐만 아니라 실행속도 같은 걱정거리도 처리할 수 있다.
		long start = System.nanoTime();				// 10억분의 1초
//		long start = System.currentTimeMillis(); 	// 1000분의 1초
		
			Object returnValue = pjp.proceed(); // JoinPoint인 methodForAroundAdvice 메서드를 실행시켜라!
		
		long end =  System.nanoTime();  			// 10억분의 1초
//		long end =  System.currentTimeMillis();  	// 1000분의 1초
	
		log.info("\t+ 4. Elapsed time : {} seconds", (end - start) / Math.pow(10, 9) );  // 10억분의 1초
//		log.info("\t+ 4. Elapsed time : {} seconds", (end - start) / Math.pow(10, 3) );  // 1000분의 1초
		
		return returnValue;
	} // logAround


LogAdvice 
@Around 메소드


콘솔창


---

chap19 스프링 트랜잭션

public interface Sample1Mapper {

	@Insert("INSERT INTO tbl_sample1 (col) VALUES ( #{col} )")
	public abstract void insertCol( @Param("col") String data) throws Exception;
	
} // end interface

Sample1Mapper

 

public interface Sample2Mapper {

	@Insert("INSERT INTO tbl_sample2 (col) VALUES ( #{col} )")
	public abstract void insertCol( @Param("col") String data) throws Exception;
	
} // end interface

Sample2Mapper (@Param 이해하고 넘어가자)

public interface SampleTXService {

	public abstract void 계좌이체(String data) throws Exception;
	
} // end interface

SampleTXService

 

@Log4j2
@NoArgsConstructor

@Service
public class SampleTXServiceImpl implements SampleTXService, InitializingBean {
	
	 // 주입
	@Setter(onMethod_= @Autowired)
	private Sample1Mapper mapper1;
	
	 // 주입
	@Setter(onMethod_= @Autowired)
	private Sample2Mapper mapper2;
		
        
	@Override
	public void 계좌이체(String data) throws Exception {
		log.info("계좌이체() invoked.");
		
		
		// 1. 소스계좌에서 이체금액만큼 차감 (DML 문장수행)
		this.mapper1.insertCol(data);
		
		// 2. 타겟계좌로 이체금액만큼 추가 (DML 문장수행)
		this.mapper2.insertCol(data);
		
	} // 계좌이체


	@Override
	public void afterPropertiesSet() throws Exception {
		log.trace("afterPropertiesSet() invoked.");
		
		log.info("\t+ this.mapper1 : {}, type : {}", this.mapper1, this.mapper1.getClass().getName());
		log.info("\t+ this.mapper2 : {}, type : {}", this.mapper2, this.mapper2.getClass().getName());
	} // afterPropertiesSet

} // end class

SampleTXServiceImpl

 

@Log4j2
@NoArgsConstructor

//JUnit5 방식
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(Lifecycle.PER_CLASS)
public class SampleTXServiceTests {

	// 주입
	@Setter(onMethod_=@Autowired)
	private SampleTXService service;

	
	// 주입이 되었는지 1회성 전처리 과정을 통해 확인하자
	@BeforeAll
	void beforeAll() {
		log.trace("beforeAll() invoked.");
			
		// 주입이 되었는지 확인 (주입이 되었다면 null이 아님)
		assertNotNull(this.service);
		log.info("\t+ this.service : {}", this.service);
		log.info("\t+ type : {}", this.service.getClass().getName());
		
	} // beforeAll
	
	
	@Test
	@Order(1)
	@DisplayName("1. test계좌이체")
	@Timeout(value = 10, unit = TimeUnit.SECONDS)
	void test계좌이체() throws Exception  {
		log.trace("test계좌이체() invoked.");
		
		// 0. 이체금액 정의
		String transferMoney = "123456789012345678901234567890123456789012345678901"; // length : 51;
		
		this.service.계좌이체(transferMoney);
		log.info("계좌이체가 잘 되었습니다.");
		
	} // test계좌이체
	

} // end class

SampleTXServiceTests

51 바이트를 집어 넣었는데, 1번 테이블은 100바이트 용량이라 성공적으로 들어갔고, 2번 테이블은 50바이트 용량이라 들어가지 않았다. 이런 경우 둘다 들어가지 않게끔 할 수 없을까?!

 

	@Transactional // TX 처리 수행 (All or Nothing)
	@Override
	public void 계좌이체(String data) throws Exception {
		log.info("계좌이체() invoked.");
		
		
		// 1. 소스계좌에서 이체금액만큼 차감 (DML 문장수행)
		this.mapper1.insertCol(data);
		
		// 2. 타겟계좌로 이체금액만큼 추가 (DML 문장수행)
		this.mapper2.insertCol(data);
		
	} // 계좌이체


impl에 @Transactional 추가해주자
이제 이런 경우 1번 테이블에 들어갔다가 롤백되서 결과적으로 둘 다 안 들어가게 된다!

이걸로 스프링도 끝 수고했습니다.