Spring AOP(Aspect Of Control) 란, DI(Dependency Injection), PSA(Portable Service Abstraction) 와 같이 Spring Framework 의 주요 Feature 이다.

 

AOP 란 공통된 관심사(Aspect)로 구분된 로직을 필요한 위치에 동적으로 삽입할 수 있게 해주는 기술로,

Advice, JoinPoint, PointCut, Advisor 등의 용어가 사용되며, 비즈니스 로직과 연계되어 공통적으로 사용하는 부분(Transaction 이나 Filter, Security 등의 Layer)을 코드를 특정시점에 코드에 삽입하는 기술이다.

 

이러한 핵심 로직 코드에의 적용을 Weaving 이라 하며 Compile , Class Loading , Runtime AOP의 구현이 가능하다.

 

다음은 몇가지 AOP 에 사용되는 용어들에 대한 정리이다.

 

- Aspect : 핵심기능에 부가될 수 있는 모듈을 말한다. 재사용 가능하며 핵심적인 기능에 부가되어 의미를 가질 수 있는 모듈이다.

 

- Advice : Aspect 에 대한 구현체가 된다. 주로 어느시점에 어떤 동작을 할지가 기술된다.

 

- Point Cut : 핵심 모듈을 분리할 선이자, 어드바이스를 적용할 시점(Join Point)을 정의하는 모듈이 된다.

 

- Proxy : 타겟은 프록시를 통해 호출되며 타겟 메서드 실행에 대한 전처리 후처리를 담당해줌과 동시에 AOP 추상화에 있어서 인터페이스를 제공한다.

 

- Weaving : 핵심 로직 코드에의 적용을 말하며, Aspect 가 적용됨으로써 새로운 Proxy 객체가 생성된다.

 

- Cross Cut : 공통 로직을 비즈니스 로직에서 추출해내는 것을 Cross Cutting 이라 한다.

 

하지만 위의 내용들은 사실 용어와 사용하는 기술들에 대한 내용으로 실제 Spring 에서 추구하는 AOP 의 개념 그 자체는 서버의 기능을 비즈니스 기능과 공통 기능으로 나누고, 공통 기능을 코드 밖에서 필요한 시점에 재사용 가능하도록 적용시켜 주겠다는 개념이다.

 

결국 공통 기능을 따로 분리해서 재사용하겠다는 관점을 말하는 것이다.

 

 




Spring 은 기본적으로 Framework 의 사용자, 즉 개발자가 비즈니스 로직의 구현에만 집중할 수 있게 서블릿 처리와 같은 기타 작업을 대신해주는 잘 구성된 프레임워크이다.


개발을 하다보면 비즈니스 로직 이외에도 Request 와 Response 에 대해 직접 처리하거나 비즈니스 로직을 처리하기 이전, 혹은 이후에 작업을 처리해야할 때가 있다.


예를 들어 Request 와 Response 에 대한 로깅이나 API 전반에 걸친 인증 등 Framework Layer 에서 처리할 수 있는 작업들이 있으며, 이 때 Filter 와 Interceptor 로 작업을 처리한다.





<출처 : https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/>



1. 필터 (Filter)



public interface Filter {

public void init(FilterConfig filterConfig) throws ServletException;

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException;

public void destroy();
}




Filter 는 Servlet Container 에 의해 동작이 제어되는 Java Class 로 HTTP Request 가 Service 에 도착하기 전에, HTTP Response 가 Client 에 도착하기 전에 제어할 수 있다.


Filter 는 Request 를 처리할 때 Dispatcher Servlet 이 작업을 처리하기 전에 동작하고, Response 를 처리할 때에는 Dispatcher Servlet 에 의해 작업이 끝난 이후에 동작한다.


정확히 분류하자면 Filter 는 J2EE 의 표준이며 Servlet 2.3 부터 지원되는 기능으로 Spring Framework 에서 지원을 하고는 있지만 Spring 프레임워크만의 기능은 아님을 알아두자.


