
spring-aop spring-context-indexer spring-instrument spring-orm spring-web
spring-aspects spring-context-support spring-jcl spring-oxm spring-webflux
spring-beans spring-core spring-jdbc spring-r2dbc spring-webmvc
spring-context spring-expression spring-jms spring-test spring-websocket
spring-messaging spring-tx
20개가 넘는 spring의 모듈이 있다. 이를 크게 6 가지로 분류하면 다음과 같다.
core
testing
data access
web & remoting
spring mvc & spring webflux
remoting 기술인 RMI, Hessian, Burlap, JAX-WS, HTTP 호출자, REST API 제공
instrumentation
aop (aspect oriented programming)


ASM
엄밀히 말해 스프링 모듈이 아니지만, 클래스 바이트코드 조작 및 분석 프레임워크인 ASM을 재 패키징한 모듈이다.
ASM 프레임워크는 스프링 뿐 아니라 여러 프레임워크에서 및 라이브러리에서 사용되는데, 이때 스프링 프레임워크와 ASM을 사용하는 다른 프레임워크와의 충돌을 방지하기 위해 org.springframework.asm 패키지로 재패키징해 독립적인 모듈로 제공한다.
Core
거의 대부분의 다른 스프링 모듈에서 필요로 하는 공통 기능을 갖는 핵심 모듈이다.
스프링에서 사용하는 주요 어노테이션, 컨버터, 상수, 유틸리티 클래스 등을 제공한다.
필수 라이브러리 - 없음
선택적 의존 - ASM
Beans
Beans는 스프링 DI 기능의 핵심인 빈 팩토리와 DI 기능을 제공하는 모듈이다.
빈 메타 정보, 빈 리더, 빈 팩토리의 구현과 프로퍼티 에디터가 포함되어 있다.
애플릿이나 모바일같은 제한된 환경에서 스프링의 DI 기능만 적용하고 싶으면 Beans 모듈까지만 적용하면 된다.
필수 라이브러리 - ASM, Core
AOP
AOP는 스프링의 프록시 AOP 기능을 제공하는 모듈이다.
프록시 기반 AOP를 만들 때 필요한 advice, pointcut, 프록시 팩토리빈, 자동 프록시 생성기 등을 제공한다.
필수 라이브러리 - Beans
Expression
Expression은 스프링 표현식 언어(SpEL) 기능을 지원한다.
필수 라이브러리 - Core
Cotext
Cotext는 어플리케이션 컨텍스트 기능을 제공한다.
어플리케이션 컨텍스트를 만드는 데 필요한 대부분의 기능과 빈 스캐너, 자바코드 설정 기능, EJB 지원, 포메터, 로드타임 위빙, 표현식, JMX JNDI, 리모팅, 스케줄링, 스크립트 언어 지원, 검증기 등의 컨테이너로서의 주요한 기능을 담고 있다.
단순한 빈팩토리가 아닌 엔터프라이즈 어플리케이션 프레임워크로 사용하기 위해 반드시 필요하다.
필수 라이브러리 - AOP, Expression
선택적 의존 - 로드 타임 위빙을 사용하는 경우 Instrument 모듈 필요
Context.Support
Context.Support의 경우 어플리케이션 컨텍스트에서 필요로 하는 부가기능을 지원한다.
EhCache, 메일 추상화 서비스, CommonJ와 Quartz 스케줄링, FreeMarker, JasperReports, Velocity 팩토리 기능을 제공한다.
해당 기능을 사용하지 않는다면 Context.Support은 필요 없다.
단 스프링 MVC가 Context.Support에 의존하므로 스프링 MVC를 사용한다면 필수로 추가해야 한다.
필수 라이브러리 - Context
선택적 의존 - Quartz의 JobStore 기능을 활용하는 경우 JDBC, Transaction 모듈 필요
트랜잭션(Transaction)
트랜잭션은 스프링의 데이터 액세스 추상화의 공통 기능을 담고 있다.
DataAcssessException 예외 계층구조와 트랜잭션 추상화 기능, 트랜잭션 동기화 저장소 그리고 JCA 기능을 포함한다.
필수 라이브러리 - Context
JDBC
JDBC는 JDBC 템플릿을 포함한 JDBC 지원 기능을 제공한다.
JdbcTemplate 등의 JDBC 지원 오브젝트 외에도 스프링이 직접 제공하는 DataSource 구현 클래스들이 제공됩니다.
필수 라이브러리 - 트랜잭션
ORM
ORM은 하이버네이트, JPA, JDO, iBatis와 같은 ORM에 대한 스프링의 지원 기능을 포함한다.
ORM은 내부적으로 JDBC를 사용한다.
필수 라이브러리 - JDBC
선택적 의존 - OpenSessionInViewFilter 같은 일부 기능은 Web 모듈에 선택적으로 의존
Web
Web은 스프링 웹 기술의 공통적인 기능을 정의한 모듈이다.
spring MVC 외에도 스프링이 지원하는 스트럿츠, JSF 등을 적용할 때 필요하다.
또한 Caucho, HttpInvoker, JAX-RPC, JAX-WS 등의 리모팅 기능도 포함한다.
기본적으로 바인딩, 컨텍스트 로더, 필터, 멀티파트, 메세지 컨버터 기능도 제공한다.
필수 라이브러리 - Context
선택적 의존 - XML을 사용하는 메세지 컨버터 기능에는 OXM 모듈이 필요
웹 서블릿(Web.Servlet)
웹 서블릿은 spring MVC 기능을 제공하는 모듈이다.
전통적인 MVC와 최신 @MVC 기능이 모두 포함되어 있다.
필수 라이브러리 - Web, Context.Support
선택적 의존 - XML을 사용하는 뷰나 메세지컨버터 등을 사용할 때에는 OXM 모듈이 필요
웹 포틀릿(Web.Portlet)
웹 포틀릿은 Portlet 개발에 사용하는 스프링 모듈이다.
필수 라이브러리 - Web.Servlet
웹 스트럿츠(Web.Struts)
웹 스트럿츠는 스트럿츠 1.x를 지원하는 모듈이다.
필수 라이브러리 - Web
JMS
JMS는 스프링의 JMS 지원 기능을 사용할 때 필요한 모듈이다.
필수 라이브러리 - Transaction
Aspects
Aspects는 스프링이 제공하는 AspectJ AOP를 사용할 때 필요한 모듈이다.
AspectJ는 스프링의 @Configurable을 이용한 도메인 오브젝트 DI 기능, JPA 예외 변환기, AspectJ 트랜잭션을 만들 때도 사용된 기술이다.
선택적 의존 - JPA 지원 기능 사용시 ORM, 트랜잭션 기능 지원시 Transaction 필요
Instrument
Instrument는 스프링의 로드타임위버(LTW) 기능을 적용할 때 필요하다.
JVM의 -javaagent 옵션을 사용해 자바에이전트로도 사용된다.
Instrument.Tomcat
Instrument.Tomcat은 어플리케이션이 아닌 톰캣 서버의 클래스 로더로 사용하는 모듈이다.
Test
Test는 스프링의 테스트 지원 기능을 가진 모듈이다.
테스트 컨텍스트 프레임워크나 목 오브젝트 등을 이용해 테스트 할 때 사용된다.
테스트용 모듈이기 때문에 운영 중에는 사용되지 않아야 된다.
코드의 흐름을 제어하는 주체가 바뀌는 것
🤔코드의 흐름을 제어한다?
💡IoC를 적용한다?
@Service
public class CouponService {
public void updateCoupon(){...};
public void addCoupon(){...};
}
Service 로직을 작성할 때 생명주기 메소드가 호출되었을 때의 동작만 정의하지, 언제 생명주기 메소드를 호출할지는 신경쓰지 않는다.
즉, Service의 실행 제어권은 spring framework에서 쥐고 있다.
✔ DI는 DIP를 구현하는 기법 중 하나이다.
✔IOC를 적용한 결과 중 하나가 DI이다.
DIP
Dependency Inversion principle의 줄임말.
SOLID 원칙 중 하나로, 핵심은 추상화에 의존하라는 것이다.a. High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces).
b. Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.예를 들어 다음과 같은 상황일 때,
추상화가 아닌 구체 클래스에 의존한 경우
public class DaoFactory {
private ConnectionMaker connectionMaker;
public DaoFactory(NaverConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
...
}
}
ConnectionMaker가 NaverConnectionMaker에서 DaumConnectionMaker로 바뀌어야 할 때 DaoFactory class까지 변경이 발생해버린다!
즉, 변경에 유연하지 못한 구조이다.
추상화에 의존한 경우
public class DaoFactory {
private ConnectionMaker connectionMaker;
public DaoFactory(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
...
}
}
DaoFactory는 ConnectionMaker interface에 의존하기 때문에 구현체인 NaverConnectionMaker이 변경하여도 DaoFactory는 영향을 받지 않는다.
즉, 변경에 유연한 구조가 된다.
dependency injection의 줄임말
필요로 하는 오브젝트를 스스로 생성하는 것이 아닌, 외부로부터 주입받는 기법을 말한다.
DI를 적용하는 2가지 기법이 있다.
public class DaoFactory {
private ConnectionMaker connectionMaker;
public DaoFactory(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
...
}
}
public class DaoFactory {
private ConnectionMaker connectionMaker;
public void setConnectionMaker(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
...
}
}
public class DaoFactory {
private ConnectionMaker connectionMaker;
public void putConnectionMaker(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
...
}
}
bean의 생성과 관계 설정 같은 제어를 담당하는 IoC 오브젝트를 의미한다.
BeanFactory는 빈을 생성하고 관계를 설정하는 IoC의 핵심 기능에 초점을 둔 interface다.
classDiagram
direction BT
class BeanFactory {
<<Interface>>
+ getBean(String, Class~T~) T
+ isPrototype(String) boolean
+ getBean(String, Object[]) Object
+ getBeanProvider(Class~T~) ObjectProvider~T~
+ getBean(String) Object
+ getType(String, boolean) Class~?~?
+ isSingleton(String) boolean
+ isTypeMatch(String, Class~?~) boolean
+ getBeanProvider(ResolvableType) ObjectProvider~T~
+ getType(String) Class~?~?
+ getBean(Class~T~, Object[]) T
+ containsBean(String) boolean
+ getAliases(String) String[]
+ getBean(Class~T~) T
+ isTypeMatch(String, ResolvableType) boolean
+ String FACTORY_BEAN_PREFIX
}
ApplicationContext는 BeanFactory에 다음 기능을 추가한 하위 interface다.

