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通过关联 UrlMatcher与 Action,就可以匹配到合适的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());
}
以上就是整个的处理流程部分.
浙公网安备 33010602011771号