Spring 3일차

의존주입과 어노테이션이 많이 햇갈리고 어려워서 책을보면서 천천히 다시 공부하였다. 그래서 어느정도 감은 익혔고 다시 시작하려고 한다.

스프링 빈 객체의 라이프사이클

스프링 컨테이너는 초기화와 종료라는 라이프사이클을 갖는다.

  1. AnnotationConfigApplicationContext 생성자를 통해 컨텍스트 객체가 생성될 때 스프링 컨테이너가 초기화된다.

  2. 초기화될 때 Config 클래스에서 정보를 읽어와 Bean을 생성하고 각 Bean을 의존주입하는 작업을 수행한다.

  3. 초기화 작업이 끝나면 컨테이너에 보관된 Bean 객체를 getBean() 메서드 또는 다른 메서드를 통해 사용한다.

  • 컨테이너 사용이 끝나면 close() 메서드를 사용하여 컨테이너를 종료한다.

Bean객체의 라이프사이클은 다음과 같다.

객체 생성 --> 의존 생성 --> 초기화 --> 소멸(컨테이너 종료)

스프링은 다음의 두 인터페이스에 이 메서드를 정의하고 있다.

public Interface InitializingBean {	
	void afterPropertiesSet() throws Exception;
} // 빈 객체가 생성된 후 초기화 과정에서 afterPropertiesSet 메서드 실행

public Interface DisposableBean {
	void destroy() throws Exception;
} // 소멸 과정에서 빈 객체의 destroy() 메서드를 실행
  • 초기화와 소멸 과정이 필요하면 해당 인터페이스를 상속하고 적절히 구현하면 된다.

<커스텀 메서드 이용>
모든 클래스가 위 2개의 인터페이스를 사용할 수 있는 것은 아니다. 스프링 설정에서 직접 메서드를 지정할 수 있다.

@Bean(initMethod = "<메서드 이름>", destroyMethod = "<메서드 이름">)
public Client2 client2() {
...
}

빈 객체의 생성과 관리 범위

  • 스프링 컨테이너는 기본적으로 Bean 객체를 한개만 생성한다.
  • 이렇게 한 식별자에 대해 한 개의 객체만 존재하는 빈을 싱글톤(singleton) 범위(scope)를 갖는다.
  • 빈의 범위를 프로토타입(prototype)으로 지정하면 빈 객체를 구할 때마다 매번 새로운 객체를 생성한다.
@Bean
@Scope("prototype") // 빈의 범위 지정
public Client client(){
...
}
  • 프로토타입 범위를 갖는 빈은 완전한 라이프사이클을 따르지 않는다.
  • 프로토타입 범위의 빈을 사용할 때에는 빈 객체의 소멸 처리를 코드에서 직접 해야 한다.

프록시와 AOP

어떠한 코드가 있을 때 테스트를 위해 특정 기능을 추가해야할 때 핵심적인 기능을 하는 코드는 수정하지 않고 중복도 피하면서 기능을 추가할 수는 없을까?

이때 출현하는 것이 바로 프록시 객체이다.

  • 핵심 기능의 실행은 다른 객체에 위임하고 부가적인 기능을 제공하는 객체를 프록시(proxy)라고 부른다.
  • 프록시의 특징은 핵심 기능은 구현하지 않는다는 점이다.
  • 프록시는 핵심 기능을 구현하지 않는 대신 여러 객체에 공통으로 적용할 수 있는 기능을 구현한다.

공통 기능 구현과 핵심 기능 구현을 분리하는 것이 AOP의 핵심이다.

AOP(Aspect Oriented Programming)

  • 여러 객체에 공통으로 적용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍 기법이다.
  • AOP는 핵심 기능과 공통 기능의 구현을 분리함으로써 핵심 기능을 구현한 코드의 수정 없이 공통 기능을 적용할 수 있게 만들어 준다.
  • 핵심 기능에 공통 기능을 삽입하는 방법에는 다음 세 가지가 있다.
    • 컴파일 시점에 코드에 공통 기능을 삽입하는 방법
    • 클래스 로딩 시점에 바이트 코드에 공통 기능을 삽입하는 방법
    • 런타임에 프록시 객체를 생성해서 공통 기능을 삽입하는 방법

--> 스프링에서는 세번째 방식을 사용한다.

AOP 주요용어

  • Advice
    : 언제 공통 관심 기능을 핵심 로직에 적용할 지를 정의하고 있다.
  • Joinpoint
    : Advice를 적용 가능한 지점을 의미한다.
  • Pointcut
    : Joinpoint의 부분 집합으로서 실제 Advice가 적용되는 Joinpoint를 나타낸다.
  • Weaving
    : Advice를 핵심 로직코드에 적용하는 것
  • Aspect:
    : 여러 객체에 공통으로 적용되는 기능

