源码分析JSF画面跳转下一个画面流程,关联faces-config.xml

项目启动加载解析faces-config.xml

1.了解一下faces-config.xml画面跳转

	<!-- 第一种 -->
	<navigation-rule>
		<!-- 配置全局 -->
		<navigation-case>
			<from-outcome>HomePage</from-outcome>
			<to-view-id>/index/home/HomePage.jsp</to-view-id>
			<redirect />
		</navigation-case>
	</navigation-rule>
	<!-- 第二种 -->
	<navigation-rule>
		<!-- 当前画面所在包位置 -->
		<from-view-id>/index/login/Login.jsp</from-view-id>
		<!-- 跳转画面 -->
		<navigation-case>
			<from-outcome>Regedit</from-outcome>
			<to-view-id>/index/login/Regedit.jsp</to-view-id>
			<redirect />
		</navigation-case>
		<navigation-case>
			<from-outcome>UpdatePassWord</from-outcome>
			<to-view-id>/index/login/UpdatePassWord.jsp</to-view-id>
			<redirect />
		</navigation-case>
	</navigation-rule>
	<!-- 第三种 -->
	<navigation-rule>
		<from-view-id>/index/login/Login.jsp</from-view-id>
		<navigation-case>
			<from-action>#{rc_Login.RegeditAction}</from-action>
			<from-outcome>Regedit</from-outcome>
			<to-view-id>/index/login/Regedit.jsp</to-view-id>
			<redirect />
		</navigation-case>
		<navigation-case>
			<from-action>#{rc_Login.CloseAction}</from-action>
			<from-outcome>CLOSE</from-outcome>
			<to-view-id>/index/login/CLOSE.jsp</to-view-id>
			<redirect />
		</navigation-case>
	</navigation-rule>
2.NavigationConfigProcessor类 解析xml
2.1.定义标签名常量
    /**
     * <p>/faces-config/navigation-rule</p>
     */
    private static final String NAVIGATION_RULE = "navigation-rule";
    /**
     * <p>/faces-config/navigation-rule/from-view-id</p>
     */
    private static final String FROM_VIEW_ID = "from-view-id";
    /**
     * <p>/faces-config/navigation-rule/navigation-case</p>
     */
    private static final String NAVIGATION_CASE = "navigation-case";
    /**
     * <p>/faces-config/navigation-rule/navigation-case/from-action</p>
     */
    private static final String FROM_ACTION = "from-action";
    /**
     * <p>/faces-config/navigation-rule/navigation-case/from-outcome</p>
     */
    private static final String FROM_OUTCOME = "from-outcome";
    /**
     * <p>/faces-config/navigation-rule/navigation-case/to-view-id</p>
     */
    private static final String TO_VIEW_ID = "to-view-id";
    /**
     * <p>/faces-config/navigation-rule/navigation-case/redirect</p>
     */
    private static final String REDIRECT = "redirect";
    /**
     * <p>If <code>from-view-id</code> is not defined.<p>
     */
    private static final String FROM_VIEW_ID_DEFAULT = "*";
2.2.解析xml入口
	//传入Document节点
	//使用org.w3c.dom操作XML
    public void process(Document[] documents)
    throws Exception {
		
        for (Document document : documents) {
            //获取节点的命名空间URI
            String namespace = document.getDocumentElement()
                    .getNamespaceURI();
            //获取<navigation-rule>标签
            NodeList navigationRules = document.getDocumentElement()
                    .getElementsByTagNameNS(namespace, NAVIGATION_RULE);
            if (navigationRules != null && navigationRules.getLength() > 0) {
            	//解析<navigation-rule>下所有子标签
                addNavigationRules(navigationRules);
            }
        }
		//获取下一个节点
        invokeNext(documents);
    }