ApplicationContext의 구현체
spring IoC container가 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 의미한다.
✔Spring IoC container는 bean정보와 연관 관계 정보가 담긴 metadata를 사용해 bean들을 조립한다.

configuration metadata
- 어떤 형태일까?
XML
Java annotation- 어떤 정보를 포함하고 있을까?
빈을 식별할 id
빈의 class type
의존하는 빈(optional)
BeanDefinition
Spring IoC container가 갖고 있는 bean metadata를 포함한 interface다.

Bean을 정의하는 정보
🤔스프링에 등록된 빈은 어떻게 사용하나?
T getBean(String name, Class<T> requiredType)로 원하는 빈을 spring IoC container에서 꺼내올 수 있다.
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
애플리케이션에 산재해서 나타나는 부가 기능을 모듈화
즉, 여러 type의 instance에 나타나는 기능(ex. transaction)를 모듈로 분리해낼 수 있다.
싱글톤 패턴은 GoF가 소개한 디자인 패턴 중의 하나다. 디자인 패턴 중에서 가장 자주 활용되는 패턴 이기도 하지만 가장 많은 비판을 받는 패턴이기도 하다. 심지어 디자인 책을 쓴 GoF 멤버조차도 싱글 톤 패턴은 매우 조심해서 사용해야 하거나 피해야 할 패턴이라고 말하기도 한다. 싱글톤 패턴은 어떤 클래스를 애플리케이션 내에서 제한된 인스턴스 개수, 이름처럼 주로 하나만 존 재하도록 강제하는 패턴이다. 이렇게 하나만 만들어지는 클래스의 오브젝트는 애플리케이션 내에서 전역적으로 접근이 가능하다. 단일 오브젝트만 존재해야 하고, 이를 애플리케이션의 여러 곳에서 공 유하는 경우에 주로 사용한다.
자신의 기능 맥락context에서, 필요에 따라 변경이 필요 한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴
여기서 알고리즘이란, 독립적인 책임으로 분리가 가능한 기능을 말한다.
바뀔 수 있는 쪽의 클래스는 인터페이스를 구현하도록 하고, 다른 클래스에서 인터 페이스를 통해서만 접근하도록 만들었다. 이렇게 해서 인터페이스를 정의한 쪽의 구현 방법 이 달라져 클래스가 바뀌더라도, 그 기능을 사용하는 클래스의 코드는 같이 수정할 필요가 없도록 만들면 된다.

