XWork 컨테이너의 스토리지 구조

Container의 기본 구현에서 Container Impl에는 두 개의 실례 변수가 있음을 알 수 있습니다.factoris 및 factory NamesByType.

객체 제조 플랜트

class ContainerImpl implements Container {

    final Map<Key<?>, InternalFactory<?>> factories;
    final Map<Class<?>, Set<String>> factoryNamesByType;

    ContainerImpl( Map<Key<?>, InternalFactory<?>> factories ) {
        this.factories = factories;
        Map<Class<?>, Set<String>> map = new HashMap<Class<?>, Set<String>>();
        for ( Key<?> key : factories.keySet() ) {
            Set<String> names = map.get(key.getType());
            if (names == null) {
                names = new HashSet<String>();
                map.put(key.getType(), names);
            }
            names.add(key.getName());
        }

        for ( Entry<Class<?>, Set<String>> entry : map.entrySet() ) {
            entry.setValue(Collections.unmodifiableSet(entry.getValue()));
        }

        this.factoryNamesByType = Collections.unmodifiableMap(map);
    }
}

우선factories를 보면 그 값은 구조 함수에 의해 전달된다.우리 그것의 유형을 좀 봅시다.
맵 형식의 키는 키입니다.
class Key<T> {

  final Class<T> type;
  final String name;
  final int hashCode;
  //...
}

이 type과 name을 보니 뭔가 생각나지 않나요?
네, 바로 struts-default입니다.xml
<struts>
    <bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
    <bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />
    <bean type="com.opensymphony.xwork2.factory.ActionFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultActionFactory" />
    <bean type="com.opensymphony.xwork2.factory.ConverterFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultConverterFactory" />
 
    <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/>
    <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="prefix" class="org.apache.struts2.impl.PrefixBasedActionProxyFactory"/>

....
<struts>
type과name은 비안을 유일하게 확인할 수 있습니다.
인터넷 팩토리 또 봐요.
interface InternalFactory<T> extends Serializable {

  /**
   * Creates an object to be injected.
   *
   * @param context of this injection
   * @return instance to be injected
   */
  T create(InternalContext context);
}
인터넷 팩토리에는 이 클래스의 실례가 아닌 하나의 클래스를 만드는 방법이 저장되어 있다.

주입기

이제 주입기를 다시 봅시다.
주입기는 무엇입니까?
지난 편에서 우리가 말한 사람과 차의 예를 기억하십니까?
사람이 용기에 차 한 대가 필요하다고 말하기만 하면 용기는 자동으로 사람에게 차 한 대를 주입한다.
도대체 어떻게 자동이야?주입기는 바로 이 일을 하는 것이다.
천천히 봅시다.
ContainerImpl inject 。
    //o   
    void inject( Object o, InternalContext context ) {
            // 
        List<Injector> injectors = this.injectors.get(o.getClass());  // 8
        for ( Injector injector : injectors ) {
            // 
            injector.inject(context, o);
        }
    }
'사람'에 어떤 주입기가 있는지 획득한다.
    this.injectors.get(o.getClass());
아래의 예에서 사람은'방법 주입기'가 하나 있다.
public class Person{
    private Car car;

    public Person(){
    // 
    }

    @Inject()
    public void setCar(Car c){
    this.car=c;
    }
    public void drive(){
    car.drive();
    }
}
주입기는 두 종류로 나뉘는데 방법 주입기, 속성 주입기이다.
그 인터페이스는 다음과 같다.
    /**
     * Injects a field or method in a given object.
     */
    interface Injector extends Serializable {

        void inject( InternalContext context, Object o );
    }

우리는 속성 주입기를 보았는데, 방법 주입기가 유사하다.
static class FieldInjector implements Injector {

    final Field field;
    final InternalFactory<?> factory;
    final ExternalContext<?> externalContext;

    public FieldInjector( ContainerImpl container, Field field, String name )
    throws MissingDependencyException {
    this.field = field;
    ...
    }

    Key<?> key = Key.newInstance(field.getType(), name);
    factory = container.getFactory(key);     // 2
    ...

