Spring νŠΉμ§•(3): AOP

μ½”λ”©ν•˜λŠ” 포둜리·2022λ…„ 2μ›” 18일
0

Spring

λͺ©λ‘ 보기
7/10
post-thumbnail

πŸ“Œ 1. AOP(Aspect-Oriented Programming)λž€?


πŸ“Ž AOPλž€?

AOPλŠ” 관점 지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ΄λΌκ³  λΆˆλ¦°λ‹€. μ–΄λ–€ λ‘œμ§μ„ κΈ°μ€€μœΌλ‘œ 핡심적인 관점, 뢀가적인 κ΄€μ μœΌλ‘œ λ‚˜λˆ„μ–΄μ„œ 보고 κ·Έ 관점을 κΈ°μ€€μœΌλ‘œ 각각 λͺ¨λ“ˆν™”ν•˜κ² λ‹€λŠ” 것이닀. AOPμ—μ„œ 각 관점을 κΈ°μ€€μœΌλ‘œ λ‘œμ§μ„ λͺ¨λ“ˆν™”ν•œλ‹€λŠ” 것은 λͺ¨λ“œλ“€μ„ λΆ€λΆ„μ μœΌλ‘œ λ‚˜λˆ„μ–΄μ„œ λͺ¨λ“ˆν™”ν•˜κ² λ‹€λŠ” μ˜λ―Έμ΄λ‹€. μ΄λ•Œ, μ†ŒμŠ€ μƒμ—μ„œ λΉ„μŠ·ν•œ κΈ°λŠ₯을 ν•˜λŠ” λΆ€λΆ„(λΉ„μŠ·ν•œ λ©”μ„œλ“œ, μ½”λ“œ)이 μžˆλŠ”λ°, 이것은 흩어진 관심사(Crosscutting Concerns)라 λΆ€λ₯Έλ‹€.

μ•„λž˜ κ·Έλ¦Όμ—μ„œ 각 λ°•μŠ€μ•ˆμ— μƒ‰μΉ λ˜μ–΄ μžˆλŠ” 뢀뢄이 흩어진 관심사이닀. (각 ν΄λž˜μŠ€μ•ˆμ— κ³΅ν†΅μ μœΌλ‘œ μ‚¬μš©ν•˜κ³  μžˆλŠ” λ©”μ†Œλ“œ ν˜Ήμ€ μ½”λ“œ) μ΄λ•Œ λ§Œμ•½ λ…Έλž€μƒ‰ κΈ°λŠ₯을 μˆ˜μ •ν•΄μ•Ό ν•œλ‹€λ©΄ 각각 클래슀의 λ…Έλž€μƒ‰ κΈ°λŠ₯을 μˆ˜μ •ν•΄μ£Όμ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— μœ μ§€λ³΄μˆ˜λ©΄μ—μ„œ λΆˆλ¦¬ν•˜λ‹€.

이것을 ν•΄κ²°ν•˜λŠ” 방법이 AOP이닀. 흩어진 관심사듀을 λͺ¨μ„λ•Œ AOP의 Aspect을 μ‚¬μš©ν•œλ‹€. 각 Concernλ³„λ‘œ Aspect을 λ§Œλ“€μ–΄μ£Όκ³ , μ–΄λŠ ν΄λž˜μŠ€μ—μ„œ μ‚¬μš©ν•˜λŠ” 지 μž…λ ₯ν•΄μ£ΌλŠ” 방식이닀. μ•„λž˜μ˜ 그림이 Aspect둜 λͺ¨λ“ˆν™” ν•œ 것을 보여쀀 것이닀.
각 λͺ¨λ“ˆμ—λŠ” Advice와 Poincut이 λ“€μ–΄μžˆλ‹€. Adviceλž€ ν•΄μ•Όν•  일, κΈ°λŠ₯을 λ‚˜νƒ€λ‚Έλ‹€. Pointcutμ΄λž€ 어디에 μ μš©ν•΄μ•Όν•˜λŠ”μ§€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 것이닀. (AλΌλŠ” 클래슀의 WλΌλŠ” λ©”μ„œλ“œ) Targetμ΄λΌλŠ” κ°œλ…λ„ μžˆλŠ”λ°, Targetμ΄λž€ 각각 클래슀λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 것이닀. 즉, μ μš©μ΄λ˜λŠ” λŒ€μƒμ„ λœ»ν•˜λŠ” μš©μ–΄μ΄λ‹€.

πŸ“Ž AOP μš©μ–΄

πŸ‘‰ Aspect: 관점