상속을 통해 슈퍼클래스의 기능을 확장할 때 사용하는 가장 대표적인 방법이다.
변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확장할 기능은 서브클래스에서 만들도록 한다.
슈퍼클래스 에서는 미리 추상 메소드 또는 오버라이드 가능한 메소드를 정의해두고 이를 활용해 코드의 기본 알고리즘을 담고 있는 템플릿 메소드를 만든다.
슈퍼클래스에서 디폴트 기능을 정의해두거나 비워뒀다 가 서브클래스에서 선택적으로 오버라이드할 수 있도록 만들어둔 메소드를 훅(hook) 메소드라고 한 다.
서브클래스에서는 추상 메소드를 구현하거나, 훅 메소드를 오버라이드하는 방법을 이용해 기능의 일부를 확장한다.



팩토리 메소드 패턴도 템플릿 메소드 패턴과 마찬가지로 상속을 통해 기능을 확장하게 하는 패턴이 다. 그래서 구조도 비슷하다.
슈퍼클래스 코드에서는 서브클래스에서 구현할 메소드를 호출해서 필요 한 타입의 오브젝트를 가져와 사용한다.
이 메소드는 주로 인터페이스 타입으로 오브젝트를 리턴하므 로 서브클래스에서 정확히 어떤 클래스의 오브젝트를 만들어 리턴할지는 슈퍼클래스에서는 알지 못 한다. 사실 관심도 없다.
서브클래스는 다양한 방법으로 오브젝트를 생성하는 메소드를 재정의할 수 있다.
이렇게 서브클래스에서 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해둔 메소 드를 팩토리 메소드라고 하고,
이 방식을 통해 오브젝트 생성 방법을 나머지 로직, 즉 슈퍼클래스의 기본 코드에서 독립시키는 방법을 팩토리 메소드 패턴이라고 한다.