2.3解析navigation-rule标签,Method「addNavigationRules」
    private void addNavigationRules(NodeList navigationRules)throws XPathExpressionException {
    	//遍历<navigation-rule>标签,获取<from-view-id>和<navigation-case>
	   for (int i = 0, size = navigationRules.getLength(); i < size; i++) {
	       Node navigationRule = navigationRules.item(i);
	       if (navigationRule.getNodeType() == Node.ELEMENT_NODE) {
	           NodeList children = navigationRule.getChildNodes();
	           //初始化fromViewId
	           //用来存放<from-view-id>标签中值
	           //默认值为*
	           String fromViewId = FROM_VIEW_ID_DEFAULT;
	           //初始化List<Node> navigationCases
	           //用来存放<navigation-case>下所有子标签
	           List<Node> navigationCases = null;
	           for (int c = 0, csize = children.getLength(); c < csize; c++) {
	               Node n = children.item(c);
	               if (n.getNodeType() == Node.ELEMENT_NODE) {
	               		//判断为<from-view-id>标签
	                   if (FROM_VIEW_ID.equals(n.getLocalName())) {
	                   		//获取标签中值
	                       String t = getNodeText(n);
	                       //判断<from-view-id>标签中值等于null,fromViewId=*
	                       //否则等于标签中值
	                       fromViewId = ((t == null) ? FROM_VIEW_ID_DEFAULT : t);
	                       //判断<from-view-id>标签中值不等于*,并且第一个字符不等于「/」
	                       //在最开始追加「/」
	                       if (!fromViewId.equals(FROM_VIEW_ID_DEFAULT) && fromViewId.charAt(0) != '/') {
	                           fromViewId = '/' + fromViewId;
	                       }
	                   } 
	                   //判断为<navigation-case>标签
	                   else if (NAVIGATION_CASE.equals(n.getLocalName())) {
	                       if (navigationCases == null) {
	                           navigationCases = new ArrayList<Node>(csize);
	                       }
	                       navigationCases.add(n);
	                   }
	               }
	           }
	           //解析<navigation-case>标签下子标签
	           addNavigationCasesForRule(fromViewId, navigationCases);
	       }
	   }
    }
2.4解析navigation-case标签,Method「addNavigationRules」
    private void addNavigationCasesForRule(String fromViewId,
                                           List<Node> navigationCases) {

        if (navigationCases != null && !navigationCases.isEmpty()) {
            ApplicationAssociate associate = ApplicationAssociate.getCurrentInstance();
			//遍历navigationCases下存放
			//<from-outcome><from-action><to-view-id><redirect>标签
            for (Node navigationCase : navigationCases) {
                if (navigationCase.getNodeType() != Node.ELEMENT_NODE) {
                    continue;
                }
                NodeList children = navigationCase.getChildNodes();
                String outcome = null;
                String action = null;
                String toViewId = null;
                boolean redirect = false;
                for (int i = 0, size = children.getLength(); i < size; i++) {
                    Node n = children.item(i);
                    if (n.getNodeType() == Node.ELEMENT_NODE) {
                    	//判断为<from-outcome>标签
                        if (FROM_OUTCOME.equals(n.getLocalName())) {
                            outcome = getNodeText(n);
                        } 
                        //判断为<from-action>标签
                        else if (FROM_ACTION.equals(n.getLocalName())) {
                            action = getNodeText(n);
                        } 
                        //判断为<to-view-id>标签
                        else if (TO_VIEW_ID.equals(n.getLocalName())) {
                            toViewId = getNodeText(n);
                            if (toViewId.charAt(0) != '/') {
                                toViewId = '/' + toViewId;           
                            }
                        } 
                        //判断为<redirect>标签
                        else if (REDIRECT.equals(n.getLocalName())) {
                            redirect = true;
                        }
                    }
                }
               	//将<fromViewId>和<navigation-case>标签下子标签值
               	//存放ConfigNavigationCase对象中
                ConfigNavigationCase cnc =
                     new ConfigNavigationCase(fromViewId,
                                              action,
                                              outcome,
                                              toViewId,
                                              redirect);
				//将解析完成<fromViewId>和<navigation-case>
				//存放caseListMap和wildcardMatchList 
                associate.addNavigationCase(cnc);
            }
        }
    }
3.ApplicationAssociate 类,将ConfigNavigationCase 对象进行解析
3.1 定义初始值
    private Map<String, List<ConfigNavigationCase>> caseListMap = null;
    private TreeSet<String> wildcardMatchList = null;
    
    public ApplicationAssociate(ApplicationImpl appImpl) {
        //noinspection CollectionWithoutInitialCapacity
        caseListMap = new HashMap<String, List<ConfigNavigationCase>>();
        wildcardMatchList = new TreeSet<String>(new SortIt());
    }