πŸ‘‰ Advice: 핡심기λŠ₯에 λΆ€μ—¬λ˜λŠ” λΆ€κ°€κΈ°λŠ₯ (μœ„μΉ˜ λ©”μ„œλ“œμ— 적용될 λΆ€κ°€ κΈ°λŠ₯)

  • Around (Advice): λŒ€μƒ 객체의 λ©”μ„œλ“œ μ‹€ν–‰ μ „, ν›„ 및 μ˜ˆμ™Έ λ°œμƒ λͺ¨λ‘ μ‹€ν–‰ν•œλ‹€.
  • Before (Advice): λŒ€μƒ 객체의 λ©”μ„œλ“œλ‘œ, λ©”μ„œλ“œ ν˜ΈμΆœμ „μ— μˆ˜ν–‰ν•œλ‹€.
  • After (Advice): λŒ€μƒ 객체의 λ©”μ„œλ“œ 싀행도쀑 μ˜ˆμ™Έ λ°œμƒ 여뢀와 상관없이 λ©”μ„œλ“œ μ‹€ν–‰ ν›„ μ‹€ν–‰ν•œλ‹€.
  • AfterReturning (Advice): λŒ€μƒ 객체의 λ©”μ„œλ“œκ°€ μ‹€ν–‰ 도쀑 μ˜ˆμ™Έμ—†μ΄ μ‹€ν–‰ μ„±κ³΅ν•œ κ²½μš°μ— μ‹€ν–‰ν•œλ‹€.
  • AfterThrowing (Advice): λŒ€μƒ 객체의 λ©”μ„œλ“œκ°€ μ‹€ν–‰ 도쀑 μ˜ˆμ™Έκ°€ λ°œμƒν•œ κ²½μš°μ— μ‹€ν–‰ν•œλ‹€.

πŸ‘‰ Pointcut: Aspect 적용 μœ„μΉ˜ μ§€μ •μž (Advice)λ₯Ό 어디에 μ μš©ν• μ§€λ₯Ό κ²°μ •

πŸ‘‰ Advisor: Advice + Pointcut

πŸ‘‰ Joinpoint: Aspectκ°€ μ μš©ν•œ 지점

πŸ“Ž AOP κ΅¬ν˜„ μˆœμ„œ

  1. pon.xml νŒŒμΌμ— AOP κ΄€λ ¨ dependency μΆ”κ°€
  2. AOPμ„€μ • xml 파일(servlet-context.xml)에 aop autoproxy μ§€μ‹œ
  3. target λ©”μ†Œλ“œμ— μΆ”κ°€ ν•  Aspect 생성
  4. κ΅¬ν˜„ν•˜κΈ°

πŸ“Ž execution λͺ…μ‹œμž

execution(μˆ˜μ‹μ–΄νŒ¨ν„΄ λ¦¬ν„΄νƒ€μž…νŒ¨ν„΄ ν΄λž˜μŠ€μ΄λ¦„νŒ¨ν„΄.λ©”μ„œλ“œμ΄λ¦„νŒ¨ν„΄())

각 νŒ¨ν„΄μ€ *을 μ΄μš©ν•˜μ—¬ λͺ¨λ“ κ°’을 ν‘œν˜„ν•  수 μžˆλ‹€.

πŸ‘‰ νŒ¨ν‚€μ§€

  • com.spring.aop β†’ com.spring.aopνŒ¨ν‚€μ§€λ₯Ό νƒ€κ²Ÿν•œλ‹€λŠ” 의미
  • com.spring.aop.. β†’ com.spring.aop둜 μ‹œμž‘ν•˜λŠ” ν•˜μœ„μ˜ λͺ¨λ“  νŒ¨ν‚€μ§€λ₯Ό νƒ€κ²Ÿν•œλ‹€λŠ” 의미

πŸ‘‰ λ¦¬ν„΄νƒ€μž…

  • * β†’ λͺ¨λ“  리턴 νƒ€μž… νƒ€κ²Ÿ
  • void > 리턴 νƒ€μž…μ΄ void인 λ©”μ„œλ“œλ§Œ 타켓
  • !void > 리턴 νƒ€μž…μ΄ voidκ°€ μ•„λ‹Œ λ©”μ„œλ“œλ§Œ νƒ€κ²Ÿ

πŸ‘‰ 맀개 λ³€μˆ˜ 지정

  • (..) > 1개 μ΄μƒμ˜ λͺ¨λ“  νŒŒλΌλ―Έν„° 타켓
  • (*) > 1개의 νŒŒλΌλ―Έν„°λ§Œ 타켓
  • (*,*) > 2개의 νŒŒλΌλ―Έν„°λ§Œ νƒ€κ²Ÿ
  • (String,*) > 2개의 νŒŒλΌλ―Έν„°μ€‘ 첫번째 νŒŒλΌλ―Έν„°κ°€ Stringνƒ€μž…λ§Œ νƒ€κ²Ÿ

