spring master은 스프링의 전반적인 사용에대해서 소개하는 책인데, 이해가되지 않은 부분이 많아서 블로그 여러글을 참조하면서 chap2내용을 정리 해보았다
JAVA- 클래스는 다른 클래스에 의존적, 클래스가 의존성의 인스턴스를 직접 생성하면 클래스 가내에 긴밀한 결합이 이뤄짐
스프링 - 객체 생성 및 연결 책임이 IOC컨테이너라는 새로운 구성 요소로 인계
클래스는 의존성을 정의, IOC 컨테이너는 객체를 만들고 의존성을 연결, 의존성 생성 및 와이어링 제어가 컨테이너에 의해 수행되는 혁신적인 개념은 ioc또는 의존성 주입으로 유명하다
쉬운 유지 관리성, 결합력 감소 및 테스트 가능성 개선, 스프링의 의존성 주입 옵션
- 자바 콘텍스트와 CDI에 대한 표준 의존성 주입 스펙과 스프링이 이를 어떻게 지원하는지 살펴보자
의존성 주입이란
의존성 주입을 올바르게 사용하면 애플리케이션을 테스트?
스프링은 어노테이션을 사용해 의존성 주입을 어떻게 구현?
구성 요소 검사란?
자바와 XML 애플레케이션 콘텍스트의 차이점은?
스프링 콘텍스트에 대한 단위 테스트는?
모킹은 단위 테스트를 어떻게 쉽게 처리
다른 빈 스코프는 무엇?
CDI란 무엇 , 스프링은 CDI를 어떻게 지원
의존성 주입 이해
1.데이터 서비스와 통신하는 비즈니스 서비스의 간단한 예
2.데이터 서비스를 생성하는 책임을 비즈니스 서비스 밖으로 이동시켜 코드를 느슨하게
3.스프링 IOC 컨테이너를 가져와 빈을 인스턴스화 하고 서로 연결
4.스프링이 제공하는 XML 및 자바 구성 옵션을 살펴보가
5. 스프링 단위 테스트 옵션을 살펴보라
6.모킹을 사용해 실제 단위 테스트를 작성하라
DataServiceImpl dataService = new DataServiceImpl
package com.mastering.spring.business;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mastering.spring.beans.Data;
import com.mastering.spring.beans.User;
import com.mastering.spring.data.DataService;
@Service
public class BusinessServiceImpl implements BusinessService {
@Autowired
private DataService dataService;
public long calculateSum(User user) {
long sum = 0;
for (Data data : dataService.retrieveData(user)) {
sum += data.getValue();
}
return sum;
}
public void setDataService(DataService dataService) {
this.dataService = dataService;
}
}
BusinessSerivcsImppl과 DataServiceImpl 간의 긴밀한 결합을 어떻게 줄일 수 있을까?
public interface DataService {
List<Data> retireveData(User user);
}
인터페이스를 사용하려면 BusinessServiceImpl의 코드를 업데이트 해야한다
DataService dataService = new DataServiceImpl();
인터페이스를 사용하면 느슨하게 결합된 코드를 만들 수 있다. 우리는 인터페이스를 구현하는 모든 와이러링을 잘 정의된 의존성으로 교체할 수 있다.
DataSrvice 인터페이스 사용 BusinessServiceImpl은 여전히 ServiceImpl의 인스턴스 생성과 밀접하게 결합돼 있다.
BusinessServiceImpl이 자체적으로 DataServiceImpl 인스턴스를 생성하지 않는 이유는?인스턴스를 BusinessServiceImpl에 제공할 수 있을까?
import com.mastering.spring.beans.User;
import com.mastering.spring.data.DataService;
public class BusinessServiceimpl {
private DataService dataService;
public long calculateSum(User user) {
long sum = 0;
for (Data data : dataService.retrieveData(user)) {
sum += data.getValuse();
}
return sum;
}
public vooid setDataService(DataService dataService) {
this.dataService =dataService;
}
}
누가 DataServiceImpl 클래스의 인스턴스를 생성 , BusinessServiceImpl 클래스에 연결?
=> 스프링 IOC 컨테이너 가 필요한 이유다
*spring bean 이란
Spring에 의하여 관리당하는 자바 객체를 사용합니다. 이렇게 Spring에 의하여 생성되고 관리되는 자바 객체를 Bean이라고 합니다. Spring Framework 에서는 Spring Bean 을 얻기 위하여 ApplicationContext.getBean() 와 같은 메소드를 사용하여 Spring 에서 직접 자바 객체를 얻어서 사용합니다.
1스프링 IOC 컨테이너는 어떤 빈을 생성해야 하는지 어떻게 알 수 있을까?
ex) 스프링 IOC 컨테이너가 BusinessServiceImpl 클래스와 DataServiceImpl클래스를 위한 빈을 생성하는 방법은?
2스프링 IOC 컨테이너는 빈을 서로 묶는 방법을 어떻게 알고 있을까 특히 IOC 컨테이너는 DataServiceImpl 클래스의 인스턴스를 BusinessServiceImpl 클래스에 주입하는 방법을 알고 있을까?
3스프링 IOC컨테이너는 빈을 검색하는 위치를 어떻게 알 수 있을까? 클래스 패스에 있는 모든 패키지를 검색하는 것은 비효율적이다
*Java에서는 @Override, @Deprecated 와 같은 기본적인 Annotation을 제공합니다. 아래의 상속 예제에서는 @Override 를 이용하여 상속임을 명시해줍니다.
public class Parent {
public void doSomething() {
System.out.println("This is Parent");
}
}
public class Son extends Parent{
@Override
public void doSomething() {
System.out.println("This is Son");
}
}
Spring에서는 여러 가지 Annotation을 사용하지만, Bean을 등록하기 위해서는 @Component Annotation을 사용합니다. @Component Annotation이 등록되어 있는 경우에는 Spring이 Annotation을 확인하고 자체적으로 Bean 으로 등록합니다.
실제로 사용되는 예시를 볼까요? 실제 Spring 프로젝트에서 Controller를 등록할 때에는 아래와 같은 Annotation을 사용합니다. 아래의 예시에서 Controller 임을 Spring 에게 알려주기 위하여 @Controller Annotation을 사용했습니다.
*출처 : https://melonicedlatte.com/2021/07/11/232800.html
@Service
public class BusinessServiceImpl implements BusinessService {
@Autowired
private DataService dataService;
-> public class BusinessServiceImpl{
Autowired
private DataService dataSpervice;
DataServiceImpl 클래스의 빈은 BusinessServiceImpl 클래스의 빈에 주입한다
빈 와이러링을 정의 이를 테스트하기 위해 DataService를 구현
@Repository
public class DataServiceImpl implements DataService {
public List<Data> retireveData(User user) {
return Arrays.asList(new Data(10), new Data(20));
}
}
https://chanhuiseok.github.io/posts/spring-4/
의존성
- 의존성은 쉽게 말해 어떤 객체가 사용해야 할 객체라고 할 수 있고, 이것을 직접 new 등을 써서 만들어 쓰면 의존성을 자기가 직접 만들어 쓴다고 할 수 있습니다
public class OwnerController {
private OwnerRepository ownerRepository = new OwnerRepository();
}
// OwnerController가, 필요한 OwnerRepository의 객체를 직접 생성하는 경우
- Inversion of Control - 제어권 역전
- 말 자체는 어렵게 들릴 수 있지만, 위에서처럼 직접적으로 의존성을 만들지 않고, 외부에서 의존성을 가져오는 경우를 말합니다.
- 즉, 밖에서 나에게 의존성을 주입해주는 것을 DI(Dependency Injection) 라고 합니다. 따라서 DI는 IoC의 일종이라고 생각하시면 됩니다.
public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
this.owners = clinicService;
this.visits = visits;
}
// OwnerController의 생성자에서 OwnerRepository를 인자로 받고, owners에 담고 있다.
// 이는 앞의 예시처럼 객체를 직접 생성하지 않고, 외부의 객체를 받고 있는 것이다.
에서의 의존성 주입은 반드시 Bean으로 등록된 객체들 끼리만 가능합니다. 스프링 IoC 컨테이너는 Bean으로 등록되지 않은 객체에는 의존성 주입을 해 주지 않습니다.
- IntelliJ 에서는 Bean 인 경우 아래 사진처럼 왼쪽에 아이콘으로 표시해 줍니다.
- 위에서 예시를 들었듯, OwnerController 생성자에서 의존성 주입이 일어나고 있습니다.
- 그러면 OwnerController, OwnerRepository 모두 Bean이어야 할 것입니다.
애플리케이션 콘텍스트에 대한 자바 구성요소 검사
@Configuration
class SpringContext {
}
@Configuration 어노테이션
스프링IOC 컨테이너는 빈을 검색하는 위치를 어떻게 알 수 있을까
@Configuration
@ComponentScan(basePackages = {"com.mastering.spring"})
class SpringContext{
}
ackage com.mastering.spring.context;
import static org.junit.Assert.assertEquals;
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 com.mastering.spring.beans.User;
import com.mastering.spring.business.BusinessService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/BusinessApplicationContext.xml" })
public class BusinessServiceJavaContextTest {
private static final User DUMMY_USER = new User("dummy");
@Autowired
private BusinessService service;
@Test
public void testCalculateSum() {
long sum = service.calculateSum(DUMMY_USER);
assertEquals(30, sum);
}
의존성 주입 타입
setter
생성자 주입
setter주입
https://kim-oriental.tistory.com/32
1. DI(의존성 주입)이란?
Spring Dependency Injection이란, 각 객체 간 의존관계를 스프링 컨테이너가 개발자가 정의한 Bean 등록 정보를 바탕으로 자동으로 주입해주는 기능입니다.
일반적인 다양한 기존 스프링 프로젝트를 보면, Controller에서 Service나 Repository 객체를 사용 시, new 키워드를 통해 컨트롤러에서 객체를 직접 생성하여 사용하지 않고 의존성 주입을 통해 스프링 컨테이너에 생성된 객체를 받아 사용하고 있는 것을 볼 수 있습니다. @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스들은 스프링 실행 시 스캔을 통해 개발자가 정의한 의존성 정보를 자동으로 bean 설정 정보에 등록을 하게 되어 의존성 주입이 동작하게 합니다.
이를 통해 객체간 결합도를 낮추고, 코드의 양을 줄여주고, 테스트를 용이하게 하여, 개발 및 유지보수를 더 쉽게 하게 하는 장점이 있습니다.
2. DI(의존성 주입) 종류
스프링에서 의존성을 주입하는 방법은 아래와 같이 3가지 방법이 있습니다.
@Controller
public class TestController {
@Autowired
TestService testService;
}
가장 코드가 단순하고 저같은 경우 기존에 많이 봐왔고 자주 사용했던 방식인데요, 아래와 같은 단점이 있어 추천되지 않는 방식이라고 합니다.
- 프레임워크 의존적 : 스프링 DI 컨테이너에서만 동작, 외부에서 수정 불가, 테스트의 어려움
- final 선언 불가 : 객체 변경 가능
- 수정자 주입 (Setter Injection)
@Controller
public class TestController {
private TestService testService;
@Autowired
public void setTestService(TestService testService) {
this.testService = testService;
}
}
수정자 주입 방식은 스프링 3.x대 버전에서 추천되었던 방식으로, 현재는 주입받는 객체가 변경될 가능성이 있을 경우에만 사용되는 방식이라고 합니다.
- 생성자 주입 (Constructor Injection)
@Controller
public class TestController {
private final TestService testService;
@Autowired // 생성자가 1개만 있을 경우 생략 가능
public TestController(TestService testService) {
this.testService = testService;
}
}
현재 스프링 프레임워크에서 가장 권장되는 방식입니다. 간략히 아래와 같은 장점이 있다고 합니다.
- 테스트 용이 : 프로엠워크 의존적이지 않아 순수 자바 등 외부 테스트 코드 작성 가능
- 객체 불변성 확보: final 선언 가능, 유지보수 용이성
- 순환 참조 에러가 발생할 경우 컴파일 시 판단 가능
생성자 주입 방식도 Lombok의 @RequiredArgsConstructor를 이용하면 코드를 아래와 같이 간결하게 작성할 수 있습니다.
@Controller
@RequiredArgsConstructor
public class TestController {
private final TestService testService;
}
스프링 빈 스코프
https://velog.io/@probsno/Bean-%EC%8A%A4%EC%BD%94%ED%94%84%EB%9E%80
1. Bean 스코프
빈 스코프는 무엇인가?
말그대로 빈이 사용되어지는 범위(?)인데, 빈이 앱이 구동되는 동안 한개만 만들어서 쓸 것인지 HTTP요청마다 생성해서 쓸 것인지 등등를 결정하는 것이 스코프임.
보통의 스프링 빈은 스프링 앱이 구동 될때 한번에 ApplicationContext에서 한 번에 모두 생성해서 하나의 클래스는 한 개의 빈만 가지지만 (Singleton), 때에 따라서는 HTTP요청마다 (Request) 다른 빈을 생성해서 쓸건지, 매번 사용될 때 마다 (Prototype) 빈을 생성해서 쓸건지 설정해서 쓸 수도 있다.
spring master
싱글톤 - 스프링 싱글톤 스코프가 하나의 스프링 컨테이너당 하나의 객체라는 것을 지정하는 것이 중요하다. 단일 JVM에 스프링 컨테이너가 여ㅓ 개 있는 경우, 동일한 빈의 인스턴스가 여러 개 있을 수 도 있다, 스프링 싱글톤의 스코프는 일반적인 정의와 다르다
프로토타임- 스프링 컨테이너에서 빈이 요청될 때마다 새로운 인스턴스가 생성, 빈에 상태가 포함되어있는 경우 프로토타입 스코프를 사용하는 것이 좋다
리퀘스트- 스프링 웹 콘텍스트에ㅓ만 사용할 수 있다. 모든 HTTP 요청 마다 빈의 새 인스턴스가 작성된다. 빈응ㄴ 요청 처리가 완료되는 즉시 폐기 되낟.
세션- 스프링 웹 콘텍스트에서만 사용할 수 있다. 모든 HTTP(Session)마다 빈의 새로운 인스턴스가 생성된다. 웹 애플리케이션의 사용자 권한과 가팅 단일 사용자 고유의 데이터에 적합한다.
애플리케이션 - 스프링 웹 콘텍스트에서만 사용할 수 있다. 웹 애플리케이션당 하나의 빈 인스턴스로 특정 환경의 애플리케이션 구성에 적합하다.
@Autowired 어노테이션
일치하는 항목이 1개있다
일치하는 항목이 2개이상 발견됐다:오토와이어링 실패
일치하는 항목이 없다:오토와이어링 실패
-후보 중 하나만 사용하려면 @primary 어노테이션을 사용하라
-오토와이어링을 더욱 강화하려면 @Qualifier를 사용해라
CDI의 예
ㅇ다른 클래스의 어노테이션이 어떻게 생겼는지 나타낸다
TEST ---
'취준 note 2023 > spring' 카테고리의 다른 글
스프링 di (0) | 2023.08.12 |
---|---|
스프링 시큐리티 (0) | 2023.08.11 |
코드없이 보는 스프링 부트 -페이징 정렬 처리하기 (0) | 2023.01.21 |
컨트롤러와 RESTAPI (0) | 2023.01.21 |
SOLID 단일 책임 원칙 (0) | 2022.11.22 |