Advice의 종률

  • Before Advice
    : 대상 객체의 메서드 호출 전
  • After Returning Advice
    : 대상 객체의 메서드가 익셉션 없이 실행된 이후
  • After Throwing Advice
    : 대상 객체의 메서드를 실행하는 도중 익셉션이 발생한 경우
  • After Advice
    : 익셉션 발생 여부에 상관없이 대상 객체의 메서드 실행 후 공통 기능을 실행
  • Around Advice
    : 대상 객체의 메서드 실행 전, 후 또는 익셉션 발생 시점에 공통 기능을 실행

널리 사용되는 것은 Around Advice이다. 다양한 시점에 원하는 기능을 삽입할 수 있기 때문이다.

스프링 AOP 구현

  1. Aspect로 사용할 클래스에 @Aspect 어노테이션을 붙인다.
  2. @Pointcut 어노테이션으로 공통 기능을 적용할 Pointcut을 정의한다.
  • Pointcut 어노테이션 내부에는 공통기능을 적용할 대상이 존재하는 경로를 입력한다.
  1. 공통 기능을 구현한 메서드에 @Around 어노테이션을 적용한다.
@Aspect
public class ExeTimeAspect {
	
	@Pointcut("execution(public * chapter7..*(..))") // 공통 기능을 적용할 대상을 설정한다.
	private void publicTarget() {
	}
	
	@Around("publicTarget()") // Around Advice를 설정
	public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
		// ProceedingJoinPoint는 프록시 대상 객체의 메서드를 호출할 때 사용한다.
        ...
    }
  • config 클래스에 @EnableAspectJAutoProxy 설정을하면 스프링은 @Aspect 어노테이션이 붙은 Bean 객체를 찾아서 Bean 객체의 @PointCut 설정과 @Around 설정을 사용한다.
@Configuration
@EnableAspectJAutoProxy
// @Aspect 어노테이션을 붙인 클래스를 공통 기능으로 적용하려면 해당 어노테이션을 붙여야함
public class AppCtx {
	
	@Bean
	public ExeTimeAspect exeTimeAspect() {
		return new ExeTimeAspect();
	}
	
	@Bean
	public Calculator calculator() {
		return new RecCalculator();
	} // 동일한 인터페이스 상속, config 클래스에서는 RecCalculator Bean 객체 생성
}

ProceedingJoinPoint의 메서드

  • Around Advice에서 사용할 공통 기능 메서드는 대부분 파라미터로 전달받은 ProceedingJoinPoint의 proceed() 메서드만 호출하면 된다.

  • ProceedingJoinPoint 인터페이스는 전달된 인자에 대한 정보를 볼 수 있는 메서드를 제공한다.

    • Signature getSignature() : 호출되는 메서드에 대한 정보를 구한다.
    • Object getTarget() : 대상 객체를 구한다.
    • Object[] getArgs() : 파라미터 목록을 구한다.
  • Signature 인터페이스는 다음 메서드를 제공한다.

    • String getName)() : 호출되는 메서드의 이름을 구한다.
    • String toLongString() : 호출되는 메서드를 완전하게 표현한 문장을 구한다.
    • String toShortString() : 호출되는 메서드를 축약해서 표현한 문장을 구한다.

프록시 생성 방식

  • 스프링은 AOP를 위한 프록시 객체를 생성할 때 실제 생성할 빈 객체가 인터페이스를 상속하면 인터페이스르르 이용해서 프록시를 생성한다.
  • 빈 객체가 인터페이스를 상속할 때 인터페이스가 아닌 클래스를 이용해서 프록시를 생성하고 싶다면 다음과 같이 설정하면 된다.

    @EnableAspectJAutoProxy(proxyTargetClass = true)

execution 명시자 표현식

execution(수식어패턴? 리턴타입패턴 클래스이름패너?메서드이름패턴(파라미터패턴))

  • '*' : 모든 값을 표현한다.
  • '..'(점 두개) : 0개 이상이라는 의미를 표현할 수 있다.

Advice 순서

  • 한 PointCut에 여러 Advice를 적용할 수도 있다.
  • 프록시가 실행되는 순서는 Config 클래스에서 Bean을 설정한 순서대로 실행이 된다.
  • 이 순서를 임의로 설정하고자 한다면 @Order(<숫자>)를 이용하여 적용 순서를 결정한다.

<오늘의 한줄평>
정보처리기사 시험이 끝났다. 아마 붙을거 같다. 그리고 이 전에는 인강만 보면서 공부를 했는데 책을 보면서 나 혼자 이해하고 모르는 부분은 찾아보면서 공부하니 더 잘되는거 같다. 꾸준하게만 하자

0개의 댓글