Filter 는 Filter Chain 을 갖고 있으며 Application Context 에 등록된 필터들이 WAS 구동 시에 Context Layer 에 설정된 순서대로 필터 체인을 구성한다.


구성된 체인은 맨 처음 인스턴스 초기화(init())를 거친 후 각 필터에서 doFilter() 를 통해 필터링 작업을 처리하고 Destroy 된다.


이 때의 환경 설정은 주로 톰캣을 사용할 경우 web.xml 또는 Java Configuration 을 이용해서 구현하게 된다.




2. 인터셉터 (Interceptor)



public interface HandlerInterceptor {

    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

}


Interceptor 는 필터와는 다르게 Spring 레벨에서 지원하는 Servlet Filter 이다. Spring Context 내에서 HTTPRequest 와 HTTPResponse 처리에 대해 강력한 기능을 제공한다.


Java Servlet 레벨에서 동작하는 Filter 와 다르게 Spring Context 레벨에서 동작하므로 Dispatcher Servlet 이 Request 및 Response 를 처리하는 시점에 Interceptor Handler 가 동작한다.


Dispatcher Servlet 에서 요청이 처리되고 나면 요청받은 URL 에 대해 등록된 Interceptor 가 호출되며 Prehandle - Controller 실행 - PostHandle - AfterCompletion 의 순서로 인터셉터가 작업을 처리한다.


이 때의 환경 설정은 주로 servletContext.xml 또는 Java Configuration 을 이용해서 구현하게 된다.



필터와 인터셉터는 현업에서도 자주 사용되는 유용한 도구이니 확실하게 알아두고 꼭 필요할 때 응용해서 사용할 필요가 있다.



참조 : 

http://www.mkjava.com/tutorial/filter-vs-interceptor/

https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle

https://supawer0728.github.io/2018/04/04/spring-filter-interceptor/




얼마전 Spring 의 Circular Dependency 이슈가 있어서 좀 더 자세히 알아보다가 궁금해져서 정리한 내용이다.


본 포스팅은 이슈를 정리한 내용이므로 다음 내용을 선행적으로 참조해볼 필요가 있다.

