의존성 : 하나의 객체가 다른 객체 없이 제대로 된 역할을 수행하지 못하는 것
예를 들어 음식점에서 서빙 직원이 없을 경우 → ㄱㅊ
그런데 주방장이 없을 경우 → 큰일!!
이처럼 A 객체가 B 객체 없이 정상 동작 불가한 경우 A가 B에게 의존적이다
외부에서 밀어넣는 것.
예를 들어 어떤 음식점은 식재료를 구하기 위해서 직접 시장을 간다. 반면에 다른 프랜차이즈 가게는 본사에서 알아서 갖다 줌
→ 필요한 객체를 얻기 위해서 주체가 능동적인가 수동적인가?
즉, 어떤 객체에게 필요한 객체를 외부에서 밀어 넣는 것이 의존성과 주입의 개념
이러면 무슨 이득을 보는가?
그림으로 보면 아래와 같다.
1.객체 A가 B를 직접 생성
2. A는 B가 필요하다는 신호를 보낸다 → B객체를 주입하는 것은 외부에서 이루어진다.
2번 그림이 의존성 주입을 수행하는 방식인데, 이를 위해서 추가적으로 하나의 외부 존재가 필요하고, 스프링에서는 이 존재가 ApplicationContext라는 존재이다.
ApplicationContext가 관리하는 객체들을 Bean이라고 부르고, 빈과 빈 사이의 의존관계를 처리하는 방식을 지정하기 위해서 XML 설정, 어노테이션 설정, JAVA 설정 방식을 사용가능
Aspect Oriented Programming
보안, 로그, 트랜잭션 등 비즈니스 로직은 아니지만 반드시 처리가 필요한 부분들을 횡단 관심사라고 한다. 스프링은 이러한 횡단 관심사를 분리하여 제작 가능하고, 이를 AOP라고 하는 것
AspectJ 문법을 이용하여 작성가능하다.
식당에서 일하는 주방장..
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.webproj</groupId>
<artifactId>springBoard</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--spring-test 추가-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<!--lombok과 log4j 추가-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<org.springframework-version>5.3.22.RELEASE</org.springframework-version>
</properties>
</project>
package com.example.springboard.sample;
import lombok.Data;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component // 스프링에게 해당 클래스가 스프링에서 관리해야할 대상임을 표시
@Data // Lombok의 setter를 생성하는 기능, 생성자, toString() 등 자동으로 생성하도록 한다.
public class Restaurant {
// 자동으로 setChef()를 컴파일 시 생성
@Setter(onMethod_ = @Autowired) // setChef()에 @Autowired 어노테이션 추가
private Chef chef;
}
package com.example.springboard.sample;
import lombok.Data;
import org.springframework.stereotype.Component;
@Component
@Data
public class Chef {
}
위 코드는 아래와 같은 구조를 가진다.
스프링 프레임워크 시작 시 스프링이 사용하는 메모리 영역인 Context가 생성된다. 이 객체를 ApplicationContext라고 한다.
스프링이 생성하고 관리해야하는 객체들에 대한 설정 파일인 root-context.xml 파일을 읽는다.
root-context.xml에 설정되어 있는 <context:component-scan>
태그의 내용 읽고 해당되는 패키지를 스캔한다.
해당 패키지의 클래스 중 @Component
어노테이션이 존재하는 클래스의 인스턴스 생성
Restaurant 객체는 Chef 객체가 필요하다고 알리는 @Autowired
설정이 존재하기에 스프링은 Chef 객체의 레퍼런스를 Restaurant에 주입
spring-test 모듈을 사용한다.
<context:component-scan base-package="com.example.springboard.sample">
</context:component-scan>
<?mappers version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration>
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
</layout>
</appender>
<!-- Application Loggers -->
<logger name="Controller">
<level value="info" />
</logger>
<!-- 3rdparty Loggers -->
<logger name="org.springframework.core">
<level value="info" />
</logger>
<logger name="org.springframework.beans">
<level value="info" />
</logger>
<logger name="org.springframework.context">
<level value="info" />
</logger>
<logger name="org.springframework.web">
<level value="info" />
</logger>
<!-- Root Logger -->
<root>
<priority value="info" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
package com.example.springboard.sample;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class SampleTests {
@Setter(onMethod_ = {@Autowired})
private Restaurant restaurant;
@Test
public void testExist(){
assertNotNull(restaurant);
log.info(restaurant);
log.info("------------");
log.info(restaurant.getChef());
}
}
@RunWith(SpringJUnit4ClassRunner.class)
현재 테스트 코드가 스프링을 실행하는 역할을 하는 것을 표시
@ContextConfiguration
지정된 클래스나 문자열을 이용하여 필요한 객체를 스프링 내의 객체로 등록한다
@Log4j
Lombok을 이용하여 로그를 기록하는 Logger를 변수로 생성
@Test
테스트 대상 표시, 해당 메서드를 선택하고 JUnit Test 기능 실행
assertNotNull(restaurant);
대상 변수가 null이 아니어야만 테스트가 성공한다.
이를 실행하면 아래와 같은 결과가 나온다.
new
키워드를 사용하지 않았음에도 객체가 생성되었다.
→ 스프링은 객체를 생성하고 관리하는 일종의 컨테이너나 팩토리의 기능
Restaurant 클래스의 @Data 어노테이션으로 Lombok을 이용하여 여러 메서드가 생성된다
→ getter / setter 등 자동 생성
Restaurant 객체의 Chef 인스턴스 변수에 객체가 주입되어 있다.
→ @Autowired를 이용하여 객체간의 관계 자동 관리