관점 지향 프로그래밍
여러 오브젝트에 나타나는 공통적인 부가 기능을 모듈화하여 재사용하는 기법

로그를 남기거나, 핵심기능을 사용할 때 인증, 인가를 한다던가, 비즈니스 로직의 트랜잭션 처리를 하는 작업은 모든 핵심기능에 공통적으로 사용되는 기능이다. AOP는 비즈니스 로직을 나타내는 핵심기능을 작성할 때마다 공통적으로 작성하는 부가기능을 분리해서 프로그래밍하는 기법이다.
Aspect

JoinPoint
Advice
Pointcut
Weaving
Target


// 의존성 주입
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
// 간단한 인터페이스를 구현하는 클래스 하나를 빈으로 정의
public interface EventService {
public void created();
public void operation();
public void deleted();
}
@Component
public class SimpleServiceEvent implements EventService {
@Override
public void created() {
long begin = System.currentTimeMillis();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("created");
System.out.println(System.currentTimeMillis() - begin);
}
@Override
public void operation() {
System.out.println(System.currentTimeMillis() - begin);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("operation");
System.out.println(System.currentTimeMillis() - begin);
}
@Override
public void deleted() {
System.out.println("deleted");
}
}
@Component
public class AppRuner implements ApplicationRunner {
@Autowired
EventService eventService;
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.created();
eventService.operation();
eventService.deleted();
}
}
created
1011
operation
2004
deleted
// 위 코드에서는 수행 시간을 재는 아래 코드가 여러 곳에서 반복 사용되고 있음
long begin = System.currentTimeMillis();
... (수행) ...
System.out.println(System.currentTimeMillis() - begin);
// 위 코드를 하나로 묶기
// Aspect 정의
@Component
@Aspect
public class PerfAspect {
// Advice 정의 (Around 사용)
// Point Cut 표현식
// (com.example.demo 밑에 있는 모든 클래스 중 EventService 안에 들어있는 모든 메쏘드에 이 행위를 적용하라.)
@Around("execution(* com.example..*.EventService.*(..))")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
// AOP를 위 코드에서 적용했으므로, 수행시간 출력 부분 코드 삭제
@Component
public class SimpleServiceEvent implements EventService {
@Override
public void created() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("created");
}
@Override
public void operation() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("operation");
}
@Override
public void deleted() {
System.out.println("deleted");
}
}
created
1011
operation
2004
deleted
3000
created() 와 operation()에만 적용하고 싶은데 delete()에도 적용이 됐다 !// Annotation 만들기
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface PerfLogging {
}
// Aspect 클래스 수정
@Component
@Aspect
public class PerfAspect {
// PerfLogging 어노테이션이 달린 곳만 포인트컷 수정
@Around("@annotation(PerfLogging)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
// class method 수정
@Component
public class SimpleServiceEvent implements EventService {
@PerfLogging
@Override
public void created() {
...
}
@PerfLogging
@Override
public void operation() {
...
}
@Override
public void deleted() {
...
}
}
created
1011
operation
2004
deleted
package org.springframework.samples.petclinic.proxy;
public interface Payment {
void pay(int amount);
}
package org.springframework.samples.petclinic.proxy;
public class Cash implements Payment{
public void pay(int amount){
System.out.println(amount + " 현금 결제");
}
}
package org.springframework.samples.petclinic.proxy;
import org.springframework.util.StopWatch;
//프록시 클래스
public class CashPerf implements Payment{
Payment cash = new Cash();
@Override
public void pay(int amount) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
cash.pay(amount);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
}
package org.springframework.samples.petclinic.proxy;
public class Store {
Payment payment;
public Store(Payment payment) {
this.payment = payment;
}
public void butSomething(int amount){
payment.pay(amount);
}
}
package org.springframework.samples.petclinic.proxy;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class StoreTest {
//Cash 클래스와 Store클래스는 바뀌지 않았지만 이 코드 앞뒤로 성능 측정을 하도록 프록시 클래스를 이용함.
@Test
public void testPay(){
Payment cashPerf = new Cash();
Store store = new Store(cashPerf);
store.butSomething(100);
}
}
https://dailyheumsi.tistory.com/202