struct2 원본 해독(8)의container 원리

struct2 원본 해독의container 원리
컨테이너를 중국어로 번역하면 용기라는 뜻으로 통속적으로 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 T 유형의 대상으로 강제로 전환됨
    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는 대상을 실례화하고 설정합니다.

좋은 웹페이지 즐겨찾기