DI(Dependency Injection)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base64Encoder{
public String encode(String message){
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
class UrlEncoder{
public String encode(String message){
return URLEncoder.encode(message, "UTF-8");
}
}
public static void main(String args[]){
String url = "www.naver.com/books/it?page=12&size=20&name=spring";
Encoder encoder = new Base64Encoder();
//Encoder encoder = new UrlEncoder();
String result = encoder.encode(url);
}
위와 같은 코드를 작성하면 인코딩 방법이 바뀔 때마다 객체를 새로 생성하고 컴파일해야한다. encode라는 같은 동작의 메서드들이 있으므로 이를 Interface로 만들어 처리 하는 것이 좋다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface IEncoder{
String encode(String message);
}
class Base64Encoder implements IEncoder{
public String encode(String message){
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
class UrlEncoder implements IEncoder{
public String encode(String message){
return URLEncoder.encode(message, "UTF-8");
}
}
public static void main(String args[]){
String url = "www.naver.com/books/it?page=12&size=20&name=spring";
IEncoder encoder = new Base64Encoder();
//IEncoder encoder = new UrlEncoder();
String result = encoder.encode(url);
}
하지만 위와 같이 인터페이스를 추가해도 main문에서는 큰 변화를 찾을 수 없다. 여기에 DI를 추가하면
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public interface IEncoder{
String encode(String message);
}
class Base64Encoder implements{
public String encode(String message){
return Base64.getEncoder().encodeToString(message.getBytes());
}
}
class UrlEncoder implements{
public String encode(String message){
return URLEncoder.encode(message, "UTF-8");
}
}
class Encoder{
private IEncoder iEncoder;
public Encoder(IEncoder encoder){
this.iEncoder = encoder;
}
public String encode(String message){
return iEncoder.encode(message);
}
}
public static void main(String args[]){
String url = "www.naver.com/books/it?page=12&size=20&name=spring";
Encoder encoder = new Encoder(new Base64Encoder());
//Encoder encoder = new Encoder(new UrlEncoder());
String result = encoder.encode(url);
}
위의 코드처럼 외부로부터 사용할 객체를 주입 받는것이 DI이다. Encoder 클래스의 입장에서는 iEncoder를 외부로부터 받아서 사용하므로(의존성을 가지고 있는것을 주입 받았다) DI라고 부른다. DI의 장점은 아래와 같다.
- 의존성으로 부터 격리시켜 코드 테스트에 용이하다.
- DI를 통해 불가능한 상황을 Mock와 같은 기술을 통해 안정적으로 테스트 가능하다.
- 코드를 확장하거나 변경 할 때 영향을 최소화 한다.(추상화)
- 순환 참조를 막을 수 있다.
IoC(Inversion Of Control) _ 제어의 역전
- DI의 마지막 코드를 보면 여전히 객체를 개발자가 관리한다.(직접 new) Spring에서 객체를 관리하게 하기 위해 Component Annotation을 사용할 수 있다.
- Component Annotation을 작성하면 해당 클래스는 Spring bean으로 만들어 Spring이 관리하게 된다. Spring이 실행 될 때 Component Annotation이 붙은 클래스를 찾아 직접 객체를 singleton형태로 만들어 Spring container에서 관리한다.
- Configuration Annotation과 Bean Annotation으로 직접 bean으로 등록할 수 있다.
- Bean Annotation의 경우 메서드에만 사용 가능하며 Configuration은 여러개의 Bean을 관리한다.
- 만약 Spring이 Bean을 구분하지 못하는 경우 Qualifier를 사용해 이름을 명시해 줘야한다.
- 스프링에서는 일반적인 JAVA객체를 new로 생성해 개발자가 관리 하는 것이 아닌 Spring container에게 모두 맡긴다.
- 즉, 개발자에서 프레임워크로 제어의 객체 관리의 권한이 넘어갔으므로 제어의 역전 이라고 한다.
AOP(Aspect Oriented Programming) _ 관점지향 프로그래밍
- AOP를 사용해 로깅, 트랜잭션 관리, 시큐리티에서 적용 등 AspectJ와 같이 완벽하게 구현된 AOP와 통합하여 사용 가능하다.
- 스프링 어플리케이션은 대부분 특별한 경우를 제외하고는 MVC웹 어플리케이션에서 Web Layer, Business Layer, Data Layer로 정의한다.
- Web Layer : REST API를 제공하며, Client 중심의 로직 적용
- Business Layer : 내부 정책에 따른 logic을 개발하며, 주로 해당 부분을 개발
- Data Layer : 데이터 베이스 및 외부와의 연동을 처리
- 특정 구역의 반복되는 로직을 한 곳에 몰아서 개발할 수 있게 해준다.
- 주요 Annotation
- Aspect : 자바에서 널리 사용하는 AOP 프레임워크에 포함되며, AOP를 정의하는 Class에 할당
- Pointcut : 기능을 어디에 적용시킬지, 메소드 Annotation 등 AOP를 적용 시킬 지점 설정
- Before : 메소드 실행하기 이전
- After : 메소드가 성공적으로 실행 후, 예외가 발생 되더라도 실행
- AfterReturing : 메소드 호출 성공 실행 시(Not Throws)
- AfterThrowing : 메소드 호출 실패 예외 발생(Throws)
- Around : Before/ After 모두 제어