스프링 삼각형
스프링을 이해하는 데는 POJO(Plain Old Java Object)를 기반으로 스프링 삼각형이라고 불리는 IoC/DI, AOP, PSA의 이해가 필수이다.
이전 게시글에서 IoC/DI에 대해 다루었다.
https://shout-to-my-mae.tistory.com/425
AOP (Aspect-Oriented Programming)
관점(Aspect) 지향 프로그래밍을 뜻한다.
스프링 AOP는 로직(code) 주입이다.
스프링 DI가 의존성에 대한 주입이라면, 스프링 AOP는 로직 주입이다.
입금, 출금, 이체에서 로깅, 보안, 트랜잭션 기능이 반복적으로 나타난다.
다수의 모듈에 공통적으로 나타나는 부분이 존재하는데, 이를 횡단 관심사(cross-cutting concern)라고 한다.
코드 = 핵심 관심사 + 횡단 관심사
핵심 관심사는 모듈의 핵심 기능으로 모듈별로 다르다.
횡단 관심사는 모듈 별로 반복되어 중복해서 나타나는 부분이다.
반복, 중복은 분리해서 한 곳에서 관리해야한다. => AOP
로직을 주입할 수 있는 곳
Around, Before, After, AfterReturning, AfterThrowing으로 총 5군데이다.
@Aspect
public class MyAspect{
@Before("execution(public void aop002.Person.runSomething ())")
public void before(JointPoint joinPoint){
System.out.println("start to run");
}
}
- @Aspect : 관점, 해당 클래스를 AOP에서 사용하겠다는 뜻이다.
공통 기능을 Aspect로 분리한다.
- @Before: 대상 메서드 실행 전에 메서드를 실행하겠다는 뜻이다.
아래에서 더 자세히 살펴보자.
AOP 용어
- Aspect : 관점, 측면
- Advisor : 조언자 (이제 안씀)
- Advice : 조언
- JoinPoint : 결합점
- Pointcut : 자르는 점
Pointcut : Aspect 적용 위치 지정자
Pointcut : 횡단 관심사를 적용할 타깃 메서드이다.
@Aspect
public class MyAspect{
@Before("execution(* runSomething ())") // 여기
public void before(JointPoint joinPoint){
System.out.println("start to run");
}
}
위 코드에서는 * runSomething() 이다.
형식 : [접근제한자패턴] 리턴타입패턴 [패키지&클래스패턴]메서드이름패턴(파라미터패턴) [throws 예외패턴]
예시 : public void aop002.Person.work()
예시 : * runSomething()
JoinPoint : 연결점, Aspect 적용이 가능한 지점
JoinPoint : Pointcut의 부분집합이다.
- 광의의 JoinPoint : Aspect 적용이 가능한 모든 지점
- 협의의 JoinPoint : 호출된 객체의 메서드
@Aspect
public class MyAspect{
@Before("execution(* runSomething ())")
public void before(JointPoint joinPoint){ // 여기 파라미터
System.out.println("start to run");
}
}
위 코드에서 JointPoint joinPoint 이다.
joinPoint를 이용하면 실행 시점에 실제 호출된 메서드가 무엇인지, 실제 호출된 메서드를 소유한 객체가 무엇인지, 또 호출된 메서드의 파라미터는 무엇인지 정보를 확인할 수 있다.
Advice : Pointcut에 적용할 메서드 + When
Advice : Pointcut에 언제 무엇을 적용할지 정의한 메서드
@Aspect
public class MyAspect{
// Adivce 시작
@Before("execution(* runSomething ())")
public void before(JointPoint joinPoint){
System.out.println("start to run");
}
// Adivce 끝
}
코드에서는 @Before 부터 before()메서드 부분이다.
지정된 pointcut이 시작되기 전 before() 메서드를 실행하라는 뜻이다.
Around, Before, After, AfterReturning, AfterThrowing 5가지가 있다.
Aspect
Aspect : Advice들(When, What) + Pointcut들(Where)
=> When + Where + What
Advisor
한 개의 Advice + 한 개의 Pointcut
(이제 거의 안쓴다..)
참고 - Pointcut 선언 중복 제거
@Aspect
public class MyAspect{
@Pointcut("execution(* runSomething())")
private void iampc(){
// 무엇을 작성해도 의미없음
}
@Before("iampc()")
public void before(JoinPoint joinPoint){
System.out.println("start to run");
}
@After("iampc()")
public void afterRun(JoinPoint joinPoint){
System.out.println("running is over");
}
}
리팩토링을 통해 @Pointcut 선언 부분 중복을 제거할 수 있다.
AOP 예시 코드
사람은 일을 하는데, 일을 하기 전 메세지를 출력해야한다고 가정하자.
public interface Person {
void work();
}
횡단 관심사 - Aspect
@Aspect
public class MyAspect{
@Before("execution(public void aop002.Person.work ())")
public void before(JointPoint joinPoint){
System.out.println("일을 하기 시작한다.");
}
}
매 클래스마다 System.out.println("일을 하기 시작한다.");를 추가하지 말고,
AOP를 적용해 중복을 줄이고 단일 책임 원칙(SRP)을 지키자.
Person 클래스에서 중복되는 부분은 MyAspect 클래스에 모아둔다.
public class Singer implments Person{
public void work(){
System.out.println("노래를 부른다");
}
}
핵심 관심사이다.
public class Dancer implments Person{
public void work(){
System.out.println("춤을 춘다");
}
}
핵심 관심사이다.
설정 클래스 - 빈 등록
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public MyAspect myAspect() {
return new MyAspect();
}
@Bean
public Person singer() {
return new Singer();
}
@Bean
public Person dancer() {
return new Dancer();
}
}
스프링에 빈을 등록하여 객체의 생성과 의존성 주입을 스프링 프레임워크에 위임한다.
@EnableAspectJAutoProxy를 사용해서 AspectJ 자동 프록시 생성을 활성화한다.
애플리케이션
public class Driver {
public static void main(String[] args){
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person singer1 = context.getBean("singer", Person.class);
Person dancer1 = context.getBean("dancer", Person.class);
System.out.println(singer1.work());
System.out.println(dancer1.work());
}
}
출력
일을 하기 시작한다.
노래를 부른다
일을 하기 시작한다.
춤을 춘다
AOP : 프록시(Proxy) 사용
스프링 AOP는 프록시를 사용하여 간접적으로 메서드를 호출한다.
프록시는 실제 대상 객체를 감싸는 Wrapper 객체로, 클라이언트의 요청을 가로채어 실제 대상 객체의 메서드 호출 전후에 필요한 로직을 수행할 수 있다.
클라이언트는 실제 대상 객체를 직접 호출하는 것이 아니라 프록시 객체를 호출한다.
호출하는 쪽(work() 호출)에서나 호출당하는 쪽(singer, dancer) 그 누구도 프록시가 존재하는지 모른다.
오직 스프링 프레임워크만 프록시의 존재를 안다.
프록시 사용 장점
AOP가 프록시 기반으로 움직이기 때문에 얻는 장점은 아래와 같다.
- 횡단 관심사 기능 추가 시 핵심 관심사 코드를 수정하지 않고도 프록시를 통해 해당 기능을 적용할 수 있다.
- 스프링 AOP는 런타임에 동적으로 프록시 객체를 생성하기 때문에 컴파일 타임에 코드를 수정할 필요가 없다.
- 횡단 관심사를 모듈화하고 개발자는 핵심 로직에만 집중할 수 있다.
프록시를 사용하므로 AOP 사용시에 @EnableAspectJAutoProxy 어노테이션을 붙이거나 아래 설정을 해주어야한다.
<aop:aspectj-autoproxy />
스프링 AOP의 핵심
- 스프링 AOP는 인터페이스 기반이다.
스프링 AOP는 실제 대상 객체와 동일한 인터페이스를 구현하여 클라이언트와의 호환성을 유지한다.
- 스프링 AOP는 프록시 기반이다.
프록시 기반으로 동작하기 때문에 기존 코드 변경 없이 AOP를 적용할 수 있다.
- 스프링 AOP는 런타임 기반이다.
런타임에 동적으로 프록시를 생성하고 AOP를 적용하기 때문에 컴파일 시간에 코드를 수정할 필요가 없다.
PSA (Portable Service Abstraction)
스프링 프레임워크에서 제공하는 일관성 있는 서비스 추상화이다.
다양한 기술과 구현체를 추상화하여 개발자가 일관된 방식으로 코드를 작성할 수 있도록 돕는다.
이를 통해 특정 구현체에 종속되지않고, 필요에 따라 구현체를 쉽게 교체할 수 있다.
JDBC
서비스 추상화의 예시로 JDBC를 들 수 있다.
JDBC라는 표준 스펙이 있기 때문에 Oracle, MySQL 어떤 것을 사용하던 Connection, Statement, ResultSet을 이용해 공통된 방식으로 코드를 작성할 수 있다.
디자인 패턴의 어댑터 패턴을 활용한다.
스프링 프레임워크에서는 서비스 추상화를 위해 다양한 어댑터를 제공한다.
각 어댑터를 제공하면서 어떤 기술을 채택하던 일관된 방식으로 코드를 작성할 수 있으며 다른 기술로 변경시에 큰 변화 없이 교체해서 사용할 수 있다.
ORM
스프링은 JPA(Java Persistence API)라는 ORM 표준에 대한 추상화를 제공한다.
캐시
스프링은 다양한 캐시 솔루션(EhCache, Redis)에 대한 추상화를 제공한다.
Reference
https://m.yes24.com/Goods/Detail/17350624
+ 책 내용 기반으로 추가적으로 내용을 작성했는데 오개념이 있다면 지적 부탁드립니다~!!
'Spring > 객체지향' 카테고리의 다른 글
스프링 삼각형 : IoC(제어의 역전) / DI(의존성 주입) + POJO 지향 (2) | 2024.04.17 |
---|---|
코드에 SRP 원칙 적용 후 Mock 테스트 작성하기 (0) | 2024.04.06 |
객체 지향과 디자인 패턴 (0) | 2024.04.02 |
객체 지향 설계 5원칙: SOLID (0) | 2024.04.01 |
Java가 확장한 객체 지향 (abstract, 생성자, static, final, this, super) (0) | 2024.03.26 |