(https://jins-dev.tistory.com/entry/Spring-DIDependency-Injection-%EC%9D%98-%EC%A0%95%EC%9D%98%EC%99%80-%EC%82%AC%EC%9A%A9?category=760012)


먼저 이슈가 된 내용은 서버가 올라갈 때 Circular Dependency 관계에 있는 Bean 들 간의 설정에 있어서 @Autowired 어노테이션을 실수로 빼먹은 부분이었는데, 그로인해 참조된 Bean 이 null 상태로 초기화되는 문제 때문이었다.



Spring Reference manual 에 따르면 스프링은 먼저 각 Bean 들을 초기화하고 다른 Bean 들에 Inject 하는 방식으로 기본적으로 Circular Dependency 문제를 해결한다.


즉, 껍데기만 만들어놓고 Bean 을 먼저 Injection 한다는 것이 옳다.


여기서 주의할 부분은 상호간에 Bean 이 Inject 될 시에 Inject 되는 Bean 은 완전히 Initialized 된 상태가 아니라는 것이다.


하지만 이런 Spring Framework 레벨에서의 처리가 있음에도 Spring team 은 결국 Circular Dependency 문제는 발생하지 않는 경우가 최선이며, 그런 안전한 구조를 설계하기 위한 Constructor Injection 을 권장하고 있다.


Spring framework 를 이용한 생산성과 편의성을 보다 추구하는 측에서도 Setter Injection 의 사용이나 @Lazy 어노테이션을 이용한 Lazy Init 을 권장하고 있다.




참조


https://stackoverflow.com/questions/3485347/circular-dependency-in-spring


https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/beans.html#d0e2299


https://www.logicbig.com/tutorials/spring-framework/spring-core/circular-dependencies.html







스프링 프레임워크의 주요 철학 중 하나로 가장 큰 테두리 중 하나는 DI(Dependency Injection) 이다.


Dependency Injection 에 대해 먼저 살펴보자면, 말그대로 "의존성 주입" 을 말하며, 스프링 프레임워크는 Framework 레벨에서 DI 를 제공해준다.


Spring 의 Container 들은 Bean 객체들을 관리하는 데 있어서 DI 를 이용하며 이를 통해 Life Cycle 을 용이하게 관리할 수 있으며 이 것이 스프링 프레임워크의 핵심적인 동작이라고 할 수 있다.


즉, 프레임워크 레벨의 관리를 통해 개발자는 객체들간의 의존성에 신경을 덜 쓰고 Coupling 을 줄일 수 있으며 높은 재사용성과 가독성있는 코드를 만들어낼 수 있다.


이를 제어의 역전(Inversion Of Control) 이라 하며, 이것이 스프링 프레임워크의 특징적인 개념인 IOC 이다.

(클래스 관리의 주체가 개발자가 아닌 프레임워크라는 뜻이다.)


그리고 결과적으로 이러한 개발 편리성은 높은 생산성을 이끌어낼 수 있는 스프링의 큰 장점이다.


- Dependency Injection 을 통해 얻을 수 있는 장점 -


 (1) Dependency Reduction : 객체 상호 간 의존성 관계를 줄여준다.


 (2) Reusable Structure : 코드의 재사용과 조합이 용이하다.


 (3) Readability : 코드들이 분리되다보니 가독성이 뛰어나진다.


 (4) Loose Coupling & Easy to change : 구조는 변화에 민감하지 않을 수 있다.


그 외에 테스트가 용이하고 다양한 패턴을 적용하는 데 유연하다는 점도 큰 장점이 될 수 있다.



Spring Framework 에서 개발자가 Dependency Injection 을 하는 데 몇가지 방법들이 있다.


 (1) Field Injection


 가장 흔히 볼 수 있는 Injection 방법으로 사용하기도 간편하고 코드도 읽기 쉽다.



public class Sample {
    
    @Autowired
    private Example example;
    
}


 많이 사용됨에도 불구하고 Field Injection 을 통한 의존성 주입은 권장되지 않는다.

 이유는 너무 추상적인 Injection 기법 때문이다. 의존성 주입이 쉽기 때문에 Dependency 관계가 복잡해질 우려가 있으며 이는 Framework 의 사용에 있어 다음과 같은 안티패턴적 측면을 갖는다.


 - Single Responsibility Principle Violation 

 : 너무나 쉬운 의존성의 주입은 하나의 클래스가 지나치게 많은 기능을 하게됨으로써 초기 설계의 목적성이자 "객체는 그에 맞는 동작만을 한다." 는 원칙에 위배되기 쉽다.

 위배된 경우 리팩토링의 비용은 크다.


 - Dependency Hiding

 : 추상화된 의존관계는 의존성을 검증하기 힘들게 만든다. 


 - DI Container Coupling

 : Field Injection 을 사용하면 해당 클래스를 곧바로 Instant 화시킬 수 없다. 이 부분 때문에 Constructor Injection 이 권장되는 이유이기도 하다.

 가령 Container 밖의 환경에서 해당 클래스의 객체를 참조할 때, Dependency 를 정의해두는 Reflection 을 사용하는 방법 외에는 참조할 수 있는 방법이 없다.

 DI Framework 는 Field Injection 된 클래스의 Instance 화에 대해서 Null Pointer Exception 을 만들어낼 것이다.


 - Immutability

 : Field Injection 된 객체는 final 을 선언할 수 없으므로 가변적(Mutable)이다. 객체는 변경될 수 있으며 이에 대한 대응에는 큰 비용이 든다.



 (2) Setter Injection


 선택적인 의존성을 주입할 경우 유용하며, Spring 3.x 대까지 가장 권장되던 방식이다.



public class Sample {
private Example example;

@Autowired
public void setExample(Example example) {
this.example = example;
}
}


 Field Injection 으로 인한 패턴적 위험성을 상당 부분 해소한다. Optional Injection 의 경우 권장되는 방식이다.

 @Required 어노테이션을 이용하면 의존성이 필요한 Setter 를 만들 수 있다.


 (3) Constructor Injection


 Spring 4.x 이상부터 권장되는 방식이다.



public class Sample {
    private final Example example;

    @Autowired
    public Sample(Example example) {
        this.example = example;
    }
}


 코드를 통해 알 수 있듯, final 선언이 가능하며 Immutability 에 대한 해소가 가능하며 의존성의 순환 참조(Circular Dependency) 에 대한 예방이 가능하다.

 순환 참조 시 위의 방법을 이용한 코드는 BeanCurrentlyCreationExeption 을 발생시킨다.

 역시 위에서 언급된 Container Coupling 문제도 해결이 되는데, 생성자를 통한 Injection 이므로 즉각적인 Instance 화 등에 대한 문제도 해결된다.



 많은 예제 코드들이 Field Injection 방식을 사용하고 있으나 Constructor Injection 의 사용이 권장된다.

추가로 참조한 바에 의하면 Spring Team 에서는 Setter 방식을 좀 더 권장하며 이유는 생성자가 지나치게 복잡해질 수 있기 때문이라고 한다.

패턴적 관점에서 이견이 있는 듯 하다.



Spring 4.3 이상부터는 생성자가 하나인 경우 @Autowired 를 사용하지 않아도 무방하다.



정리에 있어 다음 링크들을 참조하였다.


https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/


https://dzone.com/articles/dependency-injection-pitfalls


https://blog.outsider.ne.kr/753



Spring 3.1 버전부터 추가된 FlashMap 은 스프링에서 파라미터를 간편하게 전달하기 위한 자료구조이다.


주로 같은 도메인에서 Controller 간, 혹은 웹페이지에서 발생하는 Redirect 시 데이터 처리를 간단하게 하기 위한 목적으로 사용된다.


플래시 맵을 사용하는 용도는, URL 에 데이터를 노출시키지 않으면서 데이터를 전달하고 싶으면서도 Session 데이터에 넣기에 적합하지 않을 경우이다.

(Session 은 사용 후 값을 지워줘야 하므로 실수로 누락되는 경우를 방지해야하며 실제로 유저 정보들이 많이 관리되므로 가벼운 데이터의 경우에도 세션을 이용하기 부담스러운 경우가 많다.)


FlashMap 의 특징은 일반 Map 자료구조처럼 쉽고 간단하게 사용될 수 있으면서 사용되고 난 이후에는 Spring 에서 값을 자동으로 지워준다는 점에 있다.


즉, FlashMap 은 휘발성이며 사용하는 개발자 입장에서는 관리에 부담없이 가볍게 Attribute를 다룰 수 있다.


단, 휘발성이므로 서버가 지속적으로 관리하면서 사용해야 하는 공유 Attribute 에는 적합하지 않다고 한다.



참조 : 


https://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/web/servlet/FlashMap.html


https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-flash-attributes





Cache 는 서버의 동작을 이해하는 데 있어서 빼놓을 수 없는 부분이며, 서버의 부하를 줄여주고 서버가 가진 능력을 최대한으로 활용할 수 있게 해줄 뿐만 아니라, 클라이언트에도 중요한 역할을 한다.


흔히 말하는 Cache 의 종류로는 Redis 나 Memcached 를 사용하며 대부분 인메모리 형태로 서버의 값을 저장하고, 필요할 때에 해당 값을 반환함으로써 서버의 작업 공수를 줄여준다.


Spring Framework 는 프레임워크 레벨에서 캐시의 추상화를 지원해준다.


Cache의 추상화란, 흔히 캐시를 사용할 때 작업이 필요한 부분에 대한 인터페이스를 제공해준다는 뜻이다.


가령, 웹서버에 캐싱 기능을 적용하기 위해서는 다음과 같은 캐싱의 기본 로직이 탑제되어야 한다.


(1) Memory 혹은 원격 캐시에 연결된 객체를 생성한다. (이를 Cache Manager라 한다.)


(2) 캐시의 값을 불러온다.


(3) 캐시에 값이 존재하지 않는다면 캐싱할 값을 일정한 기준을 갖고 등록한다.


(4) 등록된 캐시값에 대해 조회가 가능하다.


(5) 필요할 때에 캐시의 값을 불러오고 적당한 때에 캐시를 업데이트 한다.


위의 단계들은 기본적으로 캐시가 가져야할 역할이며, 위의 역할 정도는 수행할 수 있어야 서버측에서 "캐시" 로써 동작한다고 할 수 있다.


Spring 에서 위와 같은 단계는 다음 Annotation 들로 대체될 수 있다.


Cache 의 생성 : Spring 의 Cache Configuration 참조


Cache Key Value 의 등록 : @Cacheable



@Cacheable(value="user")
public List<User> getUserListFromDB() {
return selectUserListFromDB();
}

@Cacheable(value="user", key="#uid", condition="#result!=null")
public User getUserFromDB(String uid) {
return selectUserFromDB(uid);
}


@Cacheable 어노테이션과 함께 저장할 캐시의 이름을 value에 명시하고, key 값을 지정하면 해당 결과값을 설정된 Cache에 캐싱할 수 있다.


값은 캐싱될 뿐 아니라, 다시 해당 함수로 접근 시 캐싱된 값이 있다면 내부 함수를 수행하지 않는다.


condition 은 해당 캐시에 적용 시 어떤 항목들에 대해 캐싱하거나 캐싱하지 않을 지를 결정할 수 있다.



Cache Key Value 의 삭제 : @CacheEvict



@CacheEvict(value="user", key="#user.uid", beforeInvocation=false)
public void putUserToDB(User user) {
insertUserToDB(user);
}


@CacheEvict 어노테이션을 이용하면 해당 캐시 이름과 Key 에 저장되어 있는 Cache Value 를 제거할 수 있다. 

이 때 종종 사용되는 옵션으로 beforeInvocation 옵션이 있는데, 이 옵션을 true 로 지정하면 함수가 시작되기 전에 캐시를 비우는 작업을 수행한다.


Cache 의 갱신 : @CachePut


@CachePut 어노테이션은 값이 변경되었을 경우에만 해당 캐시를 비운다.


여러 개의 Caching 동작에 대해 : @Caching



@Caching(evict = {
@CacheEvict(value="user", key="#user.uid")),
@CacheEvict(value="userGroup", key="#user.groupNo")
})
public void addNewUserToDB(User user) {
insertUserToDB(user);
insertUserGroupToDB(user.getUserGroup());
}


