스프링 프레임워크는 자바 개발시 개발자가 자유롭게 객체 지향적 설계를 하면서도 간결한 코딩, 코드 재사용 등의 필수 기능을 단순한 형태로 사용하기 위해 나온 POJO 기반의 자바 백엔드 프레임워크이다.
POJO(Plain Old Java Object), 처음 자바가 만들어졌을 당시의 자바가 추구했던 객체 지향 언어로서의 특징을 살리는 코딩 스타일로 회기하자는 의미에서 나온 용어이다. (※필자의 주관)
관점 지향 프로그래밍이란, 쉽게 말해 관점에 따라 기능들을 모듈화 (공통된 기능을 하나의 단위로 묶는 것) 하여 코드 중복, 객체들 간의 관계 복잡도 증가 등을 해결하기 위한 개발 방법을 의미한다.
스프링 AOP는 프록시 기반으로 구현되었으며, 스프링 빈에만 적용 가능하다. 우리가 사용하는 @Controller, @Service, @Repository 등은 AOP의 구현체라 할 수 있다.
당연하게도 스프링에서는 필요에 따라 커스텀한 프록시 객체를 만들어서 사용할 수 있다.
성능 체크를 위해 메서드 수행 시간을 체크하는 기능을 추가한다고 가정해보자.
public void testA() {
// 메서드 성능 체크가 필요한 로직마다 StopWatch 로직을 구현해야 한다.
StopWatch stopWatch = new StopWatch();
stopWatch.start();
int sum = 0;
for (Integer i : integerList) {
sum += i;
}
System.out.println(sum);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
public void testB(){
StopWatch stopWatch = new StopWatch();
stopWatch.start();
int mul = 0;
for (Integer i : integerList) {
mul *= i;
}
System.out.println(mul);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
@PerfLogging // 어노테이션으로 코드 중복 없이 기능 추가 가능하다.
public void proxyTestA() {
int sum = 0;
for (Integer i : integerList) {
sum += i;
}
System.out.println(sum);
}
@PerfLogging
public void proxyTestB(){
int mul = 0;
for (Integer i : integerList) {
mul *= i;
}
System.out.println(mul);
}
프록시 객체란, 적용할 기능(타켓)의 주요 기능은 수정하지 않고 타겟의 전,후에 부가 기능을 추가한 객체를 의미한다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PerfLogging {
}
@Component
@Aspect
public class PerfAspect {
@Around("execution(* com.example.springdemo.proxy.ProxyService.*(..))")
public Object logPerf(ProceedingJoinPoint joint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object process = joint.proceed();
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
return process;
}
}
@PerfLogging // 어노테이션으로 코드 중복 없이 기능 추가 가능하다.
public void proxyTestA() {
int sum = 0;
for (Integer i : integerList) {
sum += i;
}
System.out.println(sum);
}
스프링 AOP에서는 CGLIB Proxy, JDK Dynamic Proxy를 이용한 Run-time Weaving 방식을 사용하여 RunTime시 프록시 객체가 생성된다.
스프링 PSA란, 앞서 설명한 AOP 기술로 만들어진 여러 어노테이션들을 선언하는 것만으로도 별도의 추가 코드 없이 원하는 서비스를 제공하게되는데, 이렇게 내부적으로 서비스 코드가 추상화되어 숨겨진채로 개발 편의성을 제공해주는 것을 의미합니다.
즉 환경의 변화에 관계없이 일관된 방식의 기술로 접근 환경을 제공하려는 추상화 구조를 의미한다.
예시1) DB 트랜잭션 관리 로직이 기본 JDBC(DatasourceTransactionManager)를 사용하냐, ORM(JpaTransactionManager)을 사용하냐에 따라 코드가 달라지지만 우리는 @Transactional 어노테이션을 사용시 내부 로직이 바뀌는 것을 고려하지 않아도 된다. @Transactional 이라는 하나의 추상화로 여러 서비스를 제공하게 해주기 때문이다.
예시2) 스프링 Web MVC는 기본적으로 Tomcat을 WAS로 사용한다. 하지만 스프링 WebFlux를 사용하면 Tomcat이 아닌 Netty 기반으로 동작하게 된다.
따라서 원래라면 구현 코드도 변경을 해야하지만 Spring Web 모듈에서 PSA를
스프링 WebFlux는 WebMVC와 달리 Reactive가 반영된 비동기 Non-Blocking 방식으로 성능 및 효율적이 자원 관리를 지원하는 모듈이다. 이에 대한 자세한 내용은 다른 포스트에서 다루겠다.
스프링을 이용하여 개발함으로써 개발의 흐름을 프레임워크가 주도한다. 때문에 제어의 흐름이 개발자 -> 프레임워크로 바뀌었다고하여 IOC(제어의 역전)이라 부른다.
IOC 컨테이너는 스프링 컨테이너라고도 불리며, 객체의 생성, 제거, 관계 설정 등의 권한을 가진다. IOC를 담당하는 컨테이너는 BeanFactory와 ApplicationContext로 구성된다.
BeanFactory : 스프링 IOC 최상위 인터페이스이다. Bean 생성 및 라이프 사이클 관리 등의 역할을 한다.
ApplicationContext : BeanFactory를 상속 받아 확장한 것이고, 리소스 로딩, 이벤트 발행 등의 역할을 한다.
@Autowired
private SampleObject sampleObject;
매우 간단하지만 아래와 같은 단점으로 권장되지 않는 방법이다.
private SampleObject sampleObject;
@Autowired
public void setSampleObject(SampleObject sampleObject){
this.sampleObject = sampleObject;
}
@Service
public class Sample(){
private final SampleObject sampleObject;
public Sample(SampleObject sampleObject){
this.sampleObject = sampleObject;
}
}
기본적으로 스프링은 root-context와 servlet-context가 생성된다. 이때, root-context에서 생성되는 빈들은 모든 영역에서 공유(사용)할 수 있고, servlet-context 에서 생성되는 빈들은 해당 컨텍스트에서만 사용할 수 있다. root 와 servlet에서 겹치는 빈이 생길 경우 servlet에서 생성한 빈을 사용하게 된다.
한번에 쭉 핵심개념을 정리하기 정말 좋네요 감사합니다