Struts2.3.14分析-初始化2--Struts.xml解析

承接上一篇,在Filter初始化时,在DefautlConfiguration类中调用containerProvider的方法,完成对各个ContainerProvider的解析,我们着重分析对Struts.xml文件的读取和解析。

 

DefaultConfiguration类

public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
    packageContexts.clear();
    loadedFileNames.clear();
    List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();

    ContainerProperties props = new ContainerProperties();
    ContainerBuilder builder = new ContainerBuilder();
    for (final ContainerProvider containerProvider : providers)
    {
        containerProvider.init(this);①
        containerProvider.register(builder, props);②
    }
    ……

    // Then process any package providers from the plugins
    Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
    if (packageProviderNames != null) {
        for (String name : packageProviderNames) {
            PackageProvider provider = container.getInstance(PackageProvider.class, name);
            provider.init(this);
            provider.loadPackages();③ 
            packageProviders.add(provider);
        }
    }
    ……
    return packageProviders;
}

XmlConfigurationProvider类

private Set<String> loadedFileUrls = new HashSet<String>();
private Configuration configuration;
private Set<String> includedFileNames;
private List<Document> documents;

//①init 方法,读取该Provider对应的xml文件,转换成documents
public void init(Configuration configuration) {
    this.configuration = configuration;
    this.includedFileNames = configuration.getLoadedFileNames();
    //加载xml文件
    loadDocuments(configFileName);
}

private void loadDocuments(String configFileName) {
    try {
        loadedFileUrls.clear();
        documents = loadConfigurationFiles(configFileName, null);
    } catch (ConfigurationException e) {
        throw e;
    } catch (Exception e) {
        throw new ConfigurationException("Error loading configuration file " + configFileName, e);
    }
}

# 将xml文件转换成document,并存放在list中
private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
    List<Document> docs = new ArrayList<Document>();
    List<Document> finalDocs = new ArrayList<Document>();
    if (!includedFileNames.contains(fileName)) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Loading action configurations from: " + fileName);
        }
        includedFileNames.add(fileName);
        Iterator<URL> urls = null;
   	InputStream is = null;

        IOException ioException = null;
    	try {
	    # 获取配置文件的路径
  	    urls = getConfigurationUrls(fileName);
   	} catch (IOException ex) {
    	    ioException = ex;
  	}

  	if (urls == null || !urls.hasNext()) {
  	    if (errorIfMissing) {
   	        throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
   	    } else {
     		if (LOG.isInfoEnabled()) {
  		    LOG.info("Unable to locate configuration files of the name " + fileName + ", skipping");
                }
    	        return docs;
    	    }
        }

        URL url = null;
        while (urls.hasNext()) {
 	    try {
 	        url = urls.next();
                is = fileManager.loadFile(url);
      		InputSource in = new InputSource(is);
  		in.setSystemId(url.toString());
     		docs.add(DomHelper.parse(in, dtdMappings));
            } catch (XWorkException e) {
 		if (includeElement != null) {
 		    throw new ConfigurationException("Unable to load " + url, e, includeElement);
 		} else {
 		    throw new ConfigurationException("Unable to load " + url, e);
            }
        } catch (Exception e) {
 	    throw new ConfigurationException("Caught exception while loading file " + fileName, e, includeElement);
 	} finally {
  	    if (is != null) {
    	        try {
     		    is.close();
  		} catch (IOException e) {
 		    LOG.error("Unable to close input stream", e);
       		}
            }
        }
    }

    //sort the documents, according to the "order" attribute
    Collections.sort(docs, new Comparator<Document>() {
        public int compare(Document doc1, Document doc2) {
            return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
        }
     });

    # 解析文件,将xml文件转换成document对象
    for (Document doc : docs) {
  	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;
         	final String nodeName = child.getNodeName();
		# 处理include标签,xml文件嵌套
		if ("include".equals(nodeName)) {
                    String includeFileName = child.getAttribute("file");
  		    if (includeFileName.indexOf('*') != -1) {
 			// handleWildCardIncludes(includeFileName, docs, child);
                        ClassPathFinder wildcardFinder = new ClassPathFinder();                                
                        wildcardFinder.setPattern(includeFileName);
 			Vector<String> wildcardMatches = wildcardFinder.findMatches();
                        for (String match : wildcardMatches) {
                            finalDocs.addAll(loadConfigurationFiles(match, child));
                        }
                    } else {
                        finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
                    }
                }
            }
        }
        finalDocs.add(doc);
        loadedFileUrls.add(url.toString());
    }
    if (LOG.isDebugEnabled()) {
        LOG.debug("Loaded action configuration from: " + fileName);
    }
    }
    return finalDocs;
}

protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
    return ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false);
}