Cache 에 대한 여러 동작을 수행하고자 할 때에는 @Caching 어노테이션을 사용한다.



Spring 의 Cache 어노테이션은 내부적으로 SPEL(Spring Expression Language) 라는 문법을 사용한다.

위의 간단한 예시만으로도 사용하는 데 큰 지장은 없을 것이다.




Spring 의 Bean 이 정의될 경우 정의하는 Bean 의 Scope 에 대해 옵션을 부여할 수 있다.


예를 들어 Prototype Scope 는 해당 Bean 을 사용할 때마다 재정의하게끔 성질을 부여한다.


반면 Bean 을 Spring project 전역에서 단 하나의 객체로 취급하고 접근하고자 할 경우 해당 Bean 의 Scope 는 Singleton 으로 정의되는 것이 옳다.


Spring 프레임워크는 현재 5개의 Scope 를 제공하고 있다.


(1) Singleton : Spring 컨테이너 하나당 한개의 Bean 만이 생성되고 해당 Bean 이 요청될 때마다 모든 객체는 한 객체를 가리킨다.


(2) Prototype : 하나의 Bean 은 사용할 때마다 새로운 객체를 할당하여 사용하게 된다.


(3) Request : HTTP request 가 상주하는 Spring Application Context 내에서만 유효한 객체를 생성하고 재사용한다. (web-aware)