πŸ‘‰ μƒ˜ν”Œ μ˜ˆμ‹œ

  • execution(void set*(..))
    리턴 νƒ€μž…μ΄ void이고 λ©”μ„œλ“œ 이름이 set으둜 μ‹œμž‘ν•˜κ³  νŒŒλΌλ―Έν„°κ°€ 0개 이상인 λ©”μ„œλ“œ νƒ€κ²Ÿ
  • execution(* abc.*.*())
    abcνŒ¨ν‚€μ§€μ— μ†ν•œ νŒŒλΌλ―Έν„°κ°€ μ—†λŠ” λͺ¨λ“  λ©”μ„œλ“œ νƒ€κ²Ÿ
  • execution(* abc..*.*(..))
    abcνŒ¨ν‚€μ§€ 및 ν•˜μœ„ νŒ¨ν‚€μ§€μ— μžˆλŠ” νŒŒλΌλ―Έν„°κ°€ 0개 이상인 λ©”μ„œλ“œ νƒ€κ²Ÿ
  • execution(Long com.spring.aop.Boss.work(..))
    리턴 νƒ€μž…μΈ Long인 com.spring.aop νŒ¨ν‚€μ§€ μ•ˆμ˜ Boss클래슀의 work λ©”μ„œλ“œ νƒ€κ²Ÿ
  • execution (* get*(*))
    이름이 get으둜 μ‹œμž‘ν•˜κ³  νŒŒλΌλ―Έν„°κ°€ ν•œ 개인 λ©”μ„œλ“œ νƒ€κ²Ÿ
  • execution(* get*(*,*))
    이름이 get으둜 μ‹œμž‘ν•˜κ³  νŒŒλΌλ―Έν„°κ°€ 2개인 λ©”μ„œλ“œ νƒ€κ²Ÿ
  • execution(* read*(Integer,..))
    λ©”μ„œλ“œ 이름이 read λ‘œμ‹œμž‘ν•˜κ³ , 첫번째 νŒŒλΌλ―Έν„° νƒ€μž…μ΄ Integer이며 ν•œκ°œ μ΄μƒμ˜ νŒŒλΌλ―Έν„°λ₯Ό κ°–λŠ” λ©”μ„œλ“œ νƒ€κ²Ÿ


πŸ“Œ 2. AOP μ˜ˆμ‹œ


πŸ“Ž @Before

@Component
@Aspect					// AOP μ‚¬μš© μ„ μ–Έ μ–΄λ…Έν…Œμ΄μ…˜
public class Advice {

	private static final Logger logger = LoggerFactory.getLogger(Advice.class);
	
	@Pointcut("execution(void com.spring.aop.*.work())") // μ€‘λ³΅λ˜λŠ” execution을 기술
	public void pointcut() {
		 // νŠΉμ •ν•œ μ˜λ―Έκ°€ μ—†λ‹€.
	}
	
	// λ©”μ„œλ“œ 호좜 μ „
	//@Before("execution(void com.spring.aop.*.work())")
	@Before("pointcut()")
	public void before() {
		logger.info("AOP Before λ©”μ„œλ“œ 호좜 : μΆœκ·Όν•œλ‹€.");
	}
	
}

πŸ‘‰ com.spring.aop. νŒ¨ν‚€μ§€ 파일

package com.spring.aop;

import org.springframework.stereotype.Component;

//ν…ŒμŠ€νŠΈ λͺ©μ μ˜ 클래슀1
@Component
public class Boss {

	public void work() {
		System.out.println("사μž₯의 일을 ν•œλ‹€.");
	}
	
