struts2 원본 분석 - IOC 용기의 실현 메커니즘 (다음 편)
말하기 전에 몇 가지 결론은 반드시 알아야 한다.
1. 용기 위탁 관리 대상은 무엇입니까?
struts2/XWork의 설정 요소 노드에 따라 두 종류로 나눌 수 있는데 하나는
2. Container의 용기 위탁 관리 대상은 용기에 직접 저장된 것이 아니다. 실제로 용기에 저장된 것은 대상의 공장이다. 대상 공장이 있으면 언제든지 운행 기간에 대상을 얻을 수 있는 실례가 있다. 저장 공장은 대상의 발생을 통제할 수 있다. 예를 들어 새로운 대상을 되돌려야 하는지, 아니면 하나의 단일 대상을 되돌려야 하는지, 또는 하나의 라인이 안전한 대상을 되돌려야 하는지 등이다.
의존 주입
의존 주입은 바로 Container의 inject 방법을 호출하는 것입니다. 우선 이 방법 설명을 보십시오.
/**
* Injects dependencies into the fields and methods of an existing object.
*/
void inject(Object o);
/**
* Creates and injects a new instance of type {@code implementation}.
*/
<T> T inject(Class<T> implementation);
방법은 두 가지가 있는데 첫 번째는 Object에 전송되어 이 대상을 주입하는 것이다. 여기에 있는 대상은 임의의 대상이 될 수 있으며 반드시 용기 위탁 관리 대상이 되는 것은 아니다.두 번째 방법은 Class 대상을 전송하고 주입을 완성하고 주입된 대상의 실례를 되돌려줍니다.
다음은 @Injector 메모의 소스입니다.
4
/**
* <p>Annotates members and parameters which should have their value[s]
* injected.
*
* @author [email protected] (Bob Lee)
*/
@Target({METHOD, CONSTRUCTOR, FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface Inject {
/**
* Dependency name. Defaults to {@link Container#DEFAULT_NAME}.
*/
String value() default DEFAULT_NAME;
/**
* Whether or not injection is required. Applicable only to methods and
* fields (not constructors or parameters).
*/
boolean required() default true;
}
성명에서 알 수 있듯이 이 주석은 방법, 구조기, 필드, 파라미터 목록에 표시될 수 있으며 주입할 때 사용할 수 있는 다양한 종류의 주입기에 대응한다.그 value 속성은 InternalFactory (용기 위탁 관리 대상의 공장) 의name 속성 값을 찾습니다. 기본값은default입니다.Container는 하나의 인터페이스이고 실제 클래스는 Container Impl이다. 우리가 주입을 진행할 때 실제적으로 호출하는 것은 Contain Impl 클래스의 inject 방법이다.
이것은 ContainerImpl에서void inject(Object o)의 구현입니다.
public void inject(final Object o) {
callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) {
inject(o, context);
return null;
}
});
}
이 실현 방법에서 호출된 방법은callInContext 방법이다. 이것은 모형 방법이다. 뒤에서 볼 수 있듯이 용기에서 대상을 얻는 방법인 getInstance 내부에서 호출된 것도 이 방법이다. 그 목적은 모든 인터페이스 조작을 규범화하고 정의하여 통일된 조작의 입구로 삼고 이를 라인이 안전한 상하문 실행 환경에 포함시키는 것이다.구체적인 실행 논리는ContextualCallable 인터페이스의 리셋 실현 안에 봉인되어 있다.서로 다른 인터페이스의 실현에 대해 그들은 서로 다른 논리를 가지고 있으며 이러한 논리는ContextualCallable 인터페이스의 실현 클래스로 이루어진다. 그래야 inject 방법과 getInstance 방법이 내부에서 호출되는 것이 같은callInContext 방법일 수 있다.이것이 바로 템플릿 방법의 사용이다. 템플릿 방법은 프로그램의 집행 절차만 규정하고 프로그램 집행의 구체적인 논리는 각 호출자가 스스로 지정할 수 있다.여기는public Void call(Internal Context context)이 바로 그 리셋 방법입니다.
이것은 callInContext 메서드의 소스입니다.
<T> T callInContext(ContextualCallable<T> callable) {
Object[] reference = localContext.get();
if (reference[0] == null) {
reference[0] = new InternalContext(this);
try {
return callable.call((InternalContext)reference[0]);
} finally {
// Only remove the context if this call created it.
reference[0] = null;
}
} else {
// Someone else will clean up this context.
return callable.call((InternalContext)reference[0]);
}
}
이 방법에서는 앞에서 등록한 리셋 방법인call을 호출하고 나머지 작업은 올바른 실행 상하문을 가져오는 것입니다.콜백 방법 콜에서 void inject(Object o, Internal Context context) 방법이 호출되었습니다. 이 방법의 내부를 살펴보겠습니다.
void inject(Object o, InternalContext context) {
List<Injector> injectors = this.injectors.get(o.getClass());
for (Injector injector : injectors) {
injector.inject(context, o);
}
}
이 방법의 논리는 매우 간단하다. 우선 이 대상에 필요한 주입기 (Injector) 를 가져오고, 첫 번째 주입기를 반복해서 주입기의 inject 방법을 호출하여 주입을 완성한다.
여기서 말하고자 하는 것은 이 List
/**
* Field and method 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;
}
};
사실 이 대상은 맵이고 캐시 기능만 추가되었습니다. 키는 1Class 대상이고 주입된 대상의 Class 대상입니다. 그value는 이 대상에 필요한 주입기를 주입합니다. 처음에 특정한 대상에 주입할 때 획득한 주입기는null입니다. 이때 이 대상에 필요한 주입기를 찾습니다.
입력기를 가져오는 방법은 Reference Cache의 get 방법입니다. 이것은 get 방법의 원본 코드입니다.
/**
* {@inheritDoc}
*
* If this map does not contain an entry for the given key and {@link
* #create(Object)} has been overridden, this method will create a new
* value, put it in the map, and return it.
*
* @throws NullPointerException if {@link #create(Object)} returns null.
* @throws java.util.concurrent.CancellationException if the creation is
* cancelled. See {@link #cancel()}.
*/
@SuppressWarnings("unchecked")
@Override public V get(final Object key) {
V value = super.get(key);
return (value == null)
? internalCreate((K) key)
: value;
}
주석에서 알 수 있듯이 지정한 키의 값을 찾지 못하면 맵에 새 값을 만들어서 되돌려줍니다. 이 새 값은 사실 주입기 목록입니다.
Internal Create 메서드의 두 가지 소스입니다.
FutureTask<V> futureTask = new FutureTask<V>(
new CallableCreate(key));
// ...
futureTask.run();
여기에 새 Callable Create 대상이future Task 대상에 봉인되었고, 다음에future Task가 호출되었습니다.run();런 방법의 내용에 대한 호출은 Sync 대상의 innerRun () 방법이고, unnerRun 방법에는Callable Create 대상의call 방법이 호출되며, 이call 방법에는 injectors 성명에 등록된create (키) 리셋 방법이 호출됩니다.
자, 이제 이create(key) 방법에 집중할 수 있습니다. 이에는ddInjectors(key, injectors)가 호출되었습니다.메서드, 메서드 소스:
/**
* Recursively adds injectors for fields and methods from the given class to
* the given list. Injects parent classes before sub classes.
*/
void addInjectors(Class clazz, List<Injector> injectors) {
if (clazz == Object.class) {
return;
}
// Add injectors for superclass first.
addInjectors(clazz.getSuperclass(), injectors);
// TODO (crazybob): Filter out overridden members.
addInjectorsForFields(clazz.getDeclaredFields(), false, injectors);
addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors);
}
이 방법은 귀속 방법으로 먼저 부류 주입기를 찾아 부류에 주입을 실시하고 그 다음은 자신이다.여기addInjectorsForFields와addInjectorsForMethods가 호출하는 방법은addInjectorsForMembers입니다. 다음은
addInjectorsForMembers 소스:
<M extends Member & AnnotatedElement> void addInjectorsForMembers(
List<M> members, boolean statics, List<Injector> injectors,
InjectorFactory<M> injectorFactory) {
for (M member : members) {
if (isStatic(member) == statics) {
Inject inject = member.getAnnotation(Inject.class);
if (inject != null) {
try {
injectors.add(injectorFactory.create(this, member, inject.value()));
} catch (MissingDependencyException e) {
if (inject.required()) {
throw new DependencyException(e);
}
}
}
}
}
}
이 방법은 InjectorFactory의create 방법을 호출하여 하나의 Injector를 되돌려주고 주입기 목록에 넣을 뿐입니다.create 방법은 또 하나의 리셋 방법입니다.FieldInjector를 예로 들면 이것은addInjectorsForFields 방법의 원본입니다.
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 {
return new FieldInjector(container, field, name);
}
});
}
create 방법에서 용기 대상을 전송합니다. Field,name은 필드 주입기를 되돌려줍니다. 방법 주입기의 원리도 이와 같습니다.
FieldInjector 구조 방법에서 필드 유형과name 파라미터에 따라 키로 용기에서 용기 위탁 관리 대상을 찾는 인터넷 팩토리를 묻는다. 다음은FieldInjector 클래스 inject 방법의 실현이다.
public void inject(InternalContext context, Object o) {
ExternalContext<?> previous = context.getExternalContext();
context.setExternalContext(externalContext);
try {
field.set(o, factory.create(context));
} catch (IllegalAccessException e) {
throw new AssertionError(e);
} finally {
context.setExternalContext(previous);
}
}
최종 호출은 반사 기술을 통해field를 호출하는 것을 보실 수 있습니다.set () 메서드, 설정할 값은 인터넷 팩토리의create 메서드가 반환하는 값입니다.여기까지 struts2의 주입 원리에 대해 어느 정도 알고 계시겠죠?
2. 용기 위탁 관리 대상 획득
Container 인터페이스의 선언입니다.
/**
* Gets an instance of the given dependency which was declared in
* {@link com.opensymphony.xwork2.inject.ContainerBuilder}.
*/
<T> T getInstance(Class<T> type, String name);
/**
* Convenience method. Equivalent to {@code getInstance(type,
* DEFAULT_NAME)}.
*/
<T> T getInstance(Class<T> type);
사실 이 두 방법의 실질은 같고 두 번째는 간단한 방법으로 getInstance(type,DEFAULT NAME)에 해당한다.type과name을 결합 키로 찾습니다.
ContainerImpl의 구체적인 구현을 살펴보겠습니다.
public <T> T getInstance(final Class<T> type, final String name) {
return callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) {
return getInstance(type, name, context);
}
});
}
여기에서 호출하는 방법은 위에서 말한 바와 같이 모두callInContext라는 모델 방법입니다. 그 진정한 논리적 실현은 getInstance(type,name,context) 방법입니다. 그 원본은 다음과 같습니다.
@SuppressWarnings("unchecked")
<T> T getInstance(Class<T> type, String name, InternalContext context) {
ExternalContext<?> previous = context.getExternalContext();
Key<T> key = Key.newInstance(type, name);
context.setExternalContext(ExternalContext.newInstance(null, key, this));
try {
InternalFactory o = getFactory(key);
if (o != null) {
return getFactory(key).create(context);
} else {
return null;
}
} finally {
context.setExternalContext(previous);
}
}
이 방법의 코드도 매우 간단하다. 먼저 type,name에 따라 키 대상을 구성한다. 사실 키는 type과name을 저장하는 POJO이다. 키로 용기에서 해당하는 인터넷 팩토리 대상을 찾은 다음에 인터넷 팩토리 대상을 호출하는create 방법으로 이 값을 되돌려준다.
여기에 struts2에서 bean은 변수의 범위를 설명할 수 있지 않습니까?예를 들어singleton,request,session,thread의 경우 인터넷 팩토리의create 방법을 직접 호출했을 뿐입니다. 이렇게 하면 목적을 달성할 수 있습니까? 앞에서 말했듯이 용기에 저장된 이 공장들의 장점은 바로 대상을 제어할 수 있는 것입니다. 이런 기능 논리는 모두 인터넷 팩토리에 봉인되어 있습니다.구체적인 것은struts2 원본 분석-IOC 용기의 실현 메커니즘(상편)을 참고하십시오. 상세한 설명이 있습니다.
struts2에서는 템플릿 방법, 리셋 방법을 대량으로 응용했는데 방금 보니까 익숙하지 않을 수도 있어요. 몇 번 더 보면 문제가 없을 거예요.여러분에게 도움이 되었으면 좋겠습니다. 잘못이 있으면 바로잡아 주십시오.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
java의 Struts2 파일 업로드 및 다운로드 예파일 업로드 Struts 응용 프로그램에서 File Upload 차단기와 Jakarta Commons File Upload 구성 요소로 파일을 업로드할 수 있습니다. Jsp 페이지의 파일 업로드 폼에 파일 탭을 사용...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.