(4) Session : Bean 을 해당 세션이 가진 Application Context에 바인딩한다. (web-aware)


(5) Global session : HTTP 세션 전역에서 같은 Bean 을 사용가능하다. (web-aware)


Spring Bean 들의 Default scope 는 Singleton 이기 때문에 대부분 Bean 을 사용할 경우 @Autowired 를 통해 간편하게 사용이 가능하다.


만약 어플리케이션에 따라 다른 종류의 Scope 가 필요하다면 다음 예시와 같이 설정에서 변경이 가능하다.



<bean id = "..." class = "..." scope = "prototype">
//...
</bean>



보통의 어플리케이션에서는 Singleton 으로 설계하고 사용할 수 있으나 간혹 prototype 등으로 특정할 수 있으니 상황에 따라 유용하게 사용할 수도 있다.


<보다 나은 예시 참조 : https://www.tutorialspoint.com/spring/spring_bean_scopes.htm>



Spring 의 Bean 들은 대게 Singleton 패턴으로 정의되며 Application Context 에서 관리되는 형태로 개발자는 사용하게 된다.


그렇게 때문에 상당수의 개발자들이 Spring 의 각 Bean 들은 Thread Safety 가 보장된 안전한 Bean Object 이며 Spring 은 해당 동시성 문제에서 자유롭다고 여기는 경향이 있다.


