OOP Study 에서 Spring Study로 방향성을 변경한 후, 처음으로 발표한 내용이다.
Spring의 기초에는 스프링의 핵심기술인 IoC/DI, AOP, PSA 내용을 빼먹을 수 없겠지만, 다음 챕터에서 정리할 내용이기에 스프링 모듈쪽을 중심으로 정리했다.
Spring의 기초
Spring의 모듈 구성
Spring Core Container
- 스프링 프레임워크 의존성 주입
- IoC(Inversion of Control) 컨테이너 및 애플리케이션 컨텍스트 핵심 기능을 제공한다.
모듈 | 설명 |
spring-core | - 스프링 모듈이 사용하는 핵심 유틸리티 - 프레임 워크의 기반을 제공 |
spring-beans | - BeanFactory 인터페이스를 통한 구현을 포함 - 의존성 주입을 제공함 - Spring Bean 지원 |
spring-context | - spring-core, spring-beans 모듈을 확장해 BeanFactory를 상속하는 Application context를 구현 - 리소스 로드 및 국제화 - 메시지 지원을 제공 - 객체에 접근하기 위한 방법을 제공 |
spring-expression | - 런타임에 객체 그래프를 쿼리하고 조작하기 위한 EL을 제공. - EL(Expression Language)는 속성값을 설정하고 가져오는 것, 속성 부여, 메서드 호출, 배열/컬렉션/익덱서 등 내용에 접근 등 작업을 스프링의 IoC 컨테이너라는 이름 하에 서포트한다. *EL: 런타임에서 객체에 대한 쿼리와 조작(querying and manipulating)을 지원하는 강력한 표현 언어이다.(#기호로 시작해서 중괄호로 묶음) |
spring-context-support | - 스케줄링, 메일 발송, 캐시 연동 등의 부가 기능을 제공 |
AOP and Instrumentation
- 로깅 및 보안과 같은 모든 Application Layer에 적용할 수 있다.
- AOP는 일반적으로 횡단 관심을 구현하는데 사용한다.
모듈 | 설명 |
spring-aop | - AOP 구현을 위한 메소드 Intercepter와 point cut을 사용해 관점지향 프로그래밍에 대한 기본적인 지원을 제공 |
spring-aspects | - 가장 인기있는 AOP 프레임워크인 AspectJ와의 통합을 제공 |
spring-instrument | - instrumentation을 지원하는 클래스와 특정 WAS에서 사용하는 클래스로 구현체를 제공 - BCI(Byte Code Instrumentations)는 런타임이나 로드(load) 때 클래스의 바이트 코드에 변경을 가하는 방법을 말함 |
spring-messaging | - 메시지 기반 app을 작성할 수 있는 Message, MessageChannel, MessageHandler를 제공 - 각 모듈에는 메소드에 메시지를 맵핑하기 위한 annotation도 포함되어 있으며, spring MVC와 유사 |
Web
- 스프링은 스트럿츠와 같은 대중적인 웹 프레임워크와 훌륭한 통합을 제공하는 것 외에도 자체 MVC 프레임워크인 스프링 MVC를 제공한다.
모듈 | 설명 |
spring-web | - 기본적인 웹 기반을 위한 공통적인 기능을 제공 및 정의한 모듈 - 다중 FileUpload처리 - 리스너와 웹 기반으로 하는 application context를 위한 IoC 컨테이너의 초기화를 제공 |
spring-web-servlet | - webmvc라고도 함 - 웹 어플리케이션 구현을 위한 SpringMVC를 제공 - Struts, WebSocket과 같은 프레임 워크의 통합을 지원. |
spring-websocket | - WebSocket프로그래밍을 지원하는 모듈 |
spring-webmvc-protlet | - 포털(포틀릿) 기반의 MVC 구현을 위한 모듈을 제공 |
Data Access / Integration
- JDBC , ORM , 트랜잭션 등 서비스 추상화를 이용해 쉽게 데이터에 접근하는 방법을 제공한다.
모듈 | 설명 |
spring-jdbc | - JDBC 기반 하의 DAO 개발을 좀 더 쉽고, 일관된 방법으로 개발하는 것이 가능하도록 추상화된 레이어를 제공. |
spring-orm | - Object Relation Mapping 프레임워크인 Hibernate, iBatis, JDO, JPA와의 통합을 지원 - 내부적으로는 JDBC를 사용 |
spring-oxm | - Object/XML Mapping 은 Object와 XML 간의 변환을 위한 추상 계층을 제공. (JAXB, Castor, XMLBeans,JiBX, XStream 등) |
spring-tx | - 스프링의 데이터에 직접적인 트랜잭션 관리 - 선언적인 트랜잭션 관리에 있어 일관된 추상화를 제공하고 DataAcssessException 예외 계층 구조와 트랜잭션 동기화 저장소 JCA기능을 제공하거나 포함 |
spring-jms | - Java Message Service - 메시징 처리를 위한 모듈을 제공. |
Test
단위 및 통합 테스팅에 대한 기본 지원을 제공
스프링 모듈의 관계성
스프링 컨테이너와 스프링 빈
💡 스프링 컨테이너는 내부에 존재하는 애플리케이션 빈의 생명주기를 관리한다.
스프링 컨테이너는 빈(스프링에서 관리하는 객체)을 관리한다. 이러한 개념에서 스프링 컨테이너를 부른다면 BeanFactory라고 부를 수도 있고, 메시지 국제화, 리소스 로드 등 부가적인 기능을 포함해서 포괄적이게 부른다면 Application Context라고도 부를 수 있다.
스프링 컨테이너는 의존성 주입을 통해 빈을 관리하는데 이를 통해 DI컨테이너라고도 부르고 있다.(IoC컨테이너라고도 부름)
- 스프링 컨테이너는 XML, 애노테이션 기반의 자바 설정 클래스로 만들 수 있다.
- 빈의 인스턴스화, 구성, 전체 생명 주기 및 제거까지 처리한다.
- 이때 스프링 컨테이너는 빈을 싱글톤으로 관리하게 된다. 싱글톤으로 관리하고 있기에 싱글톤 레지스트리 역할을 한다고 볼 수 있다.
- 싱글톤 패턴에는 여러 단점이 존재한다. 하지만 스프링 컨테이너가 빈을 싱글톤으로 관리해주기 때문에 이에 대한 문제점들이 해결된다.
- 의존성 주입을 통해 어플리케이션의 컴포넌트를 관리한다.
XML, 애노테이션으로 설정하여 스프링 컨테이너를 만들 수 있는 이유는 BeanDefinition(빈 설정 메타정보)이라는 추상화 덕분이다.
Spring이 설정 메타정보를 BeanDefinition 인터페이스를 통해 관리하기 때문에 컨테이너 설정을 XML, 애노테이션으로 할 수 있는 것이다.(스프링 컨테이너는 XML로 설정 했는지, 자바 클래스로 설정 했는지 알 필요가 없음!)
Spring Source 직접 까보기
ApplicationContext ctx = new AnnotationConfigApplicationContext(DependencyConfig.class);
MyService myService = ctx.getBean(MyService.class);
- ApplicationContext
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
}
- EnvironmentCapable: profile과 property 관리
- ListableBeanFactory: 빈 리스트를 처리하기 위한 퍼블릭 인터페이스
- HierarchicalBeanFactory: 여러 BeanFactory들 간의 계층(부모-자식) 관계를 설정하기 위한 퍼블릭 인터페이스
- MessageSource: 메시지 국제화
- ApplicationEventPublisher: 이벤트 처리, 옵저버 패턴이 적용됨
- ResourcePatternResolver : ResourceLoader 인터페이스의 확장이고, 위치 패턴 해석에 대한 전략을 정의
- AnnotationConfigApplicationContext
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
... 생략
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses); // AnnotationBeanDefinitionReader에서 처리
// BeanDefinitionHolder 구현체에 BeanDefinition 필드 존재
refresh(); // 빈 생성 및 컨테이너 초기화
// refresh()메소드 안, onRefresh()에는 템플릿 메소드 패턴이 적용됨
}
@Override
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
.tag("classes", () -> Arrays.toString(componentClasses));
this.reader.register(componentClasses);
registerComponentClass.end();
}
Spring vs Spring Boot
Spring Application 구축에 대해서 독립적인 관점을 취해 가능한 빨리 시작하고 실행할 수 있도록 한다.
SpringBoot를 프레임워크라고 생각했었는데, 공식 사이트에선 프로젝트로 설명되고 있다. 스프링의 서브 프로젝트라고 생각하는 것이 더 나을 것 같다.
💡Spring Boot: 스프링을 더 쉽게 이용하기 위한 도구, 스프링의 여러 프로젝트 중 하나이다.
Spring boot의 특징
- 단독 실행 가능한 스프링 애플리케이션을 생성한다.
- WAS를 내장하고 있어 따로 tomcat이나 jetty를 설정하지 않아도 된다.
- 스프링 프레임워크의 복잡한 XML설정이 필요 없다.
- 간단하게 라이브러리를 관리할 수 있다. (라이브러리를 모아 놓은 Starter를 제공함)
내장 서버
- Spring MVC의 경우
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
config.MemberConfig
config.MvcConfig
config.ControllerConfig
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
직접 톰캣을 받아와야 하며 복잡한 XML설정까지도 해야한다.
- Spring Boot의 경우
WAS를 따로 설정해야 하는 Spring과 다르게 Spring Boot에서는 WAS가 내장되어 있어 바로 실행이 가능하다.
이것이 왜 가능한 것일까?
Spring Boot에서는 ServletWebServerFactoryAutoConfiguration 클래스에서 자동적으로 내장 웹서버에 대한 설정을 처리하고, 사용자가 웹 서버 관련 설정을 하지 않아도 웹 서버를 프로젝트 안에 포함시킨다.
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args); // 이쪽을 주목
}
}
최상위 메인 메서드를 확인해보면 run()메서드를 확인할 수 있다. 이 메서드가 실행되면, 스프링 컨테이너와 WAS를 생성한다.
- 웹 서버 설정(ServletWebServerFactoryAutoConfiguration )
@AutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {
return new ServletWebServerFactoryCustomizer(serverProperties,
webListenerRegistrars.orderedStream().collect(Collectors.toList()),
cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
- 톰캣 생성(TomcatServletWebServerFactory)
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat(); // 톰캣 생성
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
for (LifecycleListener listener : this.serverLifecycleListeners) {
tomcat.getServer().addLifecycleListener(listener);
}
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
스프링 컨테이너 자동생성
- 스프링 컨테이너를 생성하는 코드 (ServletWebServerApplicationContextFactory)
class ServletWebServerApplicationContextFactory implements ApplicationContextFactory {
@Override
public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : ApplicationServletEnvironment.class;
}
@Override
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : new ApplicationServletEnvironment();
}
@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : createContext();
}
private ConfigurableApplicationContext createContext() {
if (!AotDetector.useGeneratedArtifacts()) {
return new AnnotationConfigServletWebServerApplicationContext(); // 컨테이너 생성!
}
return new ServletWebServerApplicationContext();
}
}
AnnotationConfigServletWebServerApplicationContext : 애노테이션 기반 설정, 서블릿 웹 서버를 지원하는 스프링 컨테이너
자동 설정
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication 을 주목하자. 이 애노테이션이 자동 구성을 해주는 애노테이션이다.
.. 생략
@SpringBootConfiguration // Spring의 @Configuration과 동일한 역할(사용자가 추가적으로 빈이나 설정 클래스들을 등록 가능하게 함)
@EnableAutoConfiguration // jar properties를 기반으로 자동으로 의존성 설정(META-INF/spring.factories)
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
간편한 구성 관리
- 스프링 종속성 관리
- 스프링 부트 종속성 관리
스프링 부트에서는 관련 라이브러리를 묶어준 stater를 제공해주기 때문에 스프링 보다 더 간편하게 관리할 수 있다. 심지어 버전도 따로 명시해주지 않아도 된다!(경우에 따라서 명시해줘야 하는 것들도 있음)
'Spring > 개념' 카테고리의 다른 글
[Spring] 스프링 AOP (0) | 2023.05.11 |
---|---|
[Spring Security] 스프링 시큐리티의 동작 구조 (0) | 2023.05.11 |
[Spring Security] 스프링 시큐리티와 FilterChain (1) | 2023.05.11 |
[Spring] Filter, Interceptor, AOP의 차이점에 대해 (0) | 2023.04.10 |
[Spring] 스프링(Spring Framework)의 정의와 특징 (0) | 2023.04.02 |