3.2解析navigation-case标签,Method「addNavigationRules」
    public void addNavigationCase(ConfigNavigationCase navigationCase) {
    	//获取fromViewId标签值
        String fromViewId = navigationCase.getFromViewId();
        //获取<navigation-case>标签下值
        List<ConfigNavigationCase> caseList = caseListMap.get(fromViewId);
        //caseListMap获取不到fromViewId
        if (caseList == null) {
        	//初始化caseListMap的Value值
            caseList = new ArrayList<ConfigNavigationCase>();
           //将ConfigNavigationCase对象添加caseList
            caseList.add(navigationCase);
            //将fromViewId和caseList存放入caseListMap
            caseListMap.put(fromViewId, caseList);
        } 
        //caseListMap获取fromViewId,对fromViewId重复处理
        else {
            String key = navigationCase.getKey();
            boolean foundIt = false;
            for (int i = 0; i < caseList.size(); i++) {
                ConfigNavigationCase navCase = caseList.get(i);
                // if there already is a case existing for the
                // fromviewid/fromaction.fromoutcome combination,
                // replace it ...  (last one wins).
                //
                if (key.equals(navCase.getKey())) {
                    caseList.set(i, navigationCase);
                    foundIt = true;
                    break;
                }
            }
            if (!foundIt) {
                caseList.add(navigationCase);
            }
        }
        //如果fromViewId是以*结尾
        //将fromViewId存放入wildcardMatchList
        if (fromViewId.endsWith("*")) {
            fromViewId =
                 fromViewId.substring(0, fromViewId.lastIndexOf('*'));
            wildcardMatchList.add(fromViewId);
        }

    }
3.3 获取caseListMap
    /**
     * Return a <code>Map</code> of navigation mappings loaded from
     * the configuration system.  The key for the returned <code>Map</code>
     * is <code>from-view-id</code>, and the value is a <code>List</code>
     * of navigation cases.
     *
     * @return Map the map of navigation mappings.
     */
    public Map<String, List<ConfigNavigationCase>> getNavigationCaseListMappings() {
        if (caseListMap == null) {
            return Collections.emptyMap();
        }
        return caseListMap;
    }
3.4 获取wildcardMatchList
	 /**
     * Return all navigation mappings whose <code>from-view-id</code>
     * contained a trailing "*".
     *
     * @return <code>TreeSet</code> The navigation mappings sorted in
     *         descending order.
     */
    public TreeSet<String> getNavigationWildCardList() {
        return wildcardMatchList;
    }
3.5 SortIt内部类
    /**
     * This Comparator class will help sort the <code>ConfigNavigationCase</code> objects
     * based on their <code>fromViewId</code> properties in descending order -
     * largest string to smallest string.
     */
    static class SortIt implements Comparator<String> {

        public int compare(String fromViewId1, String fromViewId2) {
            return -(fromViewId1.compareTo(fromViewId2));
        }
    }
4.ConfigNavigationCase类 存放fromViewIdnavigation-case标签中子标签值
public class ConfigNavigationCase {
    private String fromViewId = null;
    private String fromAction = null;
    private String fromOutcome = null;
    private String toViewId = null;
    private String key = null;
    private boolean redirect;
    public ConfigNavigationCase(String fromViewId,
                                String fromAction,
                                String fromOutcome,
                                String toViewId,
                                boolean redirect) {
        this.fromViewId = fromViewId;
        this.fromAction = fromAction;
        this.fromOutcome = fromOutcome;
        this.toViewId = toViewId;
        this.key = fromViewId
                   + ((fromAction == null) ? "-" : fromAction)
                   + ((fromOutcome == null) ? "-" : fromOutcome);
        this.redirect = redirect;
    }
    public String getFromViewId() {
        return (this.fromViewId);
    }
    public void setFromViewId(String fromViewId) {
        this.fromViewId = fromViewId;
    }
    public String getFromAction() {
        return (this.fromAction);
    }
    public void setFromAction(String fromAction) {
        this.fromAction = fromAction;
    }
    public String getFromOutcome() {
        return (this.fromOutcome);
    }
    public void setFromOutcome(String fromOutcome) {
        this.fromOutcome = fromOutcome;
    }
    public String getToViewId() {
        return (this.toViewId);
    }
    public void setToViewId(String toViewId) {
        this.toViewId = toViewId;
    }
    public boolean hasRedirect() {
        return redirect;
    }
    public void setRedirect(boolean redirect) {
        this.redirect = redirect;
    }
    /**
     * The "key" is defined as the combination of
     * <code>from-view-id</code><code>from-action</code>
     * <code>from-outcome</code>.
     * @return the derived key
     */
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String toString() {
        StringBuilder sb = new StringBuilder(64);
        sb.append("from-view-id=").append(getFromViewId());
        sb.append(" from-action=").append(getFromAction());
        sb.append(" from-outcome=").append(getFromOutcome());
        sb.append(" to-view-id=").append(getToViewId());
        sb.append(" redirect=").append(hasRedirect());
        return sb.toString();
    }
}
5.NavigationHandlerImpl类,当前画面跳转下一个画面
5.1初始化
    /**
     * <code>Map</code> containing configured navigation cases.
     */
    private Map<String, List<ConfigNavigationCase>> caseListMap;
        /**
     * <code>Set</code> containing wildcard navigation cases.
     */
    private Set<String> wildCardSet;
    /**
     * Flag indicating navigation cases properly consumed and available.
     */
    private boolean navigationConfigured;
    
	public NavigationHandlerImpl() {
	ApplicationAssociate associate = ApplicationAssociate.getInstance(
              FacesContext.getCurrentInstance().getExternalContext());
        if (associate != null) {
            caseListMap = associate.getNavigationCaseListMappings();
            wildCardSet = associate.getNavigationWildCardList();
            navigationConfigured = (wildCardSet != null &&
                                    caseListMap != null);
        }
    }
    
    NavigationHandlerImpl(ApplicationAssociate associate) {
        if (associate == null) {
            throw new NullPointerException();
        } else {
            caseListMap = associate.getNavigationCaseListMappings();
            wildCardSet = associate.getNavigationWildCardList();
            navigationConfigured = (wildCardSet != null &&
                                    caseListMap != null);
        }
    }
