Table of Contents

Spring

BeanFactory, ApplicationContext

1.15. Additional Capabilities of the ApplicationContext

BeanFactory

Docs

ApplicationContext

Docs

@ComponentScan, @Component

flowchart LR cs["@ComponentScan"] c1["@Component"] c2["@Component"] c3[...] c4["@Component"] cs-. Scan .-> c1 cs-. Scan .-> c2 cs-. Scan .-> c3 cs-. Scan .-> c4

Dependency Injection

Lifecycle Callbacks

Docs

스프링 컨테이너 생성 → 스프링 빈 생성 → DI → 초기화 콜백 → Client가 사용 → 소멸전 콜백 → 스프링 종료

@Bean(initMethod = "init", destroyMethod = "close")
public DBConnection databaseClient() {
  ...
}

Bean Scopes

Docs

Bean이 존재할 수 있는 범위

prototype

Client가 Bean 요청시 스프링 DI 컨테이너가 의존관계, 초기화만 관리하여 반환하므로 Life Cycle로 설정한 종료 메서드를 호출하지 않는다!

매번 DI 완료된 새로운 Bean이 필요할 때 사용

DI 지연 방법

AOP(Aspect Oriented Programming)

ProxyFactory

flowchart LR client-- request -->ProxyFactory ProxyFactory-- Proxy 대상 객체가 Interface를 구현 O -->p1[JDK dynamic proxy] ProxyFactory-- Proxy 대상 객체가 Interface를 구현 X
Target Class를 상속 -->p2[CGLIB proxy] p1-- "call" -->a["Advice\n(추상화)"] p2-- "call" -->a["Advice\n(추상화)"] a-- apply -->tc[Target class]

JDK dynamic proxy

CGLIB proxy

외부 라이브러리이나 “org.aopalliance.intercept” 패키지로 편입됨

classDiagram Advice <|-- Interceptor Interceptor <|-- MethodInterceptor class Advice <> Advice Advice : class Interceptor <> Interceptor Interceptor : class MethodInterceptor <> MethodInterceptor MethodInterceptor : +Object invoke(MethodInvocation invocation)

Cross-cutting Concern

Joinpoint

Pointcut

Pointcut Designators

Supported Pointcut Designators

Spring AOP 에서 지원하는 Pointcut Designator(PCD)는 아래와 같다.

args, @args, @target는 단독으로 사용하면 안된다!
이 PCD들은 인스턴스가 만들어진 후 Runtime 시점에서 동작한다.
그러므로 단독으로 사용하면 모든 스프링 빈에 AOP를 적용하려고 시도한다.
이 때, 스프링 내부에서 사용하는 빈 중에 'final'로 지정된 빈이 있어서 오류가 발생한다.
execution

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern))

execution(public app.domain.Member app.repository.Repository.findById(String))

//  - modifiers-pattern : public
//  - ret-type-pattern : app.domain.Member
//  - declaring-type-pattern : app.repository.Repository
//  - name-pattern : findById
//  - param-pattern : String

Advice

종류

실행 순서는 기술한 순서와 같다.
만약 @Around 끼리 순서를 지정할 경우 @Order를 사용한다. 단. @Aspect 기반의 Class만 작동한다!

Aspect & Advisor

stateDiagram state Aspect(Advisor) { Pointcut Advice }

같은 개념으로, Advisor는 Spring AOP에서만 사용되는 개념

Weaving

Core Concerns

AnnotationAwareAspectjAutoProxyCreator

spring-boot-starter-aop 에 포함된 AnnotationAwareAspectjAutoProxyCreator 빈 후처리기가 작동하여

이후 프록시 객체 생성여부를 판단하고
Advisor가 포함된 객체 혹은 일반 객체가 스프링 컨테이너에 등록

용어

POJO(Plain Old Java Object)

IoC(Inversion of Control, 제어의 역행)

DI(Dependancy Injection, 의존성 주입)

Dependency

Container

Root Container

DAO(Data Acess Object)

Domain Class

VO(Value Object) & DTO(Data Transfer Object)

Command Object

Criteria Class

Persistence(영속성, 지속성)

Business Layer(비즈니스 계층) & Service

Binding

Annotation

의존성 주입시 사용.

