从年前到年后,前前后后断断续续写了差不多一个多月的框架终于大功告成啦~
借鉴了springMVC的设计原理和相当多的设计模式。
这一章我们先看一下在HTTP请求发来之前的准备工作,首先当然是MVC本身需要持有的容器。下面是代码
package webAppContext;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContext;
import handlerExecutionChain.HandlerChain;
import handlerExecutionChain.HandlerInterceptor;
public class ContextLoader {
private static String pack;
private static final String HANDLERCHAIN="chain";
private static final String HANDLERS="handlers";
private static volatile ServletContext sc;
private Set<String> classUrl=new LinkedHashSet<String> ();
private static HandlerChain chain=HandlerChain.getInstance();
public ContextLoader(){
}
//属性注入,配置package
public static void setPack(String pack) {
ContextLoader.pack = pack;
}
private static List<HandlerInterceptor> getChain() throws Exception{
return chain.getChain();
}
public Class<?> loadCon(String cla) throws ClassNotFoundException{
return Class.forName(cla);
}
//单例模式,确保一个webAPP只拥有一个web容器
public static ServletContext createContext() throws Exception{
ServletContext uniqueSc = null;
if(sc==null){
synchronized(ServletContext.class){
if(sc==null){
uniqueSc=initWebContext(pack);
}
}
}
return uniqueSc;
}
private static ServletContext initWebContext(String pack) throws Exception{
sc.setAttribute(HANDLERS, new ReqMap().registerHandlers(pack));
sc.setAttribute(HANDLERCHAIN, getChain());
return sc;
}
//将包中的类全部找出来
public Set<String> findCon(String packageName){
String pack=packageName.replace('.', '/');
Enumeration<URL> urls=null;
try {
urls=Thread.currentThread().getContextClassLoader().getResources(pack);
while(urls.hasMoreElements()){
String url=(String) urls.nextElement().getContent();
classUrl.add(url);
}
} catch (IOException e) {
e.printStackTrace();
}
return classUrl;
}
}
这里的工作是扫描并加载包文件下的所需要的类并把所有的拦截器和URL-handlerWrapper映射放入ServletContext这个web容器之中。
那么什么是URL-handlerWrapper呢?
GothaMVC通过给开发者提供@Controller注解的方式让开发者配置Controller的处理方法Method和@Url注解的方式让开发者在方法级别上进行配置需要筛选的URL。
在开发过程中我遇到过一个难题,我本来是想将控制器类里的URL和其处理方法Method绑定在一起的。因为在开发过程中或许开发者不会把所有的控制器方法都放在一个Controller当中,所以问题便随之产生了,就是当需要通过反射来调用这个Method的时候会出现因为找不到Method所在的类而无法调用。
所以我想了一个办法,就是将这个method所在类和他的参数这三个属性全部封装在另一个类中HandlerWrapper。
然后通过将URL和handlerWrapper绑定起来放入一个hashmap当中,这样一来问题就解决了。下面是HandlerWrapper类的代码
package webAppContext;
import java.lang.reflect.Method;
public class HandlerWrapper {
private Object obj;
private Method m;
private Object[] args;
public HandlerWrapper(Object obj,Method m,Object[] args){
this.obj=obj;
this.m=m;
this.args=args;
}
public HandlerWrapper(Object obj,Method m){
this(obj,m,null);
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Method getM() {
return m;
}
public void setM(Method m) {
this.m = m;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
}
写好handlerWrapper之后,我们对其和相对的URL进行绑定。
下面代码是扫描包文件并将相应的URL和handlerwrapper作映射并注册。
package webAppContext;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import annotation.Controller;
import annotation.Url;
public class ReqMap {
private Map<String,HandlerWrapper> reqMap=new HashMap<String,HandlerWrapper> ();
private ContextLoader cl=new ContextLoader();
//扫描并加载包
public Map<String,HandlerWrapper> registerHandlers(String packageName) throws Exception {
Set<String> set=cl.findCon(packageName);
Iterator<String> iter=set.iterator();
while(iter.hasNext()){
String cla=iter.next();
synchronized(this){
Class<?> clazz=cl.loadCon(cla);
if(clazz.isAnnotationPresent(Controller.class)&&clazz.isAnnotationPresent(Url.class)){
registerURL(clazz);
}
}
}
return reqMap;
}
//将注解的URL和处理方法注册在映射表中
private void registerURL(Class<?> cla) throws Exception{
Method[] ms=cla.getMethods();
for(Method m:ms){
String url=m.getAnnotation(Url.class).value();
if(reqMap.containsKey(url)){
throw new Exception("there is already a stored url");
}else{
reqMap.put(url, new HandlerWrapper(cla,m,m.getParameterTypes()));
}
}
}
}