struts2_源碼學習_factories

在ContainerBuilder和Container中最重要的一個字段就是: 

    final Map<Key<?>, InternalFactory<?>> factories;

從字面上我們可以看出這應該是對應的工廠鍵值對映射,那麼key和value分別存儲的是什麼呢?

package com.opensymphony.xwork2.inject;

class Key<T> {
    final Class<T> type;
    final String name;
    final int hashCode;

    private Key(Class<T> type, String name) {
        if (type == null) {
            throw new NullPointerException("Type is null.");
        } else if (name == null) {
            throw new NullPointerException("Name is null.");
        } else {
            this.type = type;
            this.name = name;
            this.hashCode = type.hashCode() * 31 + name.hashCode();
        }
    }

    Class<T> getType() {
        return this.type;
    }

    String getName() {
        return this.name;
    }

    public int hashCode() {
        return this.hashCode;
    }

    public boolean equals(Object o) {
        if (!(o instanceof Key)) {
            return false;
        } else if (o == this) {
            return true;
        } else {
            Key other = (Key)o;
            return this.name.equals(other.name) && this.type.equals(other.type);
        }
    }

    public String toString() {
        return "[type=" + this.type.getName() + ", name='" + this.name + "']";
    }

    static <T> Key<T> newInstance(Class<T> type, String name) {
        return new Key(type, name);
    }
}

 可以看出Key存儲的應該就是<bean>結點中對應的type和name屬性。

package com.opensymphony.xwork2.inject;

import java.io.Serializable;

interface InternalFactory<T> extends Serializable {
    T create(InternalContext var1);

    Class<? extends T> type();
}

而InternalFactory則是一個接口,create()方法應該就是創建一個新的實例,我們需要去看它具體應用的地方。<bean>信息的傳遞過程應該是從 providers(register) -> builder(create) -> container(new ContainerBuilder)

以StrutsXmlConfigurationProvider爲例,它的register方法在父類XmlConfigurationProvider中:

XmlConfigurationProvider.register()

public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
        LOG.trace("Parsing configuration file [{}]", this.configFileName);
        Map<String, Node> loadedBeans = new HashMap();
        Iterator i$ = this.documents.iterator();

        while(i$.hasNext()) {
            Document doc = (Document)i$.next();
            Element rootElement = doc.getDocumentElement();
            NodeList children = rootElement.getChildNodes();
            int childSize = children.getLength();

            for(int i = 0; i < childSize; ++i) {
                Node childNode = children.item(i);
                if (childNode instanceof Element) {
                    Element child = (Element)childNode;
                    String nodeName = child.getNodeName();
                    String type;
                    String name;
                    if ("bean".equals(nodeName)) {
                        type = child.getAttribute("type");
                        name = child.getAttribute("name");
                        String impl = child.getAttribute("class");
                        String onlyStatic = child.getAttribute("static");
                        String scopeStr = child.getAttribute("scope");
                        boolean optional = "true".equals(child.getAttribute("optional"));
                        Scope scope = Scope.SINGLETON;
                        if ("prototype".equals(scopeStr)) {
                            scope = Scope.PROTOTYPE;
                        } else if ("request".equals(scopeStr)) {
                            scope = Scope.REQUEST;
                        } else if ("session".equals(scopeStr)) {
                            scope = Scope.SESSION;
                        } else if ("singleton".equals(scopeStr)) {
                            scope = Scope.SINGLETON;
                        } else if ("thread".equals(scopeStr)) {
                            scope = Scope.THREAD;
                        }

                        if (StringUtils.isEmpty(name)) {
                            name = "default";
                        }

                        try {
                            Class classImpl = ClassLoaderUtil.loadClass(impl, this.getClass());
                            Class classType = classImpl;
                            if (StringUtils.isNotEmpty(type)) {
                                classType = ClassLoaderUtil.loadClass(type, this.getClass());
                            }

                            if ("true".equals(onlyStatic)) {
                                classImpl.getDeclaredClasses();
                                containerBuilder.injectStatics(new Class[]{classImpl});
                            } else {
                                if (containerBuilder.contains(classType, name)) {
                                    Location loc = LocationUtils.getLocation(loadedBeans.get(classType.getName() + name));
                                    if (this.throwExceptionOnDuplicateBeans) {
                                        throw new ConfigurationException("Bean type " + classType + " with the name " + name + " has already been loaded by " + loc, child);
                                    }
                                }

                                classImpl.getDeclaredConstructors();
                                LOG.debug("Loaded type: {} name: {} impl: {}", type, name, impl);
                                containerBuilder.factory(classType, name, new LocatableFactory(name, classType, classImpl, scope, childNode), scope);
                            }

                            loadedBeans.put(classType.getName() + name, child);
                        } catch (Throwable var23) {
                            ...
                            }

                            LOG.debug("Unable to load optional class: {}", impl);
                        }
                    } else if ("constant".equals(nodeName)) {
                            ...
                        }

                        props.setProperty(type, name, childNode);
                    } else if (nodeName.equals("unknown-handler-stack")) {
                            ...
                    }
                }
            }
        }

    }

