개발자들은 의존성 주입을 사용할 때 서비스 인스턴스의 생명주기를 알고 있을 수 있지만, 많은 사람들이 그것이 어떻게 작동하는지 완전히 이해하지 못한다. 이러한 개념을 명확하게 하는 수많은 글을 온라인에서 찾을 수 있지만, 그들은 대개 이미 알고 있을 수도 있는 정의들을 반복하는 것일 뿐이다. 상세한 예를 들어 설명을 간단하게 해 보겠습니다.
의존성 주입을 구현할 때, 개발자들은 인스턴스의 생명주기를 결정하는 세 가지 옵션을 가지고 있다:
- 싱글톤
- 스코프드
- 트랜지엘
대부분의 개발자는 이러한 용어를 알고 있지만, 서비스의 수명에 어느 옵션을 선택해야 하는지에 대해 고민하는 사람들도 많다.
정의
설명을 시작하려면 이렇다:
- 싱글톤라이프타임 서비스 인스턴스는 서비스 컨테이너에서 응용 프로그램당 한 번만 생성된다. 단일 인스턴스가 모든 후속 요청을 처리한다. 싱글톤 서비스는 응용 프로그램의 끝에서 제거된다 (즉, 응용 프로그램 재시작시).
- 트랜지엘라이프타임 서비스 인스턴스는 서비스 컨테이너에서 요청당 생성된다. 트랜지엘 서비스는 요청의 끝에서 제거된다.
- 스코프드라이프타임 서비스 인스턴스는 클라이언트 요청당 한 번만 생성된다. 트랜지엘 서비스는 요청의 끝에서 제거된다.
何时使用
- 싱글톤 – 응용 프로그램의 전체 周期 동안 서비스의 단일 인스턴스를 사용하고자 하는 경우
- 트랜잭ional – 클라이언트 요청 내에서 서비스의 개별 인스턴스를 사용하고자 하는 경우
- 스코oped – 각 요청마다 서비스의 단일 인스턴스를 사용하고자 하는 경우
클라이언트 요청이 무엇인지? 간단하게 말하면, 사용자의 단추 클릭으로 응용 프로그램으로 API/REST 호출을 보내고 응답을 얻는 것입니다.
걱정 안 하고 예제로 이해해 봐요.
예제
먼저, 인터페이스 / 서비스와 클래스를 생성해 봐요:
// 아래 3개의 서비스를 선언하고자 합니다
Public interface ISingleton
Public interface ITransient
Public interface IScoped
이제 위에 생성한 각 서비스 인터페이스 / 서비스에 대한 実装을 쓰 let’s 이해를 도울 수 있도록 callMeSingleton
, callMeTransient
, callMeScoped
변수를 更新하도록 해봐요.
- 싱글톤 클래스 実装:
class SingletonImplementation: ISingleton
{
var callMeSingleton = ""
// 다른 実装
public SetSingleton(string value)
{
callMeSingleton = value;
}
// 다른 実装
}
- 트랜잭ional 클래스 実装:
class TransientImplementation: ITransient
{
var callMeTransient = ""
// 다른 実装
public SetTransient(string value)
{
callMeTransient = value;
}
// 다른 実装
}
- 스코oped 클래스 実装:
class ScopedImplementation: IScoped
{
var callMeScoped = ""
// 다른 실행
public SetScoped(string value)
{
callMeScoped = value;
}
// 다른 실행
}
let’s 각 서비스 인스턴스의 周期을 결정하기 위해 (ConfigureServices
) DI (依存성 주입)로 등록해봐요.
services.AddSingleton<ISingleton, SingletonImplementation>();
services.AddTransient<ITransient , TransientImplementation>();
services.AddScoped<IScoped , ScopedImplementation>();
ClassA
이나 ClassB
나 ClassC
중任何의 서비스를 이해하기 위해서는 각 서비스의 生命周期(생명周期)을 이해하는 것이 중요합니다.
ClassA
:
public class ClassA
{
private ISingleton _singleton;
생성자에서 다른 세 가지 서비스를 인스턴스화하는 것을 허용하는 것이 중요합니다.
public ClassA(ISingleton singleton,
ITransient _transient,
IScoped _scoped)
{
_singleton = singleton;
}
public void UpdateSingletonFromClassA()
{
_singleton.SetSingleton("I am from ClassA");
}
public void UpdateTransientFromClassA()
{
_transient.SetTransient("I am from ClassA");
}
public void UpdateScopedFromClassA()
{
_scoped.SetScoped("I am from ClassA");
}
기타 구현
}
ClassB
:
public class ClassB
{
private ISingleton _singleton;
생성자에서 다른 세 가지 서비스를 인스턴스화하는 것을 허용하는 것이 중요합니다.
public ClassB(ISingleton singleton,
ITransient _transient,
IScoped _scoped)
{
_singleton = singleton;
}
public void UpdateSingletonFromClassB()
{
_singleton.SetSingleton("I am from ClassB");
}
public void UpdateTransientFromClassB()
{
_transient.SetTransient("I am from ClassB");
}
public void UpdateScopedFromClassB()
{
_scoped.SetScoped("I am from ClassB");
}
기타 구현
}
ClassC
:
public class ClassC
{
private ISingleton _singleton;
생성자에서 다른 세 가지 서비스를 인스턴스화하는 것을 허용하는 것이 중요합니다.
public ClassC(ISingleton singleton,
ITransient _transient,
IScoped _scoped)
{
_singleton = singleton;
}
public void UpdateSingletonFromClassC()
{
_singleton.SetSingleton("I am from ClassC");
}
public void UpdateTransientFromClassC()
{
_transient.SetTransient("I am from ClassC");
}
public void UpdateScopedFromClassC()
{
_scoped.SetScoped("I am from ClassC");
}
기타 구현
}
분석
위에서 제시한 구현을 통해 각 生命周期의 결과와 행동을 analyze하겠습니다.
Singleton
모든 クラ스 (ClassA
, ClassB
, ClassC
)는 应用程序의 전체 生命周期 동안 SingletonImplementation
클래스의 동일한 인스턴스를 사용합니다. 这意味着 SingletonImplementation
클래스의 속성, 필드, 및 operands는 호출 클래스에서 사용되는 모든 인스턴스 사이에 共有됩니다. 속성 또는 필드의 갱신은 이전 변경에 의해 재정의되ます.
예를 들어, 이전 코드에서, ClassA
, ClassB
, ClassC
모두 SingletonImplementation
서비스를 단일 인스턴스로 사용하고 SetSingleton
를 호출하여 callMeSingleton
변수를 갱신합니다. 이 경우, 이 속성에 액세스하려는 모든 요청은 callMeSingleton
변수의 단일 값을 가지게 됩니다. 마지막으로 접근하여 갱신하는 클azz은 callMeSingleton
값을 盖정합니다.
ClassA
–TransientImplementation
서비스를 사용할 때 다른 클azz과 같은 인스턴스를 가지게 됩니다.ClassB
–TransientImplementation
서비스를 사용할 때 다른 클azz과 같은 인스턴스를 가지게 되ます.ClassC
–TransientImplementation
서비스를 사용할 때 다른 클azz과 같은 인스턴스를 가지게 됩니다.
ClassA
, ClassB
, ClassC
가 SingletonImplementation
클azz의 동일한 인스턴스를 갱신하면 callMeSingleton
값을 盖정합니다. 따라서 단일 인스턴스 서비스 구현에 속성을 설정하거나 갱신할 때 조심하십시오.
단일 인스턴스 서비스는 应用程序 末尾에 제거됩니다(즉, 应用程序 重启 시).
Transient
모든 클래스(ClassA
, ClassB
, ClassC
)는 TransientImplementation
클래스의 개별 인스턴스를 사용합니다. 这意味着 하나의 클래스가 TransientImplementation
클래스의 속성, 필드, 或者 操作을 호출하면, 그 인스턴스의 独占적인 값들만 갱신 或者 재정의합니다. TransientImplementation
의 다른 인스턴스들과 속성 或者 필드의 갱신은 공유되지 않습니다.
이해해봐요:
ClassA
–TransientImplementation
의 서비스의 自己的 인스턴스를 가지게 될 것입니다.ClassB
–TransientImplementation
의 서비스의 自己的 인스턴스를 가지게 될 것입니다.ClassC
–TransientImplementation
의 서비스의 自己的 인스턴스를 가지게 될 것입니다.
比如说 あなたが ClassD
를 가지고 있으며 ClassA
, ClassB
, ClassC
인스턴스에서 트랜지언트 서비스를 호출하고 있을 때, 각 클래스 인스턴스가 다른/별개의 인스턴스로 처리되며 각 클래스는 自己的 callMeTransient
의 값을 가질 것입니다. 下記의 인라인 코멘트를 ClassD
に대해 읽어보세요.
public ClassD
{
// 다른 구현
// 아래 各行의 코드는 클래스 A의 인스턴스에 대해 callMeTransient 의 값을 "I am from ClassA"로 갱신하며, Class B나 C에서 来的 任何时候의 호출이나 값이 변경되지 않음
// 아래 各行의 코드는 Class B의 인스턴스에 대해 callMeTransient 의 값을 "I am from ClassB"로 갱신하며, Class A의 인스턴스 값을 덮어쓰지 않고, Class C로 来的 任何时候의 호출이나 값이 변경되지 않음
ClassA.UpdateTransientFromClassA();
// 아래 各行의 코드는 Class C의 인스턴스에 대해 callMeTransient 의 값을 "I am from ClassC"로 갱신하며, Class A, B의 인스턴스 값을 덮어쓰지 않고, 다른 任何时候의 Class에서의 호출이나 값이 변경되지 않음
// Transient services are disposed at the end of each request. Use Transient when you want a state less behavior within the request.
ClassB.UpdateTransientFromClassB();
// Scoped
// All the classes (ClassA
, ClassB
, and ClassC
) will be using single instances of ScopedImplementation
class for each request. This means that calls for properties/fields/operations on ScopedImplementation
class will happen on single instance with in the scope of request. Any updates of properties/fields will be shared among other classes.
ClassC.UpdateTransientFromClassC();
// 다른 구현
}
Transient services are disposed at the end of each request. Use Transient when you want a state less behavior within the request.
Scoped
All the classes (ClassA
, ClassB
, and ClassC
) will be using single instances of ScopedImplementation
class for each request. This means that calls for properties/fields/operations on ScopedImplementation
class will happen on single instance with in the scope of request. Any updates of properties/fields will be shared among other classes.
Let’s understand:
ClassA
– It will have its instance of service ofTransientImplementation
.ClassB
–ClassA
와 같은 인스턴스의TransientImplementation
서비스를 가지고 있을 것입니다.ClassC
–ClassA
와ClassB
가 같은 인스턴스의TransientImplementation
서비스를 가지고 있을 것입니다.
let’s say あなたには ClassD
가 있습니다. 이 경우 ClassA
, ClassB
, ClassC
인스턴스로부터 범위 서비스를 호출하고 있습니다. 각 クラス은 ScopedImplementation
クラ스의 단일 인스턴스를 가질 것입니다. ClassD
아래의 인라인 コメン트를 읽으십시오.
public class ClassD
{
// 다른 구현
// 아래 代码は callMeScoped 의 값을 "I am from ClassA" 로 ClassA 인스턴스에 대해 更新します
// 그러나 이는 스코프 생명周期이기 때문에 单个 인스턴스 ScopedImplementation을 持っています
// 그러다가 ClassB 또는 ClassCから 다음 호출이 오면 그 값을 재정의할 수 있습니다
ClassA.UpdateScopedFromClassA();
// 아래 代码は single instance ScopedImplementation의 callMeScoped 값을 "I am from ClassB" 로 更新します
// 그리고 이는 ClassA 인스턴스의 callMeScoped 값도 재정의합니다.
ClassB.UpdateScopedFromClassB();
// 이제 Class A가 ScopedImplementation에 대해 어떤 操作을 수행하면
// 그들은 classB로 재정의된 最新의 속성/字段 값을 사용합니다.
// 아래 代码は callMeScoped 값을 "I am from ClassC" 로 更新합니다
// 그리고 이는 ClassA와 ClassB 인스턴스의 callMeScoped 값도 재정의합니다.
ClassC.UpdateScopedFromClassC();
// 이제 Class B나 Class A가 ScopedImplementation에 대해 어떤 操作을 수행하면
// 다른 구현
}
// 다른 구현
Scoped services are disposed at the end of each request. Use Scoped when you want a stateless behavior between individual requests.
Trivia Time
다음은 위 클래스와 같은 예제를 가져와 SingletonImplementation
(싱글톤으로 구성됨)에서 ITransient
와 IScoped
서비스를 초기화하는 것입니다. 이렇게 하면 이 서비스들의 생명주기를 싱글톤 생명주기로 변경하고 부모 서비스로 지정합니다. 이 경우 응용 프로그램에는 (예제에서 사용했던 3개의 서비스만 고려할 때)Transient 또는 Scoped 서비스가 없게 됩니다.
다음 코드의 줄을 읽어보십시오:
public class SingletonImplementation: ISingleton
{
// 서비스 초기화를 위한 생성자.
private readonly ITransient _transient
private readonly IScoped _scoped
SingletonImplementation(ITransient transient, IScoped scoped)
{
_transient = transient;
// 이제 _transient는 Transient로 등록되었든가 상관없이 싱글톤 서비스로 동작합니다.
_scoped = scoped;
// 이제 scoped는 Scoped로 등록되었든가 상관없이 싱글톤 서비스로 동작합니다.
}
var callMeSingleton = ""
// 기타 구현
}
요약
이 글이 주제를 이해하는 데 도움이 되었기를 바랍니다. 위의 문맥을 설정하고 직접 시도해 보시고, 그러면 다시 혼돈하실 일이 없을 것입니다. 싱글톤은 가장 쉽게 이해할 수 있습니다. 인스턴스를 한 번 만들면 응용 프로그램 전체의 응용 프로그램의 생명주기 동안 공유됩니다. 싱글톤과 유사하게, Scoped 인스턴스는 응용 프로그램 내의 요청 생명주기 동안 같은 동작을 보여줍니다. Transient는 완전히 상태 없는 것으로, 각 요청과 각 클래스 인스턴스는 서비스의 본인의 인스턴스를 보유합니다.
Source:
https://dzone.com/articles/understanding-the-dependency-injection-lifecycle