	public void getWorkingTime() {
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void getInfo(String title , int salary) {
		System.out.println("\n===================");
		System.out.println("Boss클래슀 getInfoλ©”μ„œλ“œ 호좜");
		System.out.println("직급 : " + title + " / κΈ‰μ—¬ : " + salary);
		System.out.println("===================\n");
	}	

πŸ“Ž @After

@Component
@Aspect					// AOP μ‚¬μš© μ„ μ–Έ μ–΄λ…Έν…Œμ΄μ…˜
public class Advice {

	private static final Logger logger = LoggerFactory.getLogger(Advice.class);
	
	@Pointcut("execution(void com.spring.aop.*.work())") // μ€‘λ³΅λ˜λŠ” execution을 기술
	public void pointcut() {
		 // νŠΉμ •ν•œ μ˜λ―Έκ°€ μ—†λ‹€.
	}
	
	// λ©”μ„œλ“œ 호좜 ν›„
	//@After("execution(void com.spring.aop.*.work())")
	@After("pointcut()")
	public void after() {
		logger.info("AOP After λ©”μ„œλ“œ 호좜 : ν‡΄κ·Όν•œλ‹€.\n");
	}
	
}

πŸ“Ž @Around

@Component
@Aspect					// AOP μ‚¬μš© μ„ μ–Έ μ–΄λ…Έν…Œμ΄μ…˜
public class Advice {

	private static final Logger logger = LoggerFactory.getLogger(Advice.class);
	
	@Pointcut("execution(void com.spring.aop.*.work())") // μ€‘λ³΅λ˜λŠ” execution을 기술
	public void pointcut() {
		 // νŠΉμ •ν•œ μ˜λ―Έκ°€ μ—†λ‹€.
	}
	
	// λ©”μ„œλ“œ 호좜 μ „ν›„
	@Around("execution(void com.spring.aop.*.getWorkingTime())")
	public void around(ProceedingJoinPoint pjp) {
		
		logger.warn("===========================");
		// λ©”μ„œλ“œ 호좜 μ „
		long startTime = System.currentTimeMillis();
		
		// ProceedingJoinPoint 객체의 proceed()λ©”μ„œλ“œλ‘œ νƒ€κ²Ÿλ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.
		try {
			pjp.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		
		// λ©”μ„œλ“œ 호좜 ν›„
		long endTime = System.currentTimeMillis();
		
		logger.warn("업무 μ†Œμš”μ‹œκ°„ : " + (endTime-startTime) + " 초");
		logger.warn("===========================\n");
		
	}
	
	
}

πŸ“Ž @AfterReturning

@Component
@Aspect					// AOP μ‚¬μš© μ„ μ–Έ μ–΄λ…Έν…Œμ΄μ…˜
public class Advice {

	private static final Logger logger = LoggerFactory.getLogger(Advice.class);
	
	@Pointcut("execution(void com.spring.aop.*.work())") // μ€‘λ³΅λ˜λŠ” execution을 기술
	public void pointcut() {
		 // νŠΉμ •ν•œ μ˜λ―Έκ°€ μ—†λ‹€.
	}
	
	// νƒ€κ²Ÿ λ©”μ„œλ“œκ°€ μ„±κ³΅μ μœΌλ‘œ μ‹€ν–‰ν•œ ν›„
	@AfterReturning("execution(void com.spring.aop.*.getInfo(..))")
	//JoinPoint객체λ₯Ό ν†΅ν•΄μ„œ νƒ€κ²Ÿλ©”μ„œλ“œλ‘œλΆ€ν„° μ „λ‹¬λ˜λŠ” νŒŒλΌλ©”νƒ€λ₯Ό 받을 수 μžˆλ‹€.
	public void AfterReturning(JoinPoint jp) {
		logger.info(Arrays.toString(jp.getArgs())); // λ©”μ„œλ“œμ˜ νŒŒλΌλ©”νƒ€λ₯Ό 확인
		logger.info(jp.getKind());				   // λ©”μ„œλ“œμ˜ μ’…λ₯˜ 확인
		logger.info(jp.getSignature().getName());   // λ©”μ„œλ“œμ— λŒ€ν•œ μ„€λͺ…
		logger.info(jp.getTarget().toString());     // λŒ€μƒ 객체λ₯Ό λ°˜ν™˜
		logger.info(jp.getThis().toString());	   // ν”„λ‘μ‹œ 객체λ₯Ό λ°˜ν™˜
		logger.info("");
	}
	
}

πŸ“Ž @AfterThrowing

@Component
@Aspect					// AOP μ‚¬μš© μ„ μ–Έ μ–΄λ…Έν…Œμ΄μ…˜
public class Advice {

	private static final Logger logger = LoggerFactory.getLogger(Advice.class);
	
	@Pointcut("execution(void com.spring.aop.*.work())") // μ€‘λ³΅λ˜λŠ” execution을 기술
	public void pointcut() {
		 // νŠΉμ •ν•œ μ˜λ―Έκ°€ μ—†λ‹€.
	}
	
	// νƒ€κ²Ÿ λ©”μ„œλ“œμ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•œ ν›„
	@AfterThrowing("execution(void com.spring.aop.Employee.getError())")
	public void AfterThrowing(){
		logger.info("AOP AfterThrowingλ©”μ„œλ“œ 호좜\n");
	}
	
	
}



πŸ“– μ°Έκ³ 

0개의 λŒ“κΈ€