하지만 정확히 Spring Bean 들은 Thread 의 Safety 와 무관하다. 


Spring Container 는 Object 들 각각에 대한 Life cycle 을 갖고 있으며 Container 내에서 하나만 존재하도록 보장하지만 그것이 Thread-Safe 를 말하는 것은 결코 아니며, Spring framework 는 오히려 이 책임을 개발자에게 맡긴다.


만약 Non-Thread-Safe Object 가 Injection 된다면 해당 객체는 쓰레드에 안전하지 않으며 개발자가 직접 핸들링해주어야 한다.


그렇다면 Bean 에 대해 어떤 방식으로 Thread Safety 를 부여할 수 있을까?


(1) Builder Pattern 의 사용

  간단하면서도 Tricky 한 방식으로 Builder Pattern 을 사용하면 좋다.

 Builder 패턴을 이용하면 객체의 Setter 를 정의하지 않은 상태에서 생성자만으로 객체의 Mutation 을 관리할 수 있으며, Spring 의 Bean 들은 Container 에 의해 life cycle 관리가 위임된다.

 따라서 Builder Pattern 을 통해 Set 을 관리하면 간단하면서도 명확히 Thread Safe 를 구현할 수 있다.


(2) Stateless Bean

  Bean 을 상태값과 무관하게 동작할 수 있는 Bean 으로 설계하는 것이다. 

  Bean 이 특정 상태를 나타내는 변수를 계속 메모리에 들고 상주하는 형태가 아닌, 가장 단순한 형태의 도메인 모델로 사용이 추천된다.


(3) Lock the beans

  가장 최후의 수단으로 여겨야 하는 방법으로 Bean 에 대해 Thread Safe 하게 설계를 하는 것이다. Spring 은 Lower level Library 들을 통해 Bean 단위의 Lock 을 지원하고 있으며 이를 통해 병렬처리에 있어서 동시성 문제의 해결이 필요하다.


물론 실제로 Safe 하지 않은 상황을 고려해야할 경우는 많지 않지만, 각기 다른 Request 를 공통으로 분류해야한다거나, 내부에서 Internal Thread 를 구동하는 경우에는 반드시 신경써보자.





