struct2 원본 해독(8)의container 원리
19877 단어 getinstanceinjectcontainerstruct2
컨테이너를 중국어로 번역하면 용기라는 뜻으로 통속적으로 struct2의 운영 환경이다.이른바 운행 환경이란 하나의 용기와 유사한데 그 안에 각종 대상이 담겨 있다. struct2가 aciton이 요청한 것을 처리하면 용기에서 해당하는 대상을 얻는다.다음은container의 실현 원리를 탐구해 보겠습니다.container는 인터페이스로 두 가지 방법이 있는데 하나는 inject()이고 하나는 getInstance()이다.getInstance () 는 컨테이너에서 객체를 꺼냅니다. inject () 는 주입에 의존합니다.struts가 시작될 때 설정 파일의 대상을container에 넣습니다.다음에 우리는 그것의 실현 원리를 소개한다.
public interface Container extends Serializable {
// name
String DEFAULT_NAME = "default";
//
void inject(Object o);
<T> T inject(Class<T> implementation);
//
<T> T getInstance(Class<T> type, String name);
<T> T getInstance(Class<T> type);
Set<String> getInstanceNames(Class<?> type);
void removeScopeStrategy();
}
1. 용기에 대상을 넣는다
앞에서 연구한 바와 같이 struct2는 모든 설정 정보를factory 인터페이스를 실현하는 실현 클래스에 봉인한다(구체적인 해석 과정은struct2 원본 코드 해석의 해석 bean을 참고하세요). 그리고factories를 매개 변수로 하나의container 대상을 실례화하고container Impl은 그 실현 클래스이다.
: container
public Container create(boolean loadSingletons) {
ensureNotCreated();
created = true;
//factories
final ContainerImpl container = new ContainerImpl(
new HashMap<Key<?>,InternalFactory<?>>(factories));
if (loadSingletons) {
//loadSingletons false,
}
container.injectStatics(staticInjections);
return container;
}
factories를 매개 변수로 하나의 컨테이너 대상을 실례화하면 컨테이너의 구조 함수에this가 있어야 한다.factories=factories, 아니나 다를까container의 구조 함수에서
:
ContainerImpl(Map<Key<?>,InternalFactory<?>> factories) {
// factories
this.factories = factories;
//...
this.factoryNamesByType = Collections.unmodifiableMap(map);
}
이렇게 struct2를 초기화하는 과정에서 모든 대상은factories 형식으로container 용기에 넣는다.
2. 용기에서 대상을 꺼낸다
앞에서 말한 바와 같이 용기라면 저장하고 놓는 것이 있다.위에서 용기에 대상을 넣는 것을 연구했고, 다음은 용기에서 대상을 어떻게 꺼내는지 연구했다.컨테이너 인터페이스의 방법 이름을 보면 컨테이너는 getInstant () 방법으로 용기의 대상을 꺼냅니다.
:getInstant()
// class name
<T> T getInstance(Class<T> type,String name);
// class
<T> TgetInstance(Class<T> type);
이 두 가지 방법은 모두 서로 다른 조건을 통해 용기 속의 대상을 꺼내는 것이다.컨테이너 구현 클래스의 이 방법을 보면
: class name
public <T> T getInstance(final Class<T> type, final String name) {
return callInContext(newContextualCallable<T>() {
// callable.call()
public T call(InternalContextcontext) {
return getInstance(type, name,context);
}
});
}
: name
public <T> T getInstance(final Class<T> type) {
return callInContext(newContextualCallable<T>() {
public T call(InternalContextcontext) {
return getInstance(type, context);
}
});
}
모두callInContext () 방법을 호출했습니다. 말 그대로,callInContext,callin (..에서 꺼내기) Context (상하문) 는 용기에서 꺼내는 것을 말합니다.ContextualCallable는 인터페이스로call방법을 제공했고 여기서도 인터페이스의 프로그래밍 사상을 체험했다. 우리가 이 인터페이스를 실현할 때 우리가 필요로 하는call방법을 지정한다.
:
<T> TcallInContext(ContextualCallable<T> callable) {
Object[] reference = localContext.get();
if (reference[0] == null) {
//this.container=container containerImpl
reference[0] = new InternalContext(this);
try {
// call ,
return callable.call((InternalContext)reference[0]);
} finally {
// Only remove the context if this call created it.
reference[0] = null;
}
} else {
// Someoneelse will clean up this context.
return callable.call((InternalContext)reference[0]);
}
}
이 방법에서 마지막으로callable을 호출합니다.call () 방법, 즉 getInstance (type,name,context) 방법 (callable 인터페이스를 실현할 때 지정), 이name를 설정하지 않으면 기본'default'를name 속성으로 사용합니다
<T> T getInstance(Class<T>type, InternalContext context) {
return getInstance(type, DEFAULT_NAME, context);
}
이 getInstance(type,name,context) 방법을 살펴보겠습니다. 이 context가 바로containerImpl입니다.
<T> T getInstance(Class<T> type, String name, InternalContext context) {
ExternalContext<?> previous = context.getExternalContext();
// key
Key<T> key = Key.newInstance(type, name);
context.setExternalContext(ExternalContext.newInstance(null, key, this));
try {
// key factory
InternalFactory o = getFactory(key);
if (o != null) {
//
return getFactory(key).create(context);
} else {
return null;
}
} finally {
context.setExternalContext(previous);
}
}
우리가 전에 말했듯이 설정 정보를factory 대상에 봉인한 후 키(type,name)를 키값으로 하고factory는value값으로 이factory 대상을factories라는 맵 집합에 저장합니다. 여기서Key 값을 통해 이factory 대상을 찾을 수 있고factory의create () 방법을 호출하면 해당 키(tyoe,name)의 대상을 실례화할 수 있습니다.그리하여 용기에서 이 대상을 꺼낼 수 있다.여기서 모르면 업무 모델의 실현 원리를 뒤돌아보자.
의존 주입
struct2는 inject () 방법으로 의존 주입을 실현하고spring은 getBean 방법으로 의존 주입을 실현한다. 이른바 의존 주입은 하나의 대상을 실례화할 때 이 대상의 속성 값을 모두 설정하는 것이다.
// object
public void inject(final Object o) {
callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) {
inject(o, context);
return null;
}
});
}
// class
public <T> T inject(final Class<T> implementation) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return inject(implementation, context);
}
});
}
이 방법은 getInstance와 유사하지만, 다른 것은callabel의call 방법이다.다시 불러온 inject () 방법을 사용합니다.
3.1.Object 의존 주입
object 의존 주입을 통해 최종적으로 inject (o, context) 방법을 호출합니다.
void inject(Object o, InternalContext context) {
// injector
List<Injector> injectors = this.injectors.get(o.getClass());
// inject ,
for (Injector injector : injectors) {
injector.inject(context, o);
}
}
이 injectors는 무슨 귀신입니까?정의를 한번 보도록 하겠습니다.
: injectors
final Map<Class<?>, List<Injector>> injectors =
new ReferenceCache<Class<?>, List<Injector>>() {
@Override
protected List<Injector> create(Class<?> key) {
List<Injector> injectors = new ArrayList<Injector>();
addInjectors(key, injectors);
return injectors;
}
};
이 injectors는 사실 하나의 맵입니다. Reference Cache 형식의 맵입니다. 이 Reference Cache는 struct2가 디자인한 캐시 메커니즘입니다. 다시 쓴 get () 방법을 살펴보겠습니다. 다시 쓴 get () 방법은 먼저 캐시 (map) 에 데이터를 가져옵니다. 데이터가 캐시되지 않으면 필요한 데이터를 캐시로 불러옵니다. 캐시가 데이터를 불러온 후에 필요한 데이터를 되돌려줍니다.
@SuppressWarnings("unchecked")
@Override public V get(final Object key) {
// map.get()
V value = super.get(key);
// , internalCreate ,
return (value == null)
? internalCreate((K) key)
: value;
}
이 인터넷 Create 방법은 필요한 데이터를 불러오는 것입니다.
V internalCreate(K key) {
try {
// FutureTask, Callable
FutureTask<V> futureTask = new FutureTask<V>(
new CallableCreate(key));
Object keyReference = referenceKey(key);
//
Future<V> future = futures.putIfAbsent(keyReference, futureTask);
if (future == null) {
try {
if (localFuture.get() != null) {
throw new IllegalStateException(
"Nested creations within the same cache are not allowed.");
}
localFuture.set(futureTask);
futureTask.run();
//
V value = futureTask.get();
putStrategy().execute(this,
keyReference, referenceValue(keyReference, value));
return value;
} finally {
localFuture.remove();
futures.remove(keyReference);
}
} else {
// wait for winning thread.
return future.get();
}
}
}
이 방법은FutureTask 클래스에 사용되며, 메인 라인은 자신의 임무를 완성한 후에 결과를 얻을 수 있으며, 이렇게 하면 모든 대상이container에 용기에 넣은 후에 의존 주입을 할 수 있다.FutureTask를 실행하는 경우get () 방법은 주어진callable의call () 방법을 호출합니다. (특히 주의: 하나의runnable로 전송되면runnable의run() 방법을 실행하고 원리 뒤에 단독 설명을 실현합니다.)여기 callable Create가 들어왔어요.
FutureTask<V> futureTask = new FutureTask<V>(
new CallableCreate(key));
이 callable Create의 call 방법을 보도록 하겠습니다.
public V call() {
//
V value = internalGet(key);
//
if (value != null) {
return value;
}
// , create
value = create(key);
if (value == null) {
//
}
return value;
}
}
콜 방법에서create 방법을 호출했습니다.이create() 방법은 abstract 형식으로 실현 클래스에서 이루어져야 한다. 이것은 우리가 이 클래스를 실현할 때로 돌아간다.
final Map<Class<?>, List<Injector>> injectors =
new ReferenceCache<Class<?>, List<Injector>>() {
//callable creat
@Override
protected List<Injector> create(Class<?> key) {
List<Injector> injectors = new ArrayList<Injector>();
addInjectors(key, injectors);
return injectors;
}
};
그래서 결국 Rrference Cache가 다시 쓰는 create () 방법을 실행했습니다.이 방법에서ddInjectors () 방법을 호출하여 키라는 종류의 @inject 주석을 분석합니다. (이 키는 Rrference Cache.get (o.getClass () 인데 왜class 형식을 사용합니까?클래스의 부모 클래스, 속성, 방법)을class 형식으로 얻을 수 있으며, 주석마다 하나의 프로젝트or 대상으로 봉하여 모든 프로젝트or를list 집합에 넣을 수 있습니다.
void addInjectors(Class clazz, List<Injector> injectors) {
if (clazz == Object.class) {
return;
}
// @inject ,
addInjectors(clazz.getSuperclass(), injectors);
// fields , o.getClass() ?
addInjectorsForFields(clazz.getDeclaredFields(), false, injectors);
// methods
addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors);
}
여기에 Fields 해석과 Methods 해석으로 나뉘는데 위에서 말한 모든 @ inject 주석을 해당하는 Inject 대상에 봉인한다. 사실 이 Inject는 하나의 인터페이스일 뿐이고 두 가지 실현 클래스가 있다. 하나는 Field Injector이고 다른 하나는 Method Injector이다.그래서 Fields 해석과 Methods 해석도 그에 상응하는 Field Injector 대상과 Method Injector 대상으로 봉인된다.
void addInjectorsForMethods(Method[] methods, boolean statics,
List<Injector> injectors) {
addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
new InjectorFactory<Method>() {
public Injector create(ContainerImpl container, Method method,
String name) throws MissingDependencyException {
// MethodInjector
return new MethodInjector(container, method, name);
}
});
}
void addInjectorsForFields(Field[] fields, boolean statics,
List<Injector> injectors) {
addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
new InjectorFactory<Field>() {
public Injector create(ContainerImpl container, Field field,
String name) throws MissingDependencyException {
// FieldInjector
return new FieldInjector(container, field, name);
}
});
}
공장 모드도 사용합니다. Field Injector 대상과 Method Injector 대상을 되돌려보내려면 Injectory Factory가 호출됩니다.create() 방법.이들의 공통된 방법addInjectorsForMembers()를 살펴보겠습니다.
<M extends Member & AnnotatedElement> void addInjectorsForMembers(
List<M> members, boolean statics, List<Injector> injectors,
InjectorFactory<M> injectorFactory) {
// , method,menbers methods; Field,menbers Fields
for (M member : members) {
//statics false, isStatic(member)==false, true,
if (isStatic(member) == statics) {
//
Inject inject = member.getAnnotation(Inject.class);
if (inject != null) {
try {
// create , , list
injectors.add(injectorFactory.create(this, member, inject.value()));
}
//
}
}
}
}
}
이렇게 하면 모든 주석 정보 (이름, 방법, 이름, 용기) 를 injector 대상에 봉인합니다.
우리 다시 컨테이너를 보자.inject () 메서드
void inject(Object o, InternalContext context) {
// Injector
List<Injector> injectors = this.injectors.get(o.getClass());
// inject()
for (Injector injector : injectors) {
injector.inject(context, o);
}
}
3.1.1. Method 주입
예를 들어, MethodInjector 객체의 inject() 방법
public void inject(InternalContext context, Object o) {
try {
method.invoke(o, getParameters(method, context, parameterInjectors));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
여기에 주해된 방법의 방법을 집행하였다.set 방법에 @inject 주석을 추가하면 inject (Object) 에서 object의 이 set () 방법을 호출합니다.구조 함수에 @ inject 주석을 추가하면 inject (Object) 에서 object의 구조 방법을 호출합니다.이 getParameters () 는 @ inject 주석을 추가한 param 의존 주입입니다. 추가하지 않으면 공백으로 되돌려줍니다.
private static Object[] getParameters(Member member, InternalContext context,
ParameterInjector[] parameterInjectors) {
// param @inject null
if (parameterInjectors == null) {
return null;
}
//
Object[] parameters = new Object[parameterInjectors.length];
for (int i = 0; i < parameters.length; i++) {
// injector inject param
parameters[i] = parameterInjectors[i].inject(member, context);
}
return parameters;
}
이 parameterInjectors는 MethodInjector 객체를 인스턴스화할 때 지정됩니다.
public MethodInjector(ContainerImpl container, Method method, String name)
throws MissingDependencyException {
this.method = method;
if (!method.isAccessible()) {
// ,
}
// param
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) {
//
}
// @inject param, ParameterInjector ( container ), parameterInjectors
parameterInjectors = container.getParametersInjectors(
method, method.getParameterAnnotations(), parameterTypes, name);
}
ParameterInjector 대상의 inject() 방법은 공장 모드의create 방법을 호출하여 해당하는 실례를 만드는 것입니다.
T inject(Member member, InternalContext context) {
ExternalContext<?> previous = context.getExternalContext();
context.setExternalContext(externalContext);
try {
// params
return factory.create(context);
} finally {
context.setExternalContext(previous);
}
}
3.1.2.Field 주입
field 주입은 비교적 간단하다. 바로field의 set 방법을 직접 호출하는 것이다
public void inject(InternalContext context, Object o) {
ExternalContext<?> previous = context.getExternalContext();
context.setExternalContext(externalContext);
try {
//set()
field.set(o, factory.create(context));
} catch (IllegalAccessException e) {
//
} finally {
context.setExternalContext(previous);
}
}
매개 변수는factory의create() 방법으로 직접 대상의 실례를 되돌려줍니다. 이factory도FieldInjector 실례화할 때 지정합니다
public FieldInjector(ContainerImpl container, Field field, String name)
throws MissingDependencyException {
this.field = field;
if (!field.isAccessible()) {
//
}
Key<?> key = Key.newInstance(field.getType(), name);
// factory
factory = container.getFactory(key);
if (factory == null) {
//
}
this.externalContext = ExternalContext.newInstance(field, key, container);
}
3.2.class 의존 주입
Object의class 클래스를 매개 변수로 주입하면,container입니다.inject(class), inject(class,container) 방법을 호출합니다
<T> T inject(Class<T> implementation, InternalContext context) {
try {
// @inject
ConstructorInjector<T> constructor = getConstructor(implementation);
//
return implementation.cast(
constructor.construct(context, implementation));
} //
}
클라스 한번 봅시다.cast () 방법, 이 방법은 cast 매개 변수의 종류를class 형식으로 강제로 바꾸는 것입니다
public T cast(Object obj) {
//
// class
return (T) obj;
}
캐스트의 매개 변수는 constructor입니다.construct(container, class).이 constructor는 Construct Injector 대상이고, 위의 get Constructor도 Reference Cache를 호출합니다.get () 방법은 @ inject의 주석을 ConstructInjector 대상에 봉인합니다
//constructors
Map<Class<?>, ConstructorInjector> constructors =
new ReferenceCache<Class<?>, ConstructorInjector>() {
@Override
@SuppressWarnings("unchecked")
protected ConstructorInjector<?> create(Class<?> implementation) {
return new ConstructorInjector(ContainerImpl.this, implementation);
}
};
//.ReferenceCache.get()
@SuppressWarnings("unchecked")
<T> ConstructorInjector<T> getConstructor(Class<T> implementation) {
return constructors.get(implementation);
}
특히 주의하세요, Reference Cache.get () 방법은 최종적으로creat () 방법을 호출합니다. 이전에 inject (object) 는addInjectors 방법을 호출했습니다. 여기의ceate 방법은 new의ConstructInjector 대상에 직접 적용됩니다.
ConstructorInjector(ContainerImpl container, Class<T> implementation) {
this.implementation = implementation;
// class @inject , ,
constructor = findConstructorIn(implementation);
if (!constructor.isAccessible()) {
// ,
}
MissingDependencyException exception = null;
Inject inject = null;
ParameterInjector<?>[] parameters = null;
try {
inject = constructor.getAnnotation(Inject.class);
//
parameters = constructParameterInjector(inject, container, constructor);
}
parameterInjectors = parameters;
if ( exception != null) {
//
}
// injectors.get() , @inject injector
injectors = container.injectors.get(implementation);
}
이 ConstructInjector 대상을 얻고, 마지막으로 ConstructInjector를 호출합니다.construct가 대상을 되돌려주고 그 위에 있는class를 사용합니다.cast ()가class
Object construct(InternalContext context, Class<? super T> expectedType) {
ConstructionContext<T> constructionContext =
context.getConstructionContext(this);
if (constructionContext.isConstructing()) {
return constructionContext.createProxy(expectedType);
}
T t = constructionContext.getCurrentReference();
if (t != null) {
return t;
}
try {
// First time through...
constructionContext.startConstruction();
try {
Object[] parameters =
getParameters(constructor, context, parameterInjectors);
//params
t = constructor.newInstance(parameters);
constructionContext.setProxyDelegates(t);
} finally {
constructionContext.finishConstruction();
}
constructionContext.setCurrentReference(t);
// injector inject
for (Injector injector : injectors) {
injector.inject(context, t);
}
return t;
}
//
}
}
여기서 알 수 있듯이 하나의object 형식으로 매개 변수를 의존 주입하는 것과 하나의class 형식으로 매개 변수를 의존 주입하는 것은 class 형식이 먼저 실례화된object 형식으로 하고 하나의construct 대상이 많아야 한다는 데 차이가 있다.
4. 총결산
위에서 분석한 바와 같이 우리는 용기의 작업 원리를 알았다. 프로필을 분석할 때 모든 프로필 대상을factory 인터페이스의 실현 클래스에 봉하고 키(class,name)를 키 값으로 하고factory를value 값으로 맵에 저장하며 용기에서 대상을 꺼낼 때 getInstance 방법을 통해 키 값을 조건으로 이factory를 꺼낸다.그리고factory의create 방법을 사용해서 이 대상을 실례화합니다.또 다른 작업은 주입에 의존하는 것입니다. inject를 원할 때 캐시에서 대상을 찾습니다. 만약 그렇지 않으면 모든 @ inject 속성의 속성이나 방법을 반사하여 injector 대상에 봉하여 이 대상의 inject () 방법을 반복합니다.속성이면 set 방법을 사용하고 method이면 invoke 방법을 사용하여factory를 통과합니다.create는 대상을 실례화하고 설정합니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
nestjs-custom-injector의 주 버전: 공급자가 설정되지 않은 경우 예외, 기본값 등에서 약속 사용...- 라이브러리의 소스 코드 - nestjs-custom-injector를 사용한 데모 애플리케이션. - nest cli로 생성된 예. getLastComponentByName 및 getLastComponentByCl...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.