의존성 주입(Dependency Injection)은 '의존하는 부분을 외부에서 주입하는 것' 이다.
"사용하는 객체 A" 와 "사용되는 객체 B" 가 있다고 가정해보자.
A 클래스에서 B 클래스를 사용하려면 new 키워드를 이용하여 B 클래스의 인스턴스를 생성하고, B 클래스의 메서드를 사용한다. 이때, B 클래스에서 구현했던 메서드를 변경했을 때 A 클래스 역시 해당 메서드를 변경해야한다. 이런 관계에서 'A 클래스는 B 클래스에 의존한다.'
1 ) 클래스 의존 (구현 의존)
2 ) 인터페이스 의존
어노테이션을 사용하여 에러를 출력하거나 프로그램의 동작을 변경하는 등 다양한 액션이 가능하다.
소스 파일 | 필요 처리 내용 | 외부 소프트웨어 |
---|---|---|
@Override | 오버라이드 메서드의 시그니처 체크 | 자바 컴파일러 |
@Author | 도움말 문서 생성 | JavaDoc |
@Component | 인스턴스 생성 | 스프링 프레임워크 |
@NotEmpty | 입력란 체크 | Validator |
@Test | 테스트 실행 | JUnit |
복잡한 전체 내용을 한번에 정리해 이해하기보다 계층화를 통해 각 계층별로 대상의 의미를 이해하는 것이 편리하다.
레이어 | 개요 |
---|---|
애플리케이션 레이어 | 클라이언트와의 데이터 입출력을 제어하는 레이어 - @Controller |
도메인 레이어 | 애플리케이션의 중심이 되는 업무 처리를 수행하는 레이어 - @Service |
인프라스트럭처 레이어 | 데이터베이스에 대한 데이터 영속성 등을 담당하는 레이어 - @Repository |
상위 3가지 용도 이외의 인스턴스 하위 로직을 처리하는 곳에는 @Component 어노테이션을 클래스에 부여한다.
관점 지향 프로그래밍 (Aspect Oriented Programming)
데이터베이스 액세스 처리에는 예외 발생 시 처리하는 내용이 반드시 포함되어야한다.
스프링 프레임워크에서 제공하는 AOP 기능을 활용하여 '중심적 관심사' 와 '횡단적 관심사' 를 분리하여 프로그램 작성이 가능하다.
어드바이스 | 내용 | 어노테이션 |
---|---|---|
Before Advice | 중심적 관심사가 실행되기 '이전' 횡단적 관심사를 실행 | @Before |
After Returning Advice | 중심적 관심사가 '정상적으로 종료된 후' 횡단적 관심사를 실행 | @AfterReturning |
After Throwing Advice | 중심적 관심사로부터 '예외가 던져진 후' 횡단적 관심사를 실행 | @AfterThrowing |
After Advice | 중심적 관심사의 '실행 후' 횡단적 관심사를 실행 (정상 종료나 예외 종료 등 결과와 상관은 없다.) | @After |
Around Advice | 중앙적 관심사 호출 전후에 횡단적 관심사를 실행 | @Around |
직접 어드바이스를 만드는 경우 패키지, 클래스, 메서드 등 어드바이스 삽입 대상을 조건으로 지정할 수 있다. 지정하는 조건 방법에는 포인트컷 식을 사용한다.
'execution' 지시자의 구문
포인트컷 식은 와일드카드를 이용하여 유연하게 적용 범위 지정이 가능하다.
AOP 를 사용하기 위해 build.gradle 에
implementation 'org.springframework.boot:spring-boot-starter-aop'
를 추가한다.
어드바이스를 기술하는 클래스에 @Aspect 어노테이션과 인스턴스 생성을 위한 @Component 어노테이션을 부여한다.
인수로 JoinPoint를 전달하고 메서드에 @Before 부여
어노테이션의 인수에 포인트컷 식인 execution(패키지.클래스.메서드(인수))
@Before("execution(* com.example.demo.chapter03.used.*Greet.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
// 시작 부분 표시
System.out.println("==== Before Advice ====");
// 날짜 출력
System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(new java.util.Date()));
// 메서드 이름 출력
System.out.println(String.format("메서드:%s", joinPoint.getSignature().getName()));
}
인수에 같이 JoinPoint를 전달하고 메서드에 @After를 부여
지정방법은 Before Advice와 유사하다.
@After("execution(* com.example.demo.chapter03.used.*Greet.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
// 시작 부분 표시
System.out.println("==== After Advice ====");
// 날짜 출력
System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(new java.util.Date()));
// 메서드 이름 출력
System.out.println(String.format("메서드:%s", joinPoint.getSignature().getName()));
}
인수에 ProceedingJoinPoint 를 전달하고, 메서드에 @Around 를 부여한다.
@Around("execution(* com.example.demo.chapter03.used.*Greet.*(..))")
public Object beforeAdvice(ProceedingJoinPoint joinPoint) throws Throwalbe {
// 시작 부분 표시
System.out.println("==== Around Advice ====");
System.out.println("---- 처리 전 ----");
// 지정한 클래스의 메서드 실행
Object result = joinPoint.proceed();
System.out.println("---- 처리 후 ----");
// 반환값을 돌려줄 필요가 있는 경우에는 Object 타입의 반환값을 돌려준다.
return result;
}
❓Around Advice 가 다른 어드바이스와 다른 점
트랜잭션 관리에는 @Transactional 어노테이션을 사용한다. 어노테이션을 부여하여 데이터베이스 액세스 처리 메서드가 정상 종료하면 트랜잭션을 커밋하고, 예외가 발생하면 롤백한다.
스프링 부트에서 프로젝트를 시작할 때 통합 개발 환경(IDE)에 의존하지 않는 프로젝트를 만드는 방법은 Spring Initializr 에서 프로젝트를 만들고 사용 중인 통합 개발 환경에서 해당 프로젝트를 가져와서 사용한다.
https://start.spring.io/ 에 접속해서 설정 후 사용 가능하다.