[Spring] 스프링 AOP 개념 및 적용 방법
- 📚 Spring/Spring 개념
- 2020. 8. 21. 09:28
📝 스프링 AOP 개념 및 적용 방법
| 1. AOP(Aspect Oriented Programming)
- Spring은 Spring Triangle 이라고 부르는 세가지 개념을 제공해준다. 각각 IoC, AOP, PSA를 일컫는다.
- AOP는 Aspect Oriented Programming의 약자로 '측면/양상 지향적인 프로그래밍' 이라는 의미이다.
class A {
method a(){
AAA
a가 하는 일
BBB
}
method b(){
AAA
a가 하는 일
BBB
}
}
class B {
method c(){
AAA
c가 하는 일
BBB
}
}
위와 같이 동일한 일을 하는 AAA ,BBB가 여러군대에서 사용되고 이렇게 흩어져 있으면 코드 변경이 필요한 경우 모든 부분을 일일히 수정을 해주어야 한다.
AOP는 그렇게 하지 않고 여러곳에서 사용하는 중복 코드를 떼어내어 분리하고, method a, b, c 는 자신이 해야할 작업만 하자는 개념이다.
여기서 '여러곳에서 사용되는 중복되는 코드'가 AOP에서 말하는 'aspect'라고 이해하면 된다.
| 2. 프록시 패턴
Spring AOP는 프록시 패턴이라는 디자인 패터을 사용해서 AOP 효과를 낸다.
프록시 패턴을 사용하면 어떤 기능을 추가하려 할 때 기존 코드를 변경하지 않고 기능을 추가할 수 있다.
어떤 클래스가 Spring AOP의 대상이라면 그 기존 클래스의 빈이 만들어질때 Spring AOP가 프록시(기능이 추가된 클래스)를 자동으로 만들고 원본 클래스 대신 프록시를 빈으로 등록한다.
그리고 원본 클래스가 사용되는 지점에서 프록시를 대신 사용한다.
Spring의 PetClinic 예제에서 OwnerRepository 인터페이스의 @Transactional 애노테이션이 이에 해당한다.
@Transactional 애노테이션이 붙어있으면 OwnerRepository 타입의 프록시가 새로 만들어지고 Spring AOP에 의해 자동으로 생성되는 OwnerRepository의 프록시에는 Transactional 애노테이션이 지시하는 코드가 삽입된다.
@Transactional 애노테이션에 의해 추가되는 기능은 다음과 같다.
JDBC에서 트랜잭션 처리를 하려면 SQL 실행문 앞뒤에 setAutoCommit()와 commit()/rollback() 코드가 항상 붙는데 @Transactional 애노테이션은 프록시에 자동으로 그 코드를 넣어서 반복, 중복되는 코드를 생략할 수 있게하는 것이다.
| 3. Spring AOP 적용 예제
LogExecutionTime 이라는 애노테이션을 만든다.
package org.springframework.samples.petclinic.owner;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
- @Target(ElementType.METHOD)
- 애노테이셔늘 메소드에 사용할 것이라는 설정
- @Retention(RetentionPolicy.RUNTIME)
- 애노테이션이 RUNTIME 까지 유지되도록 설정
LogAspect도 만든다.
package org.springframework.samples.petclinic.owner;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@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();
// joinPoint는 타겟 메서드이다.
// @LogExecutionTime 애노테이션이 붙어있는 타겟 메소드를 실행
Object proceed = joinPoint.proceed();
stopWatch.stop();
logger.info(stopWatch.prettyPrint());
return proceed;
}
}
그리고 성능측정할 메서드 마다 @LogExecutionTime를 붙여준다
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
private VisitRepository visits;
public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
this.owners = clinicService;
this.visits = visits;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
@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";
}
}
그리고 해당 메서드를 실행하면 아래와 같이 OwnerController의 메소드 실행 시 LogAspect에 추가한 메소드 성능 측정 결과가 콘솔에 출력된다.
2020-08-22 08:59:26.747 INFO 7304 --- [nio-8080-exec-1] o.s.samples.petclinic.owner.LogAspect : StopWatch '': running time = 137800 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000137800 100%
이것이 aspect이며 Spring이 제공해주는 애노테이션 기반의 AOP이다.
이렇게 적용한 AOP는 내부적으로 프록시 패턴 기반으로 동작한다.
즉, OwnerController에는 애노테이션 외에는 아무런 코드가 추가되지 않았다.
'📚 Spring > Spring 개념' 카테고리의 다른 글
Intellij 단축키 (0) | 2020.08.23 |
---|---|
[Spring] 스프링 PSA (0) | 2020.08.23 |
[Spring] 의존성 주입(Dependency Injection) (0) | 2020.08.21 |
[Spring] Bean (0) | 2020.08.20 |
스프링 IoC Container와 Bean (0) | 2020.07.29 |