    public void inject( InternalContext context, Object o ) {
        ExternalContext<?> previous = context.getExternalContext();
        context.setExternalContext(externalContext);
        // trycatch
        field.set(o, factory.create(context));// 1

    }
}
표지2는 용기에서 이 키를 얻는 인터넷 팩토리입니다.
위 코드의 표지 1에서 주입기가 마지막 작업을 한 것은 주입이다.
Internal Factory의 creat 방법이 어떻게 구현되었는지 궁금하실 거라고 믿습니다.
우리 생각을 바꿔도 무방하다,field.set () 의 서명은 다음과 같습니다
 public void set(Object obj, Object value)
        throws IllegalArgumentException, IllegalAccessException
개인 p의 필드 카드 및 컨테이너 내부의 카드 c2에 대해
그것의 호출은 바로
c.set(p,c2);
즉 인터넷 팩토리의 creat은 용기 내의 위탁 관리 대상이 생겼을 뿐이다.
Container Impl 의 injectors 매개 변수를 다시 봅시다.
final Map<Class<?>, List<Injector>> injectors =
    new ReferenceCache<Class<?>, List<Injector>>() {
        @Override
        protected List<Injector> create( Class<?> key ) { // 7
            List<Injector> injectors = new ArrayList<Injector>();
            addInjectors(key, injectors); // 4
            return injectors;
        }
    };
웬일이야, 복잡해 보이는데.
public abstract class ReferenceCache<K, V> extends ReferenceMap<K, V> {

  private static final long serialVersionUID = 0;

  transient ConcurrentMap<Object, Future<V>> futures =
      new ConcurrentHashMap<Object, Future<V>>();

  transient ThreadLocal<Future<V>> localFuture = new ThreadLocal<Future<V>>();

    protected abstract V create(K key);
    //...
 }

ReferenceMap은 맵 인터페이스를 구현합니다.
Reference Catch가 있으면 맵을 조작하는 것이 훨씬 효율적입니다. get 방법을 호출할 때 키가 이미 존재하면 바로 되돌아갑니다. 그렇지 않으면creat를 호출해서 생성하고 캐시합니다. 다음에create를 사용하지 않아도 됩니다.
동시에 여러분은 이create가 추상적인 방법이라는 것을 주의하세요.
다시 말하면 컨테이너 Impl의 injectors는 운행 기간에 동태적으로 구축된 것이다.
그럼 도대체 언제 위에 표시된 7곳의 creat 방법을 사용합니까?
위 코드 표지 8에서 get 후 호출 (ctrl+f 표지 8)
여기에 struts2의 캐시 기술이 관련되어 있으니 이쪽은 우선 말하지 않겠습니다.
졸작을 참고할 만하다
Struts2의 캐시 - Injector의 경우
어쨌든 효과는 get 방법을 호출할 때 키가 존재하면 바로 되돌려줍니다. 그렇지 않으면 creat를 호출해서 캐시를 만들고 다음에는 create를 사용하지 않아도 됩니다.
자, 이제 4곳을 표시하는ddInjectors 방법을 살펴보겠습니다.
    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);
    }
속성 주입기를 봅시다.
    void addInjectorsForFields( Field[] fields, boolean statics,
        List<Injector> injectors ) {
        addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
                new InjectorFactory<Field>() {
                    // 5
                    public Injector create( ContainerImpl container, Field field,    String name ) throws MissingDependencyException {
                        return new FieldInjector(container, field, name);
                    }
                });
    }
addInjectorsForFields에는 한 줄의 코드만 있는데 그것이 바로 addInjectorsForMembers를 호출하는 것이다. 그 매개 변수의 마지막 유형은 InjectorFactory이다.
 
   <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) {
                // member Inject annotation
                Inject inject = member.getAnnotation(Inject.class);
                if (inject != null) {
                    try {
                        // injectorFactory 
                        // 5
                        // injecter 
                        injectors.add(injectorFactory.create(this, member, inject.value()));
                    } catch ( MissingDependencyException e ) {
                        if (inject.required()) {
                            throw new DependencyException(e);
                        }
                    }
                }
            }
        }
    }

만약 여러분이ddInjectorsForMethods를 다시 보신다면, 클래스의 방법이나 구성원 변수에 Inject라는 annotation을 더하면 용기는 매개 변수에 해당하는 실례를 주입할 것입니다.
먼저 여기를 보시고 다음 절에서는 XWork의 실현 기리를 보겠습니다.
(struts2의 원본 코드를 분석하는 것은 저에게 어려운 일입니다. 글을 잘 못 썼으니 벽돌을 찍고 함께 발전하는 것을 환영합니다.)
감사glt

좋은 웹페이지 즐겨찾기