//②register方法,处理xml文件的beans、constant标签
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
    if (LOG.isInfoEnabled()) {
        LOG.info("Parsing configuration file [" + configFileName + "]");
    }
    Map<String, Node> loadedBeans = new HashMap<String, Node>();
    for (Document doc : documents) {
    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;
            final String nodeName = child.getNodeName();
				
            if ("bean".equals(nodeName)) {
                #将bean信息加载到containBuild中,struts自己的IoC
                String type = child.getAttribute("type");
                String 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 ("default".equals(scopeStr)) {
                    scope = Scope.DEFAULT;
                } 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 = Container.DEFAULT_NAME;
                }

                try {
                    Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
                    Class ctype = cimpl;
                    if (StringUtils.isNotEmpty(type)) {
                       ctype = ClassLoaderUtil.loadClass(type, getClass());
                    }
                    if ("true".equals(onlyStatic)) {
                       // Force loading of class to detect no class def found exceptions
                       cimpl.getDeclaredClasses();
                       containerBuilder.injectStatics(cimpl);
                    } else {
                       if (containerBuilder.contains(ctype, name)) {
                           Location loc = LocationUtils.getLocation(loadedBeans.get(ctype.getName() + name));
                           if (throwExceptionOnDuplicateBeans) {
                              throw new ConfigurationException("Bean type " + ctype + " with the name " +
                              name + " has already been loaded by " + loc, child);
                           }
                        }

                        // Force loading of class to detect no class def found exceptions
                        cimpl.getDeclaredConstructors();
 
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Loaded type:" + type + " name:" + name + " impl:" + impl);
                        }
                        containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
                    }
                    loadedBeans.put(ctype.getName() + name, child);
                } catch (Throwable ex) {
                    if (!optional) {
                        throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode);
                    } else {
                        if (LOG.isDebugEnabled()) {
                           LOG.debug("Unable to load optional class: #0", ex, impl);
                        }
                    }
                }
             } else if ("constant".equals(nodeName)) {
	         # 将constant信息加载到LocatableProperties
                 String name = child.getAttribute("name");
                 String value = child.getAttribute("value");
                 props.setProperty(name, value, childNode);
             } else if (nodeName.equals("unknown-handler-stack")) {
                 List<UnknownHandlerConfig> unknownHandlerStack = new ArrayList<UnknownHandlerConfig>();
                 NodeList unknownHandlers = child.getElementsByTagName("unknown-handler-ref");
                 int unknownHandlersSize = unknownHandlers.getLength();

                 for (int k = 0; k < unknownHandlersSize; k++) {
                     Element unknownHandler = (Element) unknownHandlers.item(k);
                     unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name")));
                 }

                 if (!unknownHandlerStack.isEmpty())                            
                     configuration.setUnknownHandlerStack(unknownHandlerStack);
                 }
             }
         }
    }
}

//③loadPackages方法加载Package节点信息
public void loadPackages() throws ConfigurationException {
    List<Element> reloads = new ArrayList<Element>();
    //检查Package extends是否处在闭环,如A extends B,但B却 extends A
    verifyPackageStructure();⑴

    for (Document doc : documents) {
        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;
                final String nodeName = child.getNodeName();

                if ("package".equals(nodeName)) {
                    PackageConfig cfg = addPackage(child);⑵
                    if (cfg.isNeedsRefresh()) {
                        reloads.add(child);
                    }
                }
            }
        }
        loadExtraConfiguration(doc);
    }

    if (reloads.size() > 0) {
        reloadRequiredPackages(reloads);
    }

    for (Document doc : documents) {
        loadExtraConfiguration(doc);
    }

    documents.clear();
    declaredPackages.clear();
    configuration = null;
}

private void verifyPackageStructure() {
    DirectedGraph<String> graph = new DirectedGraph<String>();

    for (Document doc : documents) {
        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;
                final String nodeName = child.getNodeName();

                if ("package".equals(nodeName)) {
                    String packageName = child.getAttribute("name");
                    declaredPackages.put(packageName, child);
                    graph.addNode(packageName);

                    String extendsAttribute = child.getAttribute("extends");
                    List<String> parents = ConfigurationUtil.buildParentListFromString(extendsAttribute);
                    //添加顶点和变到图中
                    for (String parent : parents) {
                        graph.addNode(parent);
                        graph.addEdge(packageName, parent);
                    }
                }
            }
         }
    }

    CycleDetector<String> detector = new CycleDetector<String>(graph);
    //检测图中是否包含回路
    if (detector.containsCycle()) {
        StringBuilder builder = new StringBuilder("The following packages participate in cycles:");
        for (String packageName : detector.getVerticesInCycles()) {
            builder.append(" ");
            builder.append(packageName);
        }
        throw new ConfigurationException(builder.toString());
     }
}

protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
    PackageConfig.Builder newPackage = buildPackageContext(packageElement);

    if (newPackage.isNeedsRefresh()) {
        return newPackage.build();
    }

    if (LOG.isDebugEnabled()) {
        LOG.debug("Loaded " + newPackage);
    }

    // add result types (and default result) to this package
    addResultTypes(newPackage, packageElement);

    // load the interceptors and interceptor stacks for this package
    loadInterceptors(newPackage, packageElement);

    // load the default interceptor reference for this package
    loadDefaultInterceptorRef(newPackage, packageElement);

    // load the default class ref for this package
    loadDefaultClassRef(newPackage, packageElement);

    // load the global result list for this package
    loadGlobalResults(newPackage, packageElement);

    // load the global exception handler list for this package
    loadGobalExceptionMappings(newPackage, packageElement);

    // get actions
    NodeList actionList = packageElement.getElementsByTagName("action");

    for (int i = 0; i < actionList.getLength(); i++) {
        Element actionElement = (Element) actionList.item(i);
        //处理Package中的Action
        addAction(actionElement, newPackage);
    }

    // load the default action reference for this package
    loadDefaultActionRef(newPackage, packageElement);

    PackageConfig cfg = newPackage.build();
    //将Package信息加入DefaultConfiguration中
    configuration.addPackageConfig(cfg.getName(), cfg);
    return cfg;
}

protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException {
    String name = actionElement.getAttribute("name");
    String className = actionElement.getAttribute("class");
    String methodName = actionElement.getAttribute("method");
    Location location = DomHelper.getLocationObject(actionElement);

    if (location == null) {
        if (LOG.isWarnEnabled()) {
            LOG.warn("location null for " + className);
        }
    }
    //methodName should be null if it's not set
    methodName = (methodName.trim().length() > 0) ? methodName.trim() : null; 

    Map<String, ResultConfig> results;
    try {
	//处理Action下的多个result
        results = buildResults(actionElement, packageContext);
    } catch (ConfigurationException e) {
        throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement);
    }

    List<InterceptorMapping> interceptorList = buildInterceptorList(actionElement, packageContext);

    List<ExceptionMappingConfig> exceptionMappings = buildExceptionMappings(actionElement, packageContext);

    Set<String> allowedMethods = buildAllowedMethods(actionElement, packageContext);

    ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className)
    .methodName(methodName)
    .addResultConfigs(results)
    .addInterceptors(interceptorList)
    .addExceptionMappings(exceptionMappings)
    .addParams(XmlHelper.getParams(actionElement))
    .addAllowedMethod(allowedMethods)
    .location(location)
    .build();
    packageContext.addActionConfig(name, actionConfig);
}

StrutsXmlConfigurationProvider类

public void register(ContainerBuilder containerBuilder, 
LocatableProperties props) throws ConfigurationException {
    if (servletContext != null && !containerBuilder.contains(ServletContext.class)) {
        containerBuilder.factory(ServletContext.class, new Factory<ServletContext>() {
            public ServletContext create(Context context) throws Exception {
                return servletContext;
            }
        });
    }
    #调用XmlConfigurationProvider的register方法
    super.register(containerBuilder, props);
}

ContainerBuilder类

#容器类,保存配置文件的Beans
public final class ContainerBuilder {
    final Map<Key<?>, InternalFactory<?>> factories = new HashMap<Key<?>, InternalFactory<?>>();
    final List<InternalFactory<?>> singletonFactories = new ArrayList<InternalFactory<?>>();
    final List<Class<?>> staticInjections = new ArrayList<Class<?>>();
    boolean created;
    boolean allowDuplicates = false;

    public ContainerBuilder injectStatics(Class<?>... types) {
    	staticInjections.addAll(Arrays.asList(types));
    	return this;
    }

    private void ensureNotCreated() {
        if (created) {
            throw new IllegalStateException("Container already created.");
        }
    }

    private void checkKey(Key<?> key) {
        if (factories.containsKey(key) && !allowDuplicates) {
            throw new DependencyException("Dependency mapping for " + key + " already exists.");
        }
    }
	
    public <T> ContainerBuilder factory(final Class<T> type, final String name, final Class<? extends T> implementation, final Scope scope) {
        InternalFactory<? extends T> factory = new InternalFactory<T>(){
        // This factory creates new instances of the given implementation.
    	// We have to lazy load the constructor because the Container
        // hasn't been created yet.
      	volatile ContainerImpl.ConstructorInjector<? extends T> constructor;
      	@SuppressWarnings("unchecked")
        public T create(InternalContext context) {
            if (constructor == null) {
          	this.constructor = context.getContainerImpl().getConstructor(implementation);
            }
            return (T) constructor.construct(context, type);
      	}

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

    private <T> ContainerBuilder factory(final Key<T> key,
      InternalFactory<? extends T> factory, Scope scope) {
        ensureNotCreated();
    	checkKey(key);
    	final InternalFactory<? extends T> scopedFactory =
    	scope.scopeFactory(key.getType(), key.getName(), factory);
    	factories.put(key, scopedFactory);
            if (scope == Scope.SINGLETON) {
      		singletonFactories.add(new InternalFactory<T>() {
        	    public T create(InternalContext context) {
          	        try {
            		    context.setExternalContext(ExternalContext.newInstance(
                null, key, context.getContainerImpl()));
            		    return scopedFactory.create(context);
          		} finally {
            		    context.setExternalContext(null);
          	        }
        	    }
      		});
    	    }
    	return this;
    }

这样,在调用完init和register方法后,将struts.xml内容转化为Java对象存储起来,方便后面管理。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值