Aspect Oriented Programming
기존 코드 건드리지 않고 새 기능 추가하기
프록시 패턴
Main에서의 클래스
main\java\org...\proxy\Store
package org.springframework.samples.petclinic.proxy;
public class Store {
Payment payment;
public Store(Payment payment) {
this.payment = payment;
}
public void buySomething(){
payment.pay(100);
}
}
main\java\org...\proxy\Cash
package org.springframework.samples.petclinic.proxy;
public class Cash implements Payment{
@Override
public void pay(int amount) {
System.out.println(amount + " 현금 결제");
}
}
main\java\org...\proxy\Payment
package org.springframework.samples.petclinic.proxy;
public interface Payment {
void pay(int amount);
}
main\java\org...\proxy\CashPerf
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());
}
} //일종의 프록시
Test 확인
test\java\org...\proxy\StoreTest
package org.springframework.samples.petclinic.proxy;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class StoreTest {
@Test
public void testPay(){
//Payment cashPerf = new Cash(); //(1)
Payment cashPerf = new CashPerf(); **//(2)**
Store store = new Store(cashPerf);
store.buySomething(100);
}
}
(1)
(2) - 프록시 클래스를 사용하도록 함.
원래는 Cash가 Bean으로 등록이 되어야 하는데 내가 만들고 싶은 CashPerf(proxy)가 자동으로 생겨서 Cash 대신에 등록이 되고 Client가 CashPerf를 대신 쓰게 되는 일이 Spring 내부에서 발생한다.
스프링 AOP 기반의 @Transactional
이 위와 같은 일을 한다.
@LogExecutionTime
설정(어디에 적용할지)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
//위치 : java\org..\owner\LogExecutionTime
@Target
: 어디에 쓸 수 있는지.@Retension
: @
정보를 언제까지 유지할 것인가.java\org..\owner\LogAspect
: 스프링 AOP(프록시 패턴 기반)
Proxy 패턴(스프링 AOP)
@Component
@Aspect
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
**@Around("@annotation(LogExecutionTime)")**
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object proceed = joinPoint.proceed();
stopWatch.stop();
logger.info(stopWatch.prettyPrint());
return proceed;
}
}
@LogExecutionTime
이 붙은 메소드(타겟)@Around("@annotaion(LogExecutionTime)")
: LogExecutionTime이라는 @
주변에 적용하겠다.@Around
외에도 @After
, @Before
등.@annotation
외에도 @Bean
, Method들이 진행되는 시점 등.java\org..\owner\OwnerController
에서 성능(시간)을 측정할 메소드 위에 @LogExecutionTime
을 추가하자.
Target(타겟)
@GetMapping("/owners/new")
@LogExecutionTime
public String initCreationForm(Map<String, Object> model) {
Owner owner = new Owner();
model.put("owner", owner);
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
@PostMapping("/owners/new")
@LogExecutionTime
public String processCreationForm(@Valid Owner owner, BindingResult result) {
if (result.hasErrors()) {
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
else {
this.owners.save(owner);
return "redirect:/owners/" + owner.getId();
}
}
@GetMapping("/owners/find")
@LogExecutionTime
public String initFindForm(Map<String, Object> model) {
model.put("owner", new Owner());
return "owners/findOwners";
}
@LogExecutionTime
이 명시된 메소드들이 실행될 때 각 메소드들이 실행되는 시간, 즉 성능을 Runtime으로 표시한다.