의존주입과 어노테이션이 많이 햇갈리고 어려워서 책을보면서 천천히 다시 공부하였다. 그래서 어느정도 감은 익혔고 다시 시작하려고 한다.
스프링 컨테이너는 초기화와 종료라는 라이프사이클을 갖는다.
AnnotationConfigApplicationContext 생성자를 통해 컨텍스트 객체가 생성될 때 스프링 컨테이너가 초기화된다.
초기화될 때 Config 클래스에서 정보를 읽어와 Bean을 생성하고 각 Bean을 의존주입하는 작업을 수행한다.
초기화 작업이 끝나면 컨테이너에 보관된 Bean 객체를 getBean() 메서드 또는 다른 메서드를 통해 사용한다.
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
@Scope("prototype") // 빈의 범위 지정
public Client client(){
...
}
어떠한 코드가 있을 때 테스트를 위해 특정 기능을 추가해야할 때 핵심적인 기능을 하는 코드는 수정하지 않고 중복도 피하면서 기능을 추가할 수는 없을까?
이때 출현하는 것이 바로 프록시 객체이다.
공통 기능 구현과 핵심 기능 구현을 분리하는 것이 AOP의 핵심이다.
- 컴파일 시점에 코드에 공통 기능을 삽입하는 방법
- 클래스 로딩 시점에 바이트 코드에 공통 기능을 삽입하는 방법
- 런타임에 프록시 객체를 생성해서 공통 기능을 삽입하는 방법
--> 스프링에서는 세번째 방식을 사용한다.
널리 사용되는 것은 Around Advice이다. 다양한 시점에 원하는 기능을 삽입할 수 있기 때문이다.
@Aspect
public class ExeTimeAspect {
@Pointcut("execution(public * chapter7..*(..))") // 공통 기능을 적용할 대상을 설정한다.
private void publicTarget() {
}
@Around("publicTarget()") // Around Advice를 설정
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
// ProceedingJoinPoint는 프록시 대상 객체의 메서드를 호출할 때 사용한다.
...
}
@Configuration
@EnableAspectJAutoProxy
// @Aspect 어노테이션을 붙인 클래스를 공통 기능으로 적용하려면 해당 어노테이션을 붙여야함
public class AppCtx {
@Bean
public ExeTimeAspect exeTimeAspect() {
return new ExeTimeAspect();
}
@Bean
public Calculator calculator() {
return new RecCalculator();
} // 동일한 인터페이스 상속, config 클래스에서는 RecCalculator Bean 객체 생성
}
Around Advice에서 사용할 공통 기능 메서드는 대부분 파라미터로 전달받은 ProceedingJoinPoint의 proceed() 메서드만 호출하면 된다.
ProceedingJoinPoint 인터페이스는 전달된 인자에 대한 정보를 볼 수 있는 메서드를 제공한다.
- Signature getSignature() : 호출되는 메서드에 대한 정보를 구한다.
- Object getTarget() : 대상 객체를 구한다.
- Object[] getArgs() : 파라미터 목록을 구한다.
Signature 인터페이스는 다음 메서드를 제공한다.
- String getName)() : 호출되는 메서드의 이름을 구한다.
- String toLongString() : 호출되는 메서드를 완전하게 표현한 문장을 구한다.
- String toShortString() : 호출되는 메서드를 축약해서 표현한 문장을 구한다.
@EnableAspectJAutoProxy(proxyTargetClass = true)
execution(수식어패턴? 리턴타입패턴 클래스이름패너?메서드이름패턴(파라미터패턴))
- '*' : 모든 값을 표현한다.
- '..'(점 두개) : 0개 이상이라는 의미를 표현할 수 있다.
<오늘의 한줄평>
정보처리기사 시험이 끝났다. 아마 붙을거 같다. 그리고 이 전에는 인강만 보면서 공부를 했는데 책을 보면서 나 혼자 이해하고 모르는 부분은 찾아보면서 공부하니 더 잘되는거 같다. 꾸준하게만 하자