Spring 의 Application Context 는 Spring 에서 설정 정보의 제공을 위한 핵심 인터페이스이다.


런타임 이전에 로드가 되며, 런타임에서 일반적으로 Read-Only 로 구성되지만, 사용에 따라 런타임에 Re-load 하는 경우가 종종 있다.


프로젝트의 설정 작업을 하는 클래스 들의 경우 ApplicationContextAware 등의 인터페이스를 상속해서 ApplicationContext 에 접근하곤 한다.


ApplicationContext 를 이용하면 다음과 같은 작업이 가능하다.


 - Application 에서 Component 로 갖고 있는 Bean 에 대해 접근이 가능하다. (즉, Bean factory 를 제공한다)


 - Resource 를 직접 가져올 수 있다. 리플렉션과 유사하게 명시된 이름을 주로 사용한다.


 - 이벤트를 직접 발행할 수 있으며 이벤트는 context 에 등록된 Listener 에 작용한다.


 - 상속된 Context 컴포넌트들에 접근이 가능하다.


ApplicationContext 에 접근하는 클래스는 다음과 같이 만들 수 있다.




@Component
public class ContextAccessor implements ApplicationContextAware {
private ApplicationContext applicationContext;

    /*
     *
     * @see org.springframework.context.ApplicationContextAware#setApplicationContext
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
    }
}



위와 같이 ApplicationContextAware 를 구현하면, ApplicationContext 에 접근이 가능하며, 클래스를 컴포넌트화 시켜서 프로젝트에 광범위하게 사용하곤 한다.




토비의 스프링을 읽어보면 Spring 을 구현하는 데 있어서 핵심이 되었던 철학들에 대해 설명을 해주는데, 그 중 하나가 PSA이다.


POJO 관련 포스팅에서 조금 언급을 했지만, Spring 은 언어가 아닌 기술 그자체에 얽매이는 것에 반감을 갖고 있다. 이는 애초에 EE 에 대한 불편함으로 대세로 떠오른 프레임워크이기도 하기 때문이다. 그리고 그러한 철학을 잘반영한 기술 중 하나가 PSA 라고 생각한다. 


*POJO 에 대한 설명은 다음 링크를 참조해보자. (http://jins-dev.tistory.com/entry/Spring-%EC%9D%98-%EA%B8%B0%EB%B3%B8%EC%9D%B4-%EB%90%98%EB%8A%94-POJO-%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC?category=760012)



PSA란 Portable Service Abstraction 의 약자로 환경의 변화와 관계없이 일관된 방식의 기술로의 접근 환경을 제공하려는 추상화 구조를 말한

이는 POJO 원칙을 철저히 따른 Spring 의 기능으로 Spring 에서 동작할 수 있는 Library 들은 POJO 원칙을 지키게끔 PSA 형태의 추상화가 되어있음을 의미한다


가령 일반적인 JUnit이나 Mybatis 등의 여러 Java Framework 에서 사용가능한 라이브러리들은 Spring 에서 지원하는 JUnit 이나 MyBatis 라이브러리와 다르다. 또한 JPA를 사용할 때에 있어서도 Spring JPA가 추상화 시켜준다... 따라서 개발자가 Hibernate를 사용하건 EclipseLink 를 사용하건 신경쓸 필요가 없다.


따라서 이러한 외부 라이브러리들은 Spring에서 사용할 때 내부구현이 달라지더라도 동일한 인터페이스로 동일한 구동이 가능하게끔 설계되어 있으며 의존성을 고려할 필요가 없다.



Spring 은 이렇듯 특정 기술에 직접적 영향을 받지 않게끔 객체를 POJO 기반으로 한번씩 더 추상화한 Layer 를 갖고 있으며 이를통해 일관성있는 서비스 추상화를 만들어낸다.



+ Recent posts