Spring
BeanFactory, ApplicationContext
BeanFactory
ApplicationContext
@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
prototype
Client가 Bean 요청시 스프링 DI 컨테이너가 의존관계, 초기화만 관리하여 반환하므로 Life Cycle로 설정한 종료 메서드를 호출하지 않는다!
매번 DI 완료된 새로운 Bean이 필요할 때 사용
DI 지연 방법
AOP(Aspect Oriented Programming)
-
개발자가 비지니스 로직에만 집중할 수 있게 한다.
각 프로젝트마다 다른 관심사를 적용할 때 코드의 수정을 최소화 시킬 수 있다.
원하는 관심사의 유지보수가 수월한 코드를 구성할 수 있다.
높은 응집도(같은 목적의 Logic 모여있는 정도)를 위해 사용.
Spring AOP는 AspectJ 라이브러리의 어노테이션 인터페이스만 사용하고, 동적 런타임중에 Proxy를 생성하는 방식을 이용한다.
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)는 아래와 같다.
within : 특정 Type에 join points 적용.
target : target 객체(Spring AOP Porxy가 적용되는 실제 객체)를 대상으로 하는 join points 적용
this : Spring 빈 객체(Spring AOP Proxy)를 대상
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
종류
-
부분적. 첫 번째 인자가
JoinPoint이어야 한다. “proceed()“가 자동으로 호출된다.
@Before : “proceed()” 실행 전
@AfterReturning : “proceed()” 실행 후
@AfterThrowing : 오류 발생 (try ~ catch의 catch)
@After : 모든 작업 수행 후 실행 (try ~ catch의 finally)
실행 순서는 기술한 순서와 같다.
만약 @Around 끼리 순서를 지정할 경우 @Order를 사용한다. 단. @Aspect 기반의 Class만 작동한다!
Aspect & Advisor
stateDiagram
state Aspect(Advisor) {
Pointcut
Advice
}
같은 개념으로, Advisor는 Spring AOP에서만 사용되는 개념
Weaving
-
Compile-Time Weaving : ”.class” 파일의 바이트코드를 수정. 별도의 Aspectj 컴파일러 필요
Binary Weaving : ”.class” 파일 혹은 jar 파일을 클래스 로딩시 바이트코드를 조작하여 로딩. 'java -javaagent' 옵션 필요
Runtime Weaving : Class가 JVM에 로드되기 직전에수행. Proxy 객체 구현 필요
Core Concerns
AnnotationAwareAspectjAutoProxyCreator
용어
POJO(Plain Old Java Object)
IoC(Inversion of Control, 제어의 역행)
DI(Dependancy Injection, 의존성 주입)
IoC가 발생할 때 Spring이 내부에 있는 객체(Bean)들의 관계를 관리할 때 사용하는 기법.
의존적인 객체를 직접생성하거나, 제어하는 것이 아니라, 제어의 역행으로 특정 객체에 필요한 객체를 외부에서 결정해서 연결시키는 것. (Spring에서 처리)
개발자는 자신이 만드는 객체나 클래스 외에 신경 쓰지 않고 코드를 작성하고, 자신의 코드에 필요한 객체는 Spring을 통해서 주입받는 구조로 작성.
낮은 결합도(의존성)를 위해 사용.
Dependency
의존성.
POJO와 반대되는 개념. 어떤 객체가 혼자 일을 처리할 수 없는 것을 의미.
Container
특정 객체의 생성과
의존 관계 관리를 담당하는 객체
Root Container
DAO(Data Acess Object)
Domain Class
VO(Value Object) & DTO(Data Transfer Object)
계층간 데이터 교환을 위한 자바빈즈를 말한다. 여기서 말하는 계층간의 컨트롤러, 뷰, 비즈니스 계층, 퍼시스턴스 계층을 말하며 각 계층간 데이터 교환을 위한 객체를 DTO 또는 VO라고 부른다. 즉, Data의 수집과 전달에 사용한다.
차이점
Command Object
Criteria Class
Persistence(영속성, 지속성)
데이터를 생성한 프로그램의 실행 종료 혹은 컴퓨터가 종료해도 사라지지 않는 데이터의 특성.
영속성은 파일 시스템, 관계형 테이터베이스 혹은 객체 데이터베이스 등을 활용하여 구현한다. (일반적으로 DB에 저장하는 것을 의미한다.) 영속성을 갖지 않는 데이터는 단지 메모리에서만 존재하기 때문에 프로그램을 종료하면 모두 잃어버리게 된다. 결국 영속성은 특정 데이터 구조를 이전 상태로 복원할 수 있게 해주어 프로그램의 종료와 재개를 자유롭게 해준다.
Business Layer(비즈니스 계층) & Service
고객의 요구사항이 반영되는 영역
비즈니스 로직이란 업무에 필요한 데이터 처리를 수행하는 응용프로그램의 일부를 말한다. 이것은 데이터 입력, 수정, 조회 및 보고서 처리 등을 수행하는 루틴, 좀더 엄밀히 말하면 보이는 것의 그 뒤에서 일어나는 각종 처리를 의미한다.
Controller와
DAO의 접착제 역할. Logic에 필요한 DB 관련 객체들을 모아서 자신이 원하는 일을 처리하는 용도.
스프링에서 일반적으로 Service라 칭한다.
개발 양이 늘어나는데도 사용하는 이유
고객마다 다른 부분을 처리할 수 있는 완충장치 역할. 각 회사마다 다른 Logic이나 규칙을 DB에 무관하게 처리할 수 있는 완충 영역으로 존재할 필요가 있다.
Controller와 같은 외부 계층의 호출이 영속 계층에 종속적인 상황을 막아준다. 만약 Controller가 직접 영속 계층의 DB를 이용하게 되면 Transaction의 처리나 Exception의 처리 등 모든 Logic이 Controller로 집중된다. 비즈니스 계층은 Controller로 하여금 처리해야 하는 일을 분업하게 만들어 준다.
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;
}
}