URL匹配部分:

 

UrlMatcher:用于将 @Mapping中包含 $1$2……的字符串变为正则表达式,进行预编译,并检查参数个数是否符合方法参数

看构造函数:输入一个@Mapping中包含 $1$2……url字符串,开始构造表达式

 

public UrlMatcher(String url) {
        this.url = url;
        StringBuilder sb = new StringBuilder(url.length() + 20);
        sb.append('^');
        List<Integer> paramList = new ArrayList<Integer>();
        Set<Integer> paramSet = new HashSet<Integer>();
        int start = 0;
//扫描, 完成url的翻译以及参数抽取
        for (;;) {
            int n = url.indexOf('$', start);
            if (n!=(-1) && n<url.length()-1 && isParamIndex(url.charAt(n+1))) {
                // get index:
                int i = url.charAt(n+1) - '0';
                // $x found:
                paramSet.add(i);
                paramList.add(i);
                addExactMatch(sb, url.substring(start, n));
                addParameterMatch(sb);
                start = n + 2;
            }
            else {
                // $x not found!
                addExactMatch(sb, url.substring(start, url.length()));
                break;
            }
        }
        // check parameters:
        if (paramList.size()!=paramSet.size())
            throw new ConfigException("Duplicate parameters.");
        for (int i=1; i<=paramSet.size(); i++) {
            if (!paramSet.contains(i))
                throw new ConfigException("Missing parameter '$" + i + "'.");
        }
        this.orders = new int[paramList.size()];
        for (int i=0; i<paramList.size(); i++) {
            this.orders[i] = paramList.get(i) - 1;
        }
        sb.append('$');
//编译一个正则表达式模式
        this.pattern = Pattern.compile(sb.toString());
    }

 

 

getMatchedParameters方法:

输入一个请求的url,看是否和当前的模式匹配.如果匹配了,则返回请求中包含的参数.

 

public String[] getMatchedParameters(String url) {
		Matcher m = pattern.matcher(url);
		if (!m.matches())
			return null;
		if (orders.length == 0)
			return EMPTY_STRINGS;
		String[] params = new String[orders.length];
		for (int i = 0; i < orders.length; i++) {
			params[orders[i]] = m.group(i + 1);
		}
		return params;
	}
 

 

当符合了之后,需要调用一个具体的处理,这个处理其实就是一个类的方法(@Mapping标记),需要一个类去调用这个方法:

Action:

持有了对象的instance,方法以及参数类型:

public Action(Object instance, Method method) {
        this.instance = instance;
        this.method = method;
        this.arguments = method.getParameterTypes();
    }
 

 

负责请求转发的Dispatcher通过关联 UrlMatcherAction,就可以匹配到合适的URL,并转发给相应的Action

 

Dispatcher:

dispatcher类处理所有的客户请求,然后把每个请求派发给相应的处理方法去处理.

 

初始化方法:

 

void initAll(Config config) throws Exception {
        // detect multipart support:
        try {
//初始化文件上传, 如果不存在这个类, 则会抛异常, 这样的话, 系统就不支持multipartSupport.
//这种处理手法非常适合支持组件
            Class.forName("org.apache.commons.fileupload.servlet.ServletFileUpload");
            this.multipartSupport = true;
            log.info("Using CommonsFileUpload to handle multipart http request.");
            String maxFileSize = config.getInitParameter("maxFileSize");
            if (maxFileSize!=null) {
                try {
                    long n = Long.parseLong(maxFileSize);
                    if (n<=0)
                        throw new NumberFormatException();
                    this.maxFileSize = n;
                }
                catch (NumberFormatException e) {
                    log.warn("Invalid parameter <maxFileSize> value '" + maxFileSize + "', using default.");
                }
            }
        }
        catch (ClassNotFoundException e) {
            log.info("CommonsFileUpload not found. Multipart http request can not be handled.");
        }

//获取container的名字
        // init IoC container:
        String containerName = config.getInitParameter("container");
        if (containerName==null)
            throw new ConfigException("Missing init parameter <container>.");
//创建一个containerFactory
        this.containerFactory = Utils.createContainerFactory(containerName);
//初始化containerFactory, 必须保证, 这个类是单例, 否则的话, containerFactory 会有多份.
        this.containerFactory.init(config);
        List<Object> beans = this.containerFactory.findAllBeans();
//初始化所有的组件
        initComponents(beans);
//初始化模板
        initTemplateFactory(config);
    }

 

 

其中,initComponents方法:

//这里的beans, 就是所有带有@Mapped方法的类(具体的Action)
void initComponents(List<Object> beans) {
        List<Interceptor> intList = new ArrayList<Interceptor>();
//如果是Interceptor的话, 则加入到Interceptor队列中
        for (Object bean : beans) {
            if (bean instanceof Interceptor)
                intList.add((Interceptor)bean);
            if (this.exceptionHandler==null && bean instanceof ExceptionHandler)
                this.exceptionHandler = (ExceptionHandler) bean;
            addActions(bean);
        }
//定义一个处理异常的handler, 这个是非常好的. 可以专门一个异常模板去渲染
        if (this.exceptionHandler==null)
            this.exceptionHandler = new DefaultExceptionHandler();
        this.interceptors = intList.toArray(new Interceptor[intList.size()]);
//通过InterceptorOrder标记, 来排序.
//一个链式的处理, 必须要有序
        Arrays.sort(
                this.interceptors,
                new Comparator<Interceptor>() {
                    public int compare(Interceptor i1, Interceptor i2) {
                        InterceptorOrder o1 = i1.getClass().getAnnotation(InterceptorOrder.class);
                        InterceptorOrder o2 = i2.getClass().getAnnotation(InterceptorOrder.class);
                        int n1 = o1==null ? Integer.MAX_VALUE : o1.value();
                        int n2 = o2==null ? Integer.MAX_VALUE : o2.value();
                        if (n1==n2)
                            return i1.getClass().getName().compareTo(i2.getClass().getName());
                        return n1<n2 ? (-1) : 1;
                    }
                }
        );
        this.urlMatchers = urlMap.keySet().toArray(new UrlMatcher[urlMap.size()]);
//对urlMather按url排序
        Arrays.sort(
                this.urlMatchers,
                new Comparator<UrlMatcher>() {
                    public int compare(UrlMatcher o1, UrlMatcher o2) {
                        String u1 = o1.url;
                        String u2 = o2.url;
                        int n = u1.compareTo(u2);
                        if (n==0)
                            throw new ConfigException("Cannot mapping one url '" + u1 + "' to more than one action method.");
                        return n;
                    }
                }
        );
    }


//找出action类的所有标注方法放到Map中
void addActions(Object bean) {
        Class<?> clazz = bean.getClass();
        Method[] ms = clazz.getMethods();
        for (Method m : ms) {
            if (isActionMethod(m)) {
                Mapping mapping = m.getAnnotation(Mapping.class);
//获取被标记的路径
                String url = mapping.value();
                UrlMatcher matcher = new UrlMatcher(url);
                if (matcher.getArgumentCount()!=m.getParameterTypes().length) {
                    warnInvalidActionMethod(m, "Arguments in URL '" + url + "' does not match the arguments of method.");
                    continue;
                }
                log.info("Mapping url '" + url + "' to method '" + m.toGenericString() + "'.");
                urlMap.put(matcher, new Action(bean, m));
            }
        }
    }

 

//判断一个方法是不是被标记的
boolean isActionMethod(Method m) {
//获取一个方法的Mapping标记
        Mapping mapping = m.getAnnotation(Mapping.class);
        if (mapping==null)
            return false;
        if (mapping.value().length()==0) {
            warnInvalidActionMethod(m, "Url mapping cannot be empty.");
            return false;
        }
        if (Modifier.isStatic(m.getModifiers())) {
            warnInvalidActionMethod(m, "method is static.");
            return false;
        }
        Class<?>[] argTypes = m.getParameterTypes();
        for (Class<?> argType : argTypes) {
            if (!converterFactory.canConvert(argType)) {
                warnInvalidActionMethod(m, "unsupported parameter '" + argType.getName() + "'.");
                return false;
            }
        }
//判断方法的返回类型: 根据作者的意图, 一共有三种类型的处理方法: void型, String型以及Render型
//所有其他的返回类型都不支持
        Class<?> retType = m.getReturnType();
        if (retType.equals(void.class)
                || retType.equals(String.class)
                || Renderer.class.isAssignableFrom(retType)
        )
            return true;
        warnInvalidActionMethod(m, "unsupported return type '" + retType.getName() + "'.");
        return false;
    }

 

看一下dispatcher是如何处理客户请求的:
public boolean service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//得到请求的URI, 比如/article/200910/
        String url = req.getRequestURI();
//得到项目的名字, 比如http://localhost/alexlog/article/200910/ ==> alexlog
        String path = req.getContextPath();
//获取真实请求URI
        if (path.length()>0)
            url = url.substring(path.length());
        // set default character encoding to "utf-8" if encoding is not set:
        if (req.getCharacterEncoding()==null)
            req.setCharacterEncoding("UTF-8");
        if (log.isDebugEnabled())
            log.debug("Handle for URL: " + url);
        Execution execution = null;
//遍历所有的urlMather, 查找符合的处理, 只找第一个, 因为不可能有多个, 这不是filterchain
        for (UrlMatcher matcher : this.urlMatchers) {
            String[] args = matcher.getMatchedParameters(url);
//有匹配的处理
            if (args!=null) {
//从urlMap中根据mathcer来获取对应的action
                Action action = urlMap.get(matcher);
                Object[] arguments = new Object[args.length];
                for (int i=0; i<args.length; i++) {
                    Class<?> type = action.arguments[i];
                    if (type.equals(String.class))
                        arguments[i] = args[i];
                    else
                        arguments[i] = converterFactory.convert(type, args[i]);
                }
//构造一个执行者
                execution = new Execution(req, resp, action, arguments);
                break;
            }
        }
        if (execution!=null) {
//处理执行
            handleExecution(execution, req, resp);
        }
        return execution!=null;
    }

