의존성이란 무엇인가?
한 클래스가 다른 클래스의 메소드나 데이터를 사용하는 경우를 '의존한다.'라고 말한다.
코드레벨에서의 의존성
1. 생성자 의존성
- 클래스가 다른 클래스의 인스턴스를 필요로 할 때, 생성자를 통해 직접 생성하는 경우
class Engine {
}
class Car {
private Engine engine;
public Car() {
this.engine = new Engine(); // Engine 클래스에 대한 의존성
}
}
!발생할 수 있는 단점
Car 객체 생성자에서 Engine 생성자를 new를 통해 직접 생성하고 있음
--> Car가 Engine에 직접적으로 의존하고 있고, 클래스 간에 너무 강하게 결합되어 있다.
--> Engine 클래스의 구현을 변경하거나, 다른 종류의 Engine을 사용할 때 Car 클래스 코드를 수정해야함
--> 결국 Car클래스 내부 코드를 Engine에 대한 변경이 있을 때마다 수정해야 하기 때문에 유연성이 낮아짐
2. 메소드 파라미터 의존성
- 메소드 실행될 때, 필요한 객체를 파라미터로 전달 받음
class Printer {
void print(Document document) { // Document 객체에 의존
// 문서 출력 로직
}
}
!발생할 수 있는 단점
- 많은 파라미터를 요구할 수록, 직접 파라미터를 명시하기 때문에 호출이 복잡해짐
- 동일한 의존성을 필요로 하는 메소드가 여러개 있을 때, 코드 중복이 많아질 수 있음
DI(Dependency Injection) - 의존성 주입
Why?
- 클래스 간의 결합도를 낮춰서 객체의 독립성을 높이고, 코드의 유연성과 확장성을 높이기 위해서 사용
How?
- 외부에서 의존성을 주입해서 클래스 간의 결합도를 낮춤
interface Engine {
void start();
}
class GasolineEngine implements Engine {
public void start() {
System.out.println("가솔린 엔진이 작동합니다.");
}
}
class ElectricEngine implements Engine {
public void start() {
System.out.println("전기 엔진이 작동합니다.");
}
}
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
void start() {
engine.start();
}
}
// 의존성 주입 예
public class Main {
public static void main(String[] args) {
Engine engine = new ElectricEngine(); // 여기서 사용할 엔진을 선택
Car car = new Car(engine); // 선택한 엔진을 Car 객체에 주입
car.start();
}
}
- Car 클래스가 직접적으로 Engine클래스에 영향을 받지 않고, Engine 인터페이스에 의존함
- 이제 Car 내부에서 Engine 구현체를 생성하는 것이 아니라, 외부에서 생성된 Engine 구현체를 Car는 주입만 받아서 사용!
- 실제 Engine 구현체를 무엇을 쓰는지에 대한 구체적인 것은 Car는 신경쓰지 않아도됨. Car 클래스 내부의 변경 없이 Engine의 종류만을 교체할 수 있다
IoC(Inversion of Control) - 제어의 역전
- 프로그램의 실행 흐름을 개발자(사용자) 가 아니라, 외부 시스템, 프레임워크가 제함
- 이 원칙을 구현하는 방법이 DI
- 의존성 주입을 개발자가 직접 하는 게 아니라, 외부(프레임 워크 etc)에서 하게함으로써 통제권이 프레임워크로 넘어가는 것.
- 이 원칙을 구현하는 방법이 DI
Spring을 통한 IoC/DI
- Bean
- 스프링 IoC 컨테이너가 관리하는 객체
- Service, Repository, Controller 등의 컴포넌트를 Bean으로 등록하여 사용
- Bean들 사이의 의존성이 스프링에 의해 자동으로 관리됨
---> 이 Bean을 스프링 설정을 통해 정의함으로써, 스프링을 통한 IoC/ DI가 이뤄질 수 있음
// Engine.java
public interface Engine {
void start();
}
// GasolineEngine.java
@Component // 스프링의 관리 대상임을 선언
public class GasolineEngine implements Engine {
@Override
public void start() {
System.out.println("가솔린 엔진이 시동됩니다.");
}
}
// ElectricEngine.java
@Component // 스프링의 관리 대상임을 선언
public class ElectricEngine implements Engine {
@Override
public void start() {
System.out.println("전기 엔진이 시동됩니다.");
}
}
// Car.java
@Component // 스프링의 관리 대상임을 선언
public class Car {
private Engine engine;
@Autowired // 스프링에게 Engine 타입의 객체를 주입하도록 지시
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
1. @Component
- 스프링에게 해당 클래스들이 스프링 컨테이너에 의해 인스턴스화 되고 관리되어야 함을 알려줌
- 해당 클래스의 객체를 스프링이 관리하는 Bean으로 생성하게 함
2. @Autowired
- 객체를 생성할 때 필요한 의존성을 자동으로 주입하게 해줌
- 예제 코드에서는 Engine 타입의 인스턴스가 필요하니까, 스프링이 자동으로 Engine 타입의 인스턴스 찾아서 주입해줌
AOP(Aspect-Oriented Programming) - 관점 지향 프로그래밍
- 모듈에서 공통적 -> 즉, 중복적으로 나타나는 부분을 분리하기 위한 방법
- 횡단 관심사 - 공통 기능
- 핵심 관심사 - 주요 기능
장점
- 재사용성 증가 : 공통 기능을 하나의 장소에 모듈화하기 때문에 여러 부분에서 재사용 가능
- 유지보수성 향상 :핵심로직과 공통 기능을 분리함으로써, 핵심로직이 공통기능의 변경 등에 영향 받지 않도록 함
- Aspect
- 횡단 관심사(공통 부분)을 모듈화한 것 ex) 로깅, 보안, 트랜잭션 등
- @Aspect -> 클래스가 횡단관심사를 모듈화한 것임을 선언
- Advice
- pointcut에 언제, 무엇을 적용할지 정의한 메서드
- @Before, @After , @AfterReturning, @AfterThrowing, @Around
- Join Point
- Aspect 적용이 가능한 지점
- 스프링 프레임워크가 관리하는 빈의 모든 메서드가 조인 포인트임(메소드 실행 시)
- Pointcut
- JoinPoint 중에서 어떤 것이 Advice의 대상이 될 지 결정 -> 즉 자르는 지점!
- JoinPoint 중 특정 조건에 맞는 경우를 필터링 해서 Advice 적용
- Advice가 적용된 메소드(PointCut) 지점에서는 JoinPoint 지점이 실행되기 전에 메소드를 호출 ex) 보안 검
- Target
- 어드바이스가 적용되는 대상 객체(클래스)
- Weaving
- 어드바이스를 Target의 코드에 적용하는 과정, 컴파일 타임, 로드 타임, 런타임 등에서 수행될 수 있다.
ex)
모든 가능한 JoinPoint 중 특정 조건 만족하는 시점(Pointcut)에 Advice 적용하는예
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// Pointcut 정의: AccountService의 deposit 메소드에만 적용
@Pointcut("execution(* AccountService.deposit(..))")
public void depositPointcut() {}
// Before Advice: deposit 메소드 실행 전에 로깅 실행
@Before("depositPointcut()")
public void logBeforeDeposit() {
System.out.println("LoggingAspect: About to deposit money");
}
}
- Join Point: AccountService 클래스의 deposit, withdraw, getBalance 가능한 모든 메소드 실행 시점
- Pointcut: depositPointcut 포인트컷은 deposit 메소드의 실행만을 대상으로 하여 이러한 조인 포인트 중 deposit 메소드 실행 시점만을 선택
- Advice: @Before 어드바이스는 deposit 메소드가 실행되기 전에 로깅 실행
Spring AOP
- 개발자가 JoinPoint 정의 X -> PointCut을 정의함으로써 스프링 AOP 가 자동으로 처리한 JoinPoint들 중 어느 지점에 Advice를 적용할 것인기 결정
@Controller, @Service같은 어노테이션들..
컨트롤러- 웹 요청을 수행한다는 공통 관심
서비스 - 비즈니스 로직을 수행한다는 공통 관심...
이라는 측면에서 관심사를 효율적으로 분리해서 AOP가 효과적으로 적용될 수 있게함
ex) @Controller 계층에서 사용자의 인증 및 권한 검사하는 Aspect를 정의할 수 있음
@Aspect
@Component
public class SecurityAspect {
@Pointcut("within(@org.springframework.stereotype.Controller *) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void controllerMethods() {}
@Before("controllerMethods()")
public void checkAuthorization(JoinPoint joinPoint) throws Throwable {
// 여기서 인증 및 권한 검사 로직을 구현
System.out.println("Checking authorization for accessing: " + joinPoint.getSignature().toShortString());
}
}
-> 모든 컨트롤러 계층에서 사용자의 인증 및 권한 검사라는 공통적인 부분(횡단 관심사)가 AOP를 통해 모듈로 분리됨!
- Join Point: 스프링 Bean의 모든 메소드 실행 시점
- Pointcut: 모든 조인포인트 중 @Controller 어노테이션이 붙은 클래스내의 메소드 중 @RequestMapping 어노테이션이 붙은 메소드만을 대상으로 함
- Advice: @Before 어드바이스는 포인트컷에 정의 메소드가 실행되기 전에 실행
ex2) AOP를 통해 트랜잭션을 관리할 수 있음
1. @Transactional 어노테이션으로 범위 지정
@Service
public class MyService {
@Transactional
public void someTransactionalMethod() {
// 데이터베이스 변경 로직
}
}
2. 트랜잭션 Advice 설정 파일 구성
포인트컷 표현식으로 어느 메서드에 트랜잭션을 적용할 지 정의하고, 트랜잭션 어드바이스와 연결
<aop:config>
<aop:pointcut id="transactionalMethods" expression="execution(* *.transactional*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionalMethods"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
'JAVA' 카테고리의 다른 글
[이펙티브 자바] 객체 생성과 파괴 (0) | 2024.07.13 |
---|---|
싱글톤 패턴과 프록시 패턴 (1) | 2024.05.01 |
자바의 제네릭(Generic) (0) | 2024.04.08 |
자바의 신 인사이트 정리 (0) | 2024.03.21 |