首先就是對<bean>結點信息進行解析,主要的信息type\name\class\scope等。然後調用:

containerBuilder.factory(classType, name, new LocatableFactory(name, classType, classImpl, scope, childNode), scope);

對於LocatableFactory不加以解釋,暫時理解爲工廠。scope就是範圍的意思。

    public <T> ContainerBuilder factory(final Class<T> type, final String name, final Factory<? extends T> factory, Scope scope) {
        InternalFactory<T> internalFactory = new InternalFactory<T>() {
            public T create(InternalContext context) {
                try {
                    Context externalContext = context.getExternalContext();
                    return factory.create(externalContext);
                } catch (Exception var3) {
                    throw new RuntimeException(var3);
                }
            }

            public Class<? extends T> type() {
                return factory.type();
            }

            public String toString() {
                return (new LinkedHashMap<String, Object>() {
                    {
                        this.put("type", type);
                        this.put("name", name);
                        this.put("factory", factory);
                    }
                }).toString();
            }
        };
        return this.factory(Key.newInstance(type, name), internalFactory, scope);
    }

終於看到了InternalFactory的實現了,其中create方法主要就是爲工廠注入一個上下環境,我們進入create方法看看:

 LocatableFactory

   public T create(Context context) {
        Object obj = context.getContainer().inject(this.implementation);
        return obj;
    }

這個我們在Container中提到過:inject()創建一個類的實例並進行對象的依賴注入操作。

那麼接下來到了另外一個factory方法

  return this.factory(Key.newInstance(type, name), internalFactory, scope);

   private <T> ContainerBuilder factory(Key<T> key, InternalFactory<? extends T> factory, Scope scope) {
        this.ensureNotCreated();
        this.checkKey(key);
        InternalFactory<? extends T> scopedFactory = scope.scopeFactory(key.getType(), key.getName(), factory);
        this.factories.put(key, scopedFactory);
        InternalFactory<T> callableFactory = this.createCallableFactory(key, scopedFactory);
        if (EarlyInitializable.class.isAssignableFrom(factory.type())) {
            this.earlyInitializableFactories.add(callableFactory);
        } else if (scope == Scope.SINGLETON) {
            this.singletonFactories.add(callableFactory);
        }

        return this;
    }

InternalFactory又會被再次加工:

 InternalFactory<? extends T> scopedFactory = scope.scopeFactory(key.getType(), key.getName(), factory);
Scope

    SINGLETON {
        <T> InternalFactory<? extends T> scopeFactory(Class<T> type, String name, final InternalFactory<? extends T> factory) {
            return new InternalFactory<T>() {
                T instance;

                public T create(InternalContext context) {
                    synchronized(context.getContainer()) {
                        if (this.instance == null) {
                            this.instance = InitializableFactory.wrapIfNeeded(factory).create(context);
                        }

                        return this.instance;
                    }
                }

                public Class<? extends T> type() {
                    return factory.type();
                }

                public String toString() {
                    return factory.toString();
                }
            };
        }
    },

Scope是一個枚舉類型,爲工廠添加一個作用的範圍,我們可以看到其中的SINGLETON也就是單例模式,返回的就是一個單例模式的工廠。這就是factories中存儲的value的值了

factories

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章