最后看如何执行action:


void handleExecution(Execution execution, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//如果支持上传, 把当前的request重新包装成一个MultipartHttpServletRequest类.
        if (this.multipartSupport) {
            if (MultipartHttpServletRequest.isMultipartRequest(request)) {
                request = new MultipartHttpServletRequest(request, maxFileSize);
            }
        }
//把request和response封装到ThreadLocal中, 供其他类使用
        ActionContext.setActionContext(servletContext, request, response);
//处理InterceptorChain
        try {
            InterceptorChainImpl chains = new InterceptorChainImpl(interceptors);
//从第一个interceptor 开始处理, 当最后一个拦截器被调用后,InterceptorChain 才真正调用 Execution 对象的 execute() 方法,
//并保存其返回结果,整个请求处理过程结束,进入渲染阶段。
            chains.doInterceptor(execution);
//开始渲染
            handleResult(request, response, chains.getResult());
        }
        catch (Exception e) {
            handleException(request, response, e);
        }
        finally {
            ActionContext.removeActionContext();
        }
    }

 


void handleResult(HttpServletRequest request, HttpServletResponse response, Object result) throws Exception {
        if (result==null)
            return;
//如果是Renderer的话, 使用Renderer来渲染
        if (result instanceof Renderer) {
            Renderer r = (Renderer) result;
            r.render(this.servletContext, request, response);
            return;
        }
//如果是String的话
        if (result instanceof String) {
            String s = (String) result;
//如果开头是redirect的话, 那么做转发, 比如:
//@Mapping("/register") 
//String register() { 
//    ... 
//    if (success) 
//        return "redirect:/reg/success"; 
//    return "redirect:/reg/failed"; 
//} 
            if (s.startsWith("redirect:")) {
                response.sendRedirect(s.substring("redirect:".length()));
                return;
            }
//如果开头是script的话, 那么就使用JavaScriptRenderer去渲染
            if (s.startsWith("script:")) {
                String script = s.substring("script:".length());
                new JavaScriptRenderer(script).render(servletContext, request, response);
                return;
            }
//默认使用Text去渲染
            new TextRenderer(s).render(servletContext, request, response);
            return;
        }
        throw new ServletException("Cannot handle result with type '" + result.getClass().getName() + "'.");
    }

 

看一下Execution:


// 调用 execute() 方法就可以执行目标方法,并返回一个结果。
public Object execute() throws Exception {
        try {
            return action.method.invoke(action.instance, args);
        }
        catch (InvocationTargetException e) {
//请注意,当通过反射调用方法失败时,我们通过查找 InvocationTargetException 的根异常并将其抛出,
//这样,客户端就能捕获正确的原始异常。
            Throwable t = e.getCause();
            if (t!=null && t instanceof Exception)
                throw (Exception) t;
            throw e;
        }
    }

 

StaticFileHandler:处理静态文件:


处理主函数:
public void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String url = request.getRequestURI();
        String path = request.getContextPath();
        url = url.substring(path.length());
//处理静态文件时要过滤 /WEB-INF/ 目录,否则将造成安全漏洞。
//WEB-INF内部的内容不允许访问
        if (url.toUpperCase().startsWith("/WEB-INF/")) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
//处理 /xxx.html?yy=zz这种资源
        int n = url.indexOf('?');
        if (n!=(-1))
            url = url.substring(0, n);
//处理 /xxx.html#1这种资源
        n = url.indexOf('#');
        if (n!=(-1))
            url = url.substring(0, n);
//获取资源名
        File f = new File(servletContext.getRealPath(url));

//如果找不到资源, 则返回404
        if (! f.isFile()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

//设置ETag
        long ifModifiedSince = request.getDateHeader("If-Modified-Since");
        long lastModified = f.lastModified();
        if (ifModifiedSince!=(-1) && ifModifiedSince>=lastModified) {
            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }
        response.setDateHeader("Last-Modified", lastModified);
        response.setContentLength((int)f.length());
        // set cache:
        if (expires<0) {
            response.setHeader("Cache-Control", "no-cache");
        }
        else if (expires>0) {
            response.setHeader("Cache-Control", maxAge);
            response.setDateHeader("Expires", System.currentTimeMillis() + expires);
        }
        // should download?
//        String name = request.getParameter("_download");
//        if (name!=null) {
//            resp.setContentType(MIME_OCTET_STREAM);
//            resp.setHeader("Content-disposition", "attachment; filename=" + name);
//        }
        response.setContentType(getMimeType(f));
//把文件的内容写入到response的流中
        sendFile(f, response.getOutputStream());
    }

以上就是整个的处理流程部分.

posted on 2010-12-16 00:12  菊次郎  阅读(334)  评论(0)    收藏  举报