자바에서는 종종 오브젝트를 생성하는 기능을 가진 메소드를 일반적으 로 팩토리 메소드라고 부르기도 한다. 이때 말하는 팩토리 메소드와 팩토리 메소드 패턴의 팩토리 메 소드는 의미가 다르므로 혼동하지 않도록 주의해야 한다
전략 패턴의 기본 구조에 익명 내부 클래스를 활용한 방식의 디자인 패턴이다.
템플릿(template)은 어떤 목적을 위해 미리 만들어둔 모양이 있는 틀을 가리킨다.
프로그래밍에서는 고정된 틀 안에 바꿀 수 있는 부분을 넣어서 사용하는 경우에 템플릿이라고 부른다.
JSP는 HTML이라는 고정된 부분에 EL 과 스크립릿이라는 변하는 부분을 넣은 일종의 템플릿 파일이다.
템플릿 메소드 패턴은 고정된 틀의 로직을 가진 템플릿 메소드를 슈퍼클래스에 두고, 바뀌는 부분을 서브클래스의 메소드에 두는 구조로 이뤄진다
콜백(callback)은 실행되는 것을 목적으로 다른 오브젝트의 메소드에 전달되는 오브젝트를 말한다.
파라미터로 전달되지만 값을 참조하기 위한 것이 아니라 특정 로직을 담은 메소드를 실행시키기 위해 사용한다.
자바에선 메소드 자체를 파라미터로 전달할 방법은 없기 때문에 메소드가 담긴 오브젝트를 전달해야 한다.
그래서 펑셔널 오브젝트(functional object)라고도 한다.



