ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] 스프링 기본
    백엔드/Spring 2023. 4. 11. 09:00
    반응형

    1. DI (Dependency Injection)

    객체 간의 의존성을 외부에서 주입하는 방식

    - 장점: 낮은 결합도를 가지고, 더 쉽게 테스트할 수 있음

    - 낮은 결합도를 가지면 장점: 개방 폐쇄 원칙의 장점을 가짐 (코드 재사용, 확장에 용이해짐)


    2. Spring Bean

    스프링에서 관리하는 객체로 IoC 컨테이너에 의해 생성되고, 관리되어짐

    Bean을 등록하는 방법은 @Configuration과 @Bean을 사용하거나, @Component를 사용할 수 있음


    3. IoC 컨테이너

    IoC 컨테이너는 객체의 생명주기를 관리하는 곳임

    이건 프로그래밍에서 쓰이는 컨테이너와 뜻이 같지만, 스프링에서 IoC가 추가된 이유는 개발자가 직접 객체의 생명주기를 관리하지 않고, 프레임워크가 대신 수행하기 때문임

    IoC 컨테이너의 종류는 BeanFactory, ApplicationContext로 나눌 수 있음

    BeanFactory는 빈을 생성하고 관리하고, ApplicationContext는 BeanFactory 기능을 상속하고, 추가적인 기능을 제공함

    ApplicationContext가 BeanFactory을 상속한 것이라서 ApplicationContext를 사용하는 것이 일반적임 


    4. @Configuration, @Bean

    클래스에 @Configuration이 붙은 곳이라면 Bean이 싱글톤으로 관리되어짐

    @Configuration이 없는 곳이라면 Bean이 싱글톤으로 관리하지 않음

    @Bean은 DefaultListableBeanFactory에서 ConcurrentHashMap<String, BeanDefinition>으로 관리되고 있음

    여기서 String은 Bean 클래스 이름이고, BeanDefinition은 빈 설정 메타데이터를 가지고 있는 인터페이스임(빈 클래스 이름, 스코프 등..)


    5. @Autowired

    @Autowired를 붙이게 되면 다음 방식으로 작동함

    1. BeanFactory에 같은 타입이 저장된 것을 찾음. 고유한 값이면 반환

    2. 같은 이름이 존재하는지 찾음. 고유한 값이면 반환. 여러 개라면 식별자를 확인함

    3. 에러를 반환함

    - null이 들어와도 되는 경우: required = false, 객체를 Optional로 래핑하여 사용, 객체에 @Nullable을 붙임


    6. @Qualifier, @Primary

    @Autowired의 에러는 크게 두 가지로 나뉘어짐

    - BeanFactory에 Bean이 저장되지 않았을 때

    - BeanFactory에 동일한 타입의 Bean이 2개 이상 저장되어 있을 때, 그 타입을 자동 주입하는 경우

     

    여기서 두 번째 문제를 해결하기 위해 사용함

    @Qualifier는 @Autowired에서 같은 이름이 존재하는지 확인할 때 식별자 용도로 사용함

    사용하게 된다면 @Autowired를 사용하는 곳에 @Qualifier를 붙여서 같은 이름을 적어야함

    하지만 Bean을 등록하는 곳에서는 @Component(??), @Bean(??), @Qualifier(??)로 다양한 선택지가 있음

     

    @Primary는 빈이 2개 이상 있을 때, 에러를 반환하지 않고, @Primary가 붙은 빈을 우선적으로 선택하여 주입함

    @Primary와 @Qualifier를 동시에 사용할 수 있음


    7. @Component

    클래스 자체를 Bean으로 등록하는 방법

    @Component를 사용하게 된다면 스프링은 Component Scan을 통해 자동으로 빈을 등록하게 만들어줌

    @Configuration뿐만 아니라 @Controller, @Repository, @Service, @Aspect, @SpringBootApplication... 은 @Component가 적용되어 있음


    8. Component Scan

    Bean을 자동으로 등록하기 위한 어노테이션임

    이것으로 인해 @Bean을 하나하나 등록할 필요가 없어짐

    Component Scan의 범위를 지정하거나, 제외할 수도 있는데, basePackages와 excludeFilters를 통해 적용하여 성능을 최적화할 수도 있음

     

    컴포넌트 스캔 작동 방식

    [1] ConfigurationClassPostProcessor에서 @Configuration 어노테이션이 붙은 클래스를 찾아 처리하기 위해 [2] ~ [6] 과정을 실행한다.

    [2] ConfigurationClassParser에서 Component Scan 동작에 필요한 정보를 수집하고, Base Package를 설정한다.

    [3] PathMatchingResourcePatternResolver에서는 Base Package로부터 모든 클래스를 로드한다.

    [4] ConfigurationClassParser는 로드된 클래스를 excludeFilters를 통해 특정 대상을 제외하고 모든 대상을 로드한다.

    [5] ConfigurationClassBeanDefinitionReader는 스캔 대상 클래스를 모두 BeanDefinition으로 만들어준다.

    [6] DefaultListableBeanFactory에 BeanDefinition을 추가하여 빈을 생성한다.

     

    Base Package는 @SpringBootApplication이 붙어있는 클래스가 존재하는 패키지를 최상위 패키지로 간주한다.

    정리하면 최상위 패키지 내부에 있는 모든 스캔 대상 클래스를 Bean으로 생성한다는 것이다.


    9. Bean Life Cycle

    스프링 IoC 컨테이너에서 Bean의 생명 주기를 관리함

     

    [생명 주기 순서]

    객체 생성 → 의존 설정 → 초기화 → 소멸

     

    스프링 컨테이너가 초기화될 때, Bean 객체를 생성하고 의존을 주입한다.

    모든 의존 주입이 완료되면 InitializingBean을 통해 Bean 객체의 초기화를 진행한다.

    그리고 소멸을 할 때에는 DisposableBean을 통해 Bean 객체를 소멸한다.

    Bean의 소멸은 컨테이너가 종료될 때 소멸한다. 이때 모든 Bean이 소멸되어야 컨테이너가 종료된다. 

     

    초기화와 소멸은 아래 방식으로 발전해왔다.

    - InitializingBean과 DispoableBean을 상속 (단점: 외부 라이브러리에는 사용이 불가능)

    - @Bean(initMethod=??, destoryMethod=??)를 사용 (단점: 초기화/소멸 메서드를 직접 적어줘야함)

    - 메서드에 @PostConstruct, @PreDestroy를 사용 (단점: 외부 라이브러리에는 사용이 불가능)


    10. Bean Scope

    @Scope를 통해 Bean의 생명주기와 범위를 지정할 수 있다.

     

    - Singleton: 기본 스코프, 컨테이너당 하나의 인스턴스만 생성하여 빈을 요청할 때마다 같은 인스턴스를 반환함

    - Prototype: 빈을 요청할 때마다 새로운 인스턴스를 반환함

    - Request: HTTP 요청당 새로운 인스턴스를 반환함. 요청 완료시 소멸

    - Session: HTTP 세션당 새로운 인스턴스를 반환함. 세션 종료시 소멸

    - Application: 같은 Servlet Context면 같은 인스턴스를 반환(싱글톤), 다른 Servlet Context에서는 다른 싱글톤 객체를 반환함

    - Websocket: 소켓이 새로 열릴 때마다 새로운 인스턴스를 웹소켓 세션 속성값에 저장하고, 세션 종료까지 동일한 인스턴스를 반환함 (소켓 내부에서는 싱글톤으로 작동함)

     

    Request, Session, Application 같은 것들은 실제 요청이 있어야 존재할 수 있음. 그래서 프록시 패턴을 사용하여 실제 요청이 있기 전까지 빈 접근을 지연시킴


    11. AOP (Aspect Oriented Programming)

    여러 객체에 공통으로 사용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍

    AOP를 통해 핵심 기능과 공통 기능을 분리할 수 있음

    Spring AOP는 런타임에 프록시 객체를 생성해 공통 기능을 넣어줌

     

    AOP 적용 사례 (초반부만 보면 댐)

    https://dataonair.or.kr/db-tech-reference/d-lounge/technical-data/?mod=document&uid=235236 

     

    개발자 곁으로 성큼 다가온 AOP 실무에 활용하기

    개발자 곁으로 성큼 다가온AOP 실무에 활용하기 박재성 | javajigi@gmail.com | NHN 블로그 서비스 담당 Aspect Oriented Programming(이하 AOP)은 책이나 문서만을 통해서 이야기되는 용어가 아니다. AOP는 이미

    dataonair.or.kr

     

    [관심사의 분리 - 핵심 관심사, 횡단 관심사]

    어떤 로직에 대한 성능을 측정하고 싶다고 가정함

    그러면 로직은 `현재 시간을 측정하는 로직`, `성능 측정을 원하는 로직`, `로직 실행 후에 현재 시간을 측정하는 로직`으로 총 3가지를 나눌 수 있음

    현재 시간 측정 → 로직 실행 → 현재 시간 측정 후 시간 비교에서 핵심 관심사는 `로직 실행`으로 볼 수 있고, 횡단 관심사는 `시간 측정`으로 볼 수 있음

    다른 예시를 들어보면 특정 로직을 수행할 때, 로깅 + 보안 + 트랜잭션을 반복적으로 수행하는 기능이 있다고 하면, 횡단 관심사는 로깅, 보안, 트랜잭션임

    AOP는 핵심 관심사와 횡단 관심사를 분리하여 핵심 관심사의 로직에만 집중할 수 있도록 만들어줌


    12. AOP 주요 개념

    - Advice: 언제 실행되는지 알려줌. 스프링 AOP에서는 Before, After, AfterReturning, AfterThrowing, Around Advice가 있음

    - Joinpoint: Advice를 적용 가능한 지점을 뜻함. 스프링 AOP에서 메서드를 호출할 때만을 지원함

    - Pointcut: Advice를 적용할 Joinpoint를 나타냄

    - Weaving: Advice를 핵심 로직 코드에 적용함

    - Aspect: 여러 객체에 공통으로 적용되는 기능으로 중복 코드를 제거할 수 있음

     

    [스프링 AOP - Advice 종류]

    - Before: 메서드 실행 전 무조건 실행

    - After: 메서드 실행 후 무조건 실행

    - AfterReturning: 메서드가 예외 처리 없이 실행될 경우 실행

    - AfterThrowing: 메서드 실행 도중 예외가 발생할 경우 실행

    - Around Advice: 메서드 실행 전 / 후에 무조건 실행 (제일 많이 쓰임 - 실행 시간 측정, 로깅, 트랜잭션, 성능 모니터링, ...)


    13. 스프링 AOP 특징

    스프링 AOP는 프록시 패턴을 사용하여 관심사의 분리를 함

     

    아래 링크의 자바 코드에서 displayImage 메서드에서 횡단 관심사 코드를 작성한다고 생각하면 편함

    https://en.wikipedia.org/wiki/Proxy_pattern

     

    Proxy pattern - Wikipedia

    From Wikipedia, the free encyclopedia In computer programming, the proxy pattern is a software design pattern. A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network con

    en.wikipedia.org


    14. @Order

    만약 Aspect가 여러 개라면 실행 순서를 정해야할 상황이 존재함

    이런 경우 @Order(??)를 사용하여 순서를 정할 수 있음. 숫자값으로 숫자가 작을 수록 먼저 적용함


    15. AOP 사용 예시

    [@Transactional]

    트랜잭션과 관련된 공통 코드를 AOP로 처리함

    TransactionInterceptor의 invoke가 @Transactional를 가진 메서드를 프록시 객체가 실제 객체를 호출하기 전에 가로챔 (MethodInterceptor를 상속한 이유)

    이후 TrasacntionAspectSupport에 작성된 invokeWithinTransaction을 통해 AOP를 실행함

     

    https://github.com/spring-projects/spring-framework/blob/b23cc01cb7981bdaf7274483a69673c519123444/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java#L383-L412

     

    GitHub - spring-projects/spring-framework: Spring Framework

    Spring Framework. Contribute to spring-projects/spring-framework development by creating an account on GitHub.

    github.com

     

    Line 385: createTrasactionIfNecessary(...) - 트랜잭션 시작

    Line 391: invocation.proceedWithInvocation() - 로직 처리

    Line 395: completeTransactionAfterThrowing(...) - 예외 발생시 롤백 or 커밋

    Line 410: commitTransactionAfterReturning(...) - 정상 작동시 커밋

     

    바로 아래 else문도 코드 흐름이 AOP로 되어 있는 것 같은데, 확실하지는 않음

     

    코드 분석 글

    https://clack2933.tistory.com/45

     

    Spring Transactional 동작 과정

    전체 로직 Flow public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { @Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class targetClass, final InvocationCallback invocation) throws Thr

    clack2933.tistory.com

     

    반응형

    댓글

Designed by Tistory.