Annotation 설명 사용
의존성 관련
@Qualifier 다수의 Bean 있을 시, 특정 객체의 이름을 이용하여 구분지어 의존성 주입.
@Qualifier 끼리 비교 → 이름 → 실패
※ @Primary보다 우선순위가 높음 (더 상세하게 구현되었기 때문)
Instance variable, Method
@Inject @Autowired와 동일 기능. (Java에서 제공)
찾는 순서 : 타입 → @Qualifier→ 이름 → 실패
@Autowired 주로 변수 위에 설정하여 해당 Type의 객체를 찾아서 자동으로 할당. (Spring에서 제공)
찾는 순서 : 타입 → 이름 → @Qualifier → 실패
@Resource @Autowired와 @Qualifier 을 합한 기능. (1개, 다수 사용 가능.)
찾는 순서 : 이름 → 타입 → @Qualifier → 실패
@Value Properties 값 주입 (사전에 root-context.xml에서 context:property-placeholder 설정 필요) String variable
Component 관련
@Component component scan 설정 후 자동 Bean 생성. 기본적으로 DI 컨테이너가 Singleton으로 관리 Class
@Configuration @Component 기능 및 Bean 설정 정보 관리
@Primary 같은 타입의 컴포넌트가 있을 시 우선권 명시
@Controller @Component 기능 및 Spring MVC의 Controller object 명시 (Web component)
@Repository @Component 기능 및 DAO 객체를 Spring에 인식시킴 (Web component)
@Service @Component 기능 및 Service 객체 명시. (Web component) 소스를 보면 @Component만 명시되어 있고, 딱히 하는 일이 없음.
Bean 관련
@Bean 의존성을 관리할 Bean 생성. @Configuration과 함께 사용시 Singleton으로 관리 Method
@Scope Bean 생명 주기 관리. 기본적으로 Singleton. Docs
REST 관련
@ResponseBody Return type이 HTTP의 응답 메시지로 전송 (객체를 JSON Data로 전송시 사용) (3.0부터 지원) Method, Return Type
@RestController View가 아닌 Data 자체를 반환 (jackson-databind와 사용하면 객체 자체를 자동으로 JSON Data로 반환) (4.0부터 지원)
@PathVariable 현재 URI에서 원하는 Data 추출할 때 사용 Parameter
@RequestBody request 문자열이 그대로 Paramter로 전달. 전송된 JSON Data를 객체로 변환. @ModelAttribute와 유사하지만 JSON에서 사용된다는 점이 차이
@ModelAttribute 자동으로 해당 Command 객체를 View까지 전달.
Parameter에 적용된 경우 : Command 객체의 이름 변경(이 Annotation을 지정하지 않으면 클래스 이름의 첫 글자를 소문자로 변경한 이름이 자동으로 설정.)
Method에 적용된 경우 : 반환된 객체를 View(JSP)에서 사용할 Data 설정. 이 때, @RequestMapping가 적용된 Method보다 먼저 호출된다.
Method, Parameter
@RequestMapping 특정 URI에 Mapping 되는 Class나 Method 명시 Class, Method
@RequestParam request에서 특정 Parameter 값을 찾아낼 때 사용 (≒ request.getParameter())
Command 객체를 사용하지 못할 경우(Command Class에 없는 Parameter를 추출할 경우) 사용.
Parameter
@RequestHeader request에서 특정 HTTP 헤더 정보 추출할 때 사용
@CookieValue 현재 사용자의 쿠키가 존재하는 경우 쿠키의 이름을 이용해서 쿠키 값 추출
@SessionAttribute Session상에서 Model의 정보를 유지하고 싶은 경우 사용 Class
@InitBinder Parameter를 수집해서 객체로 만들 경우에 Customizing Method

Object

ModelAndView

말그대로 model과 view를 저장하여 반환시켜 사용. 그러나 일반적으로는 Model 사용.

Model

사용할 Data를 addAttribute()로 지정하고, 해당 Data를 보낼 view를 문자열로 반환시켜 사용.

RedirectAttribute

말그래도 “Redirect” 시키며 Attribute 추가.

ResponseEntity<T>

상태 코드 제어. AJAX 연동(@RestController)에서 Data를 첨부하여 사용. 특이점으로 상태코드 상관없이 Data는 전송된다.

package io.github.ledyx.controller;

import javax.inject.Inject;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import io.github.ledyx.domain.ReplyVO;
import io.github.ledyx.service.ReplyService;

@RestController
@RequestMapping("/replies")
public class ReplyController {
	
	@Inject
	private ReplyService service;
	
	@RequestMapping(value = "/", method = RequestMethod.POST)
	public ResponseEntity<String> add(@RequestBody ReplyVO vo) {
		ResponseEntity<String> entity = null;
		
		try {
			service.add(vo);
			entity = new ResponseEntity<>("SUCCESS", HttpStatus.OK);
		} catch (Exception e) {
			e.printStackTrace();
			entity = new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
		}
		
		return entity;
	}
}