마치 자기가 client가 원했던 대상이었던 것처럼 위장해서 client의 요청을 받아주는 것을 proxy라고 한다.
proxy를 통해 최종적으로 요청을 외주 받아 처리하는 것은 target 또는 real subject라고 한다.
proxy는 target과 같은 인터페이스를 구현 + target을 제어할 수 있는 점에서 멋지다.
proxy 사용 목적 2가지!
타깃에 부가적인 기능을 런타임 시 다이내믹하게 부여해주기 위해 프록시를 사용하는 패턴이다.
🤔dynamiccally 기능 부여?
코드 상에서는(aka compile time) 어떤 방법과 순서로 proxy와 target이 연결되어 사용되는지는 정해져 있지 않다는 의미다. 고로 proxy가 여러 개일 수 있다.
Ex. runtime에 proxy들을 적절한 순서로 조합해서 사용할 수 있다.
이 때 proxy로 동작하는 decorator들은 위임하는 대상에도 interface로 접근하기 때문에 지가 target에 접근하는지 or 또다른 decorator에 접근하는지도 모른다. ➡decorator의 다음 위임 대상은 인터페이스로 선언 + 생성자나 수정자로 위임 대상을 runtime시에 주입받을 수 있게 해야한다.

proxy != proxy pattern
proxy : client - 사용 대상 사이에 대리 역할인 obj를 두는 방법을 총칭
proxy pattern : proxy 사용 방법 중 target에 대한 접근 방법을 제어하려고 proxy를 사용한 경우. 즉 타깃의 기능을 확장하거나 추가하지 않는다. 대신 client가 target에 접근하는 방식을 바꿔준다.
target obj가 겁나 복잡하거나 당장 필요한게 아니라면 굳이 섣부르게 만들 필요가 없다.
하지만 client에게 target obj에 대한 reference가 미리 필요할 순 있다. 이 때 실제 obj가 아닌 proxy를 넘기는 거다!
실제 obj야 뭐 proxy method로 target을 사용하려고 하면 그때 만들면 된다.
원격 obj! 그러니까 다른 서버에 존재하는 obj를 써야 한다면 원격 obj에 대한 proxy를 만들어두고 client는 마치 로컬 obj대하듯 내버려 두면,proxy입장에선 client의 요청을 받았을때 그제서야 네트워크로 원격의 obj를 실행하고 결과를 받아서 client에게 돌려주면 된다!
또 특별한 상황에서 target에 대한 접근 권한에 차등을 둘때도 proxy pattern 쓸 수 있다.
Collections의 unmodifidableCollection()은 parameter로 넘어온 놈의 proxy를 만들어서 add()같은 수정 메소드 호출할 경우 예외가 발생하게 해준다.
접근 제어를 위한 proxy pattern + paging 기능을 위한 proxy쓰는 decorator pattern

dynamic proxy는 reflection 기능을 이용해 proxy를 만들어준다.
🤔reflection : 구체적인 클래스 타입을 몰라도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 Java API