5.2获取跳转下一个画面视图路径,Method「handleNavigation」
	//fromAction为跳转画面Action接口名
	//outcome为跳转下一个画面key
    public void handleNavigation(FacesContext context, String fromAction,
                                 String outcome) {
        if (context == null) {
            String message = MessageUtils.getExceptionMessageString
                (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
            throw new NullPointerException(message);
        }
        //outcome等于null,直接跳过
        if (outcome == null) {
            return; // Explicitly remain on the current view
        }
        //获取视图对象CaseStruct
        CaseStruct caseStruct = getViewId(context, fromAction, outcome);
        ExternalContext extContext = context.getExternalContext();
        if (caseStruct != null) {
            ViewHandler viewHandler = Util.getViewHandler(context);
            assert (null != viewHandler);

            if (caseStruct.navCase.hasRedirect()) {
                // perform a 302 redirect.
                String newPath =
                    viewHandler.getActionURL(context, caseStruct.viewId);
                try {
                    // encode the redirect to ensure session state
                    // is maintained
                    extContext.redirect(extContext.encodeActionURL(newPath));
                } catch (java.io.IOException ioe) {
                    throw new FacesException(ioe.getMessage(), ioe);
                }
                context.responseComplete();
            } else {
                UIViewRoot newRoot = viewHandler.createView(context,
                                                            caseStruct.viewId);
                context.setViewRoot(newRoot);
            }
        }
    }
5.3获取视图对象CaseStruct,Method「getViewId」
    private CaseStruct getViewId(FacesContext context, String fromAction,
                                 String outcome) {
        //获取FacesContext上下文根节点view
        UIViewRoot root = context.getViewRoot();
        String viewId = (root != null ? root.getViewId() : null);
        
        // if viewId is not null, use its value to find
        // a navigation match, otherwise look for a match
        // based soley on the fromAction and outcome
        CaseStruct caseStruct = null;
        if (viewId != null) {
        	//根节点viewId,从caseListMap获取List<ConfigNavigationCase>
        	//相当于获取多个<navigation-case>标签
            caseStruct = findExactMatch(viewId, fromAction, outcome);

            if (caseStruct == null) {
                caseStruct = findWildCardMatch(viewId, fromAction, outcome);
            }
        }

        if (caseStruct == null) {
            caseStruct = findDefaultMatch(fromAction, outcome);
        }

        if (caseStruct == null && logger.isLoggable(Level.WARNING)) {
            if (fromAction == null) {
            } else {
            }
        }
        return caseStruct;
    }
5.4在caseListMap查询根节点viewId,Method「findExactMatch」
    private CaseStruct findExactMatch(String viewId,
                                      String fromAction,
                                      String outcome) {

        if (!navigationConfigured) {
            return null;
        }
        List<ConfigNavigationCase> caseList = caseListMap.get(viewId);

        if (caseList == null) {
            return null;
        }
        //匹配fromAction和outcome,并返回faces-config.xml结果
        return determineViewFromActionOutcome(caseList, fromAction, outcome);
    }
5.5在wildCardSet中查询fromViewId标签值以「*」结尾,Method「findWildCardMatch」
    private CaseStruct findWildCardMatch(String viewId,
                                         String fromAction,
                                         String outcome) {
        CaseStruct result = null;
        if (!navigationConfigured) {
            return null;
        }

        for (String fromViewId : wildCardSet) {

            if (!viewId.startsWith(fromViewId)) {
                continue;
            }
            String wcFromViewId = new StringBuilder(32).append(fromViewId).append('*').toString();
            List<ConfigNavigationCase> caseList = caseListMap.get(wcFromViewId);

            if (caseList == null) {
                return null;
            }
            //匹配fromAction和outcome,并返回faces-config.xml结果
            result = determineViewFromActionOutcome(caseList, fromAction, outcome);
            if (result != null) {
                break;
            }
        }
        return result;
    }
5.6在caseListMap查询key为「*」,Method「findDefaultMatch」
    private CaseStruct findDefaultMatch(String fromAction,
                                        String outcome) {
        if (!navigationConfigured) {
            return null;
        }
        List<ConfigNavigationCase> caseList = caseListMap.get("*");

        if (caseList == null) {
            return null;
        }
        //匹配fromAction和outcome,并返回faces-config.xml结果
        return determineViewFromActionOutcome(caseList, fromAction, outcome);
    }
5.7匹配fromAction和outcome,并返回faces-config.xml结果
    private CaseStruct determineViewFromActionOutcome(List<ConfigNavigationCase> caseList,
                                                      String fromAction,
                                                      String outcome) {
		//遍历List<ConfigNavigationCase>
		//相当于遍历<navigation-case>标签
        CaseStruct result = new CaseStruct();
        for (ConfigNavigationCase cnc : caseList) {
            String cncFromAction = cnc.getFromAction();
            String fromOutcome = cnc.getFromOutcome();
            String toViewId = cnc.getToViewId();
            //<from-action>#{rc_Login.CloseAction}</from-action>不等于空
            //<from-outcome>CLOSE</from-outcome>不等于空
            if ((cncFromAction != null) && (fromOutcome != null)) {
            	//匹配当前Action接口和跳转key必须相等
                if ((cncFromAction.equals(fromAction)) &&
                    (fromOutcome.equals(outcome))) {
                    result.viewId = toViewId;
                    result.navCase = cnc;
                    return result;
                }
            }
			//<from-action></from-action>等于空
			//<from-outcome>HomePage</from-outcome>不等于空
             if ((cncFromAction == null) && (fromOutcome != null)) {
                if (fromOutcome.equals(outcome)) {
                    result.viewId = toViewId;
                    result.navCase = cnc;
                    return result;
                }
            }
            //<from-action>#{rc_Login.CloseAction}</from-action>不等于空
            //<from-outcome></from-outcome>等于空
            if ((cncFromAction != null) && (fromOutcome == null)) {
            	//匹配当前Action接口相等
                if (cncFromAction.equals(fromAction)) {
                    result.viewId = toViewId;
                    result.navCase = cnc;
                    return result;
                }
            }
			 //<from-action></from-action>等于空
            //<from-outcome></from-outcome>等于空
            if ((cncFromAction == null) && (fromOutcome == null)) {
                result.viewId = toViewId;
                result.navCase = cnc;
                return result;
            }
        }
        return null;
    }
5.8内部类CaseStruct,存储跳转画面key和action接口与faces-config.xml进行匹配后,存放对象
    private static class CaseStruct {
        String viewId;
        ConfigNavigationCase navCase;
    }
6.ActionListenerImpl类,Action监听,跳转下一个视图并调用NavigationHandlerImplhandleNavigation方法
    @SuppressWarnings("deprecation")
    public void processAction(ActionEvent event) {

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine(MessageFormat.format("processAction({0})",
                                             event.getComponent().getId()));
        }
        UIComponent source = event.getComponent();
        ActionSource actionSource = (ActionSource) source;
        FacesContext context = FacesContext.getCurrentInstance();

        Application application = context.getApplication();

        Object invokeResult;
        String outcome = null;
        MethodBinding binding;

        binding = actionSource.getAction();
        if (binding != null) {
            try {
                if (null != (invokeResult = binding.invoke(context, null))) {
                    outcome = invokeResult.toString();
                }
                // else, default to null, as assigned above.
            } catch (MethodNotFoundException e) {
                if (LOGGER.isLoggable(Level.SEVERE)) {
                    LOGGER.log(Level.SEVERE, e.getMessage(), e);
                }
                throw new FacesException
                      (binding.getExpressionString() + ": " + e.getMessage(),
                       e);
            }
            catch (EvaluationException e) {
                if (LOGGER.isLoggable(Level.SEVERE)) {
                    LOGGER.log(Level.SEVERE, e.getMessage(), e);
                }
                throw new FacesException
                      (binding.getExpressionString() + ": " + e.getMessage(),
                       e);
            }
        }

        // Retrieve the NavigationHandler instance..

        NavigationHandler navHandler = application.getNavigationHandler();

        // Invoke nav handling..
        navHandler.handleNavigation(context,
                                    (null != binding) ?
                                    binding.getExpressionString() : null,
                                    outcome);

        // Trigger a switch to Render Response if needed
        context.renderResponse();

    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值