문제 상황
구현할 인터페이스
interface Hello {
String sayHello(String name);
String sayHi(String name);
String sayThankYou(String name);
}
구현한 target class
class HelloTarget implements Hello {
@Override
public String sayHello(String name) {
return "Hello" + name;
}
@Override
public String sayHi(String name) {
return "Hi" + name;
}
@Override
public String sayThankYou(String name) {
return "Thank You" + name;
}
}
Hello interface를 통해 HelloTarget obj를 사용할 client
@Test
public void simpleProxy() {
Hello hello = new HelloTarget();
assertThat(hello.sayHello("mtak")).isEqualTo("Hello mtak");
assertThat(hello.sayHi("mtak")).isEqualTo("Hi mtak");
assertThat(hello.sayThankYou("mtak")).isEqualTo("Thank You mtak");
}
자 ~! 이제 Hello interface를 구현한 proxy 😆decorator pattern 적용해서 HelloTarget에 부가 기능(upper case) 넣는다.
class HelloUppercase implements Hello{
Hello hello;
public HelloUppercase(Hello hello) {
this.hello = hello;
}
@Override
public String sayHello(String name) {
return hello.sayHello(name).toUpperCase();
}
@Override
public String sayHi(String name) {
return hello.sayHi(name).toUpperCase();
}
@Override
public String sayThankYou(String name) {
return hello.sayThankYou(name).toUpperCase();
}
}
@Test
public void simpleProxy() {
Hello hello = new HelloTarget();
assertThat(hello.sayHello("mtak")).isEqualTo("Hello mtak");
assertThat(hello.sayHi("mtak")).isEqualTo("Hi mtak");
assertThat(hello.sayThankYou("mtak")).isEqualTo("Thank You mtak");
Hello hello1 = new HelloUppercase(new HelloTarget());
assertThat(hello1.sayHello("mtak")).isEqualTo("HELLO MTAK");
assertThat(hello1.sayHi("mtak")).isEqualTo("HI MTAK");
assertThat(hello1.sayThankYou("mtak")).isEqualTo("THANK YOU MTAK");
}
🤔전형적인 proxy문제를 다 가지고 있다. 귀찮게 인터페이스 모든 메소드 오버라이드 해야되고, 부가 기능(toUppercase())이 모든 메소드에 중복되서 나타난다.
dynamic proxy를 만들어보자!
class UppercaseHandler implements InvocationHandler {
Hello target;
public UppercaseHandler(Hello target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String ret = (String) method.invoke(target, args);
return ret.toUpperCase();
}
}
//dynamic proxy obj
Hello proxyHello = (Hello) Proxy.newProxyInstance(getClass().getClassLoader(),//동적으로 생성되는 dynamic proxy class에 쓸 class loader
new Class[]{Hello.class},//구현할 interface
new UppercaseHandler(new HelloTarget())//부가 기능과 위임 코드를 담은 InvocationHandler
);
assertThat(proxyHello.sayHello("mtak")).isEqualTo("HELLO MTAK");
assertThat(proxyHello.sayHi("mtak")).isEqualTo("HI MTAK");
assertThat(proxyHello.sayThankYou("mtak")).isEqualTo("THANK YOU MTAK");
예외 처리 및 버전 검색을 위한 기본 클래스와 프레임워크의 특정 부분이 아닌 기타 핵심 도우미를 제공

Java Bean을 조작하기 위한 인터페이스와 클래스가 포함되어 있다.
BeanWrapper 객체는 bean 속성을 단독으로 또는 대량으로 설정하고 가져오는 데 사용할 수 있습니다.

AOP Alliance AOP 상호 운용성 인터페이스를 기반으로 하는 코어 Spring AOP 인터페이스.
Spring AOP는 프로그래밍 방식으로 사용하거나 (가급적이면) Spring IoC 컨테이너와 통합하여 사용할 수 있다.

context API : https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/package-summary.html

잘 봤습니다. 좋은 글 감사합니다.