MVC框架原理(简单模拟)

一个简单的MVC框架

1、ActionMap,action name与action object之间的映射

public class ActionMap {

private Map<String, Object> actionMap = new HashMap<String, Object>();

public Object getAction(String actionName){
return actionMap.get(actionName);
}

public void initFromProperties(Configuration conf) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
System.out.println("Init actionMap from Properties " + conf.getFileName() + " ...");
Set<Entry<Object, Object>> keyValues = conf.entrySet();
for(Entry<Object, Object> keyValue : keyValues){
this.actionMap.put(keyValue.getKey().toString(), Class.forName(keyValue.getValue().toString()).newInstance());

}
System.out.println("Init actionMap success, Total size " + this.actionMap.size());
}
}

2、NormalUrl是用来解析url中的Servlet path中的action name与method name的(本来还有一个RestfulUrl的,但又不想在这里实现),约定为 http://www.unclepeng.com:XXXX/appName/actionName/methodName/...
public class NormalUrl { 

public static final String  DEFAULT_METHOD_NAME = "index"; 

private String actionName; 

private String methodName; 

private int level = 2; 

public String getActionName() { 
return actionName; 
} 

public String getMethodName() { 
return methodName; 
} 

public static NormalUrl fromServletPath(String str){ 
if(str.startsWith("/")){ 
str = str.substring(1); 
} 
String[] strs = StringUtils.split(str, '/', true); 
NormalUrl url = new NormalUrl(); 
if(strs == null || strs.length == 0){ 
throw new IllegalArgumentException(String.format("Illegal Servlet path %s", str)); 
} 
url.actionName = StringUtils.split(strs[0], '.', true)[0]; 
if(strs.length > 1){ 
url.methodName = StringUtils.split(strs[1], '.', true)[0]; 
}else{ 
url.methodName = DEFAULT_METHOD_NAME; 
url.level = 1; 
} 
return url; 
} 

public int getLevel() { 
return level; 
} 

public void setLevel(int level) { 
this.level = level; 
} 
} 
[、code]
3、核心DispatcherServlet,在init的时候初始化actionMap,在doGet方法中根据servlet path解析出对应的actionName与methodName,再找到对应的Action Object,然后invoke method 
public class DispatcherServlet extends HttpServlet { 

private static final long serialVersionUID = - 8330920476525405610L; 

    public static final String INCLUDE_PATH_INFO = 
        "javax.servlet.include.path_info"; 

public DispatcherServlet() { 
super(); 
} 

private ActionMap actionMap = new ActionMap(); 

public static int FILE_NOT_FOUND = 404; 

public void destroy() { 
super.destroy(); 
this.actionMap = null; 
} 

public void doGet(HttpServletRequest request, HttpServletResponse response) 
throws ServletException, IOException { 
//Split URL 
String path = request.getServletPath(); 

NormalUrl url = NormalUrl.fromServletPath(path); 
String actionName = url.getActionName(); 
String methodName = url.getMethodName(); 

//Get action by name, if not found, response 404 
Object action = this.actionMap.getAction(actionName); 
if(action == null){//action not found 
System.err.println(String.format("Action named '%s' not found", actionName)); 
response.sendError(FILE_NOT_FOUND); 
} 

//Invoke action method, and forward if invoke result not returns null 
try{ 
Method method = action.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); 
Object o = method.invoke(action, new Object[]{request, response}); 
if(o!= null){ 
if(url.getLevel() == 2){ 
request.getRequestDispatcher("../" + (String)o).forward(request, response); 
}else{ 
request.getRequestDispatcher((String)o).forward(request, response); 
} 
return; 
} 
}catch(Exception e){ 
e.printStackTrace(); 
response.sendError(FILE_NOT_FOUND); 
return; 
} 
} 

public void doPut(HttpServletRequest request, HttpServletResponse response) 
throws ServletException, IOException { 
doGet(request, response); 
} 

public void doDelete(HttpServletRequest request, HttpServletResponse response) 
throws ServletException, IOException { 
doGet(request, response); 
} 

public void doPost(HttpServletRequest request, HttpServletResponse response) 
throws ServletException, IOException { 
doGet(request, response); 
} 

public void init() throws ServletException { 
        ServletConfig servletConfig = this.getServletConfig();          
        String mvcConfigFilePath = servletConfig.getInitParameter("mvc_config");  
        Configuration conf = null; 
        if(mvcConfigFilePath != null){//case 1, use properites configuration 
        try { 
conf = new PropertiesConfiguration(mvcConfigFilePath); 
actionMap.initFromProperties(conf); 
return; 
} catch (Exception e) { 
e.printStackTrace(); 
System.exit(1); 
} 
        }else{//case 2, use packages setting 
        String realPath = this.getServletContext().getRealPath("/"); 
        String packagesPath = servletConfig.getInitParameter("action_packages"); 
        String[] packagePathArray = StringUtils.split(packagesPath, ',', true); 
        for(String packagePath : packagePathArray){ 
        
        } 
        } 
} 

} 

4、web.xml配置
<?xml version="1.0" encoding="UTF-8"?> 
<web-app version="2.5" 
xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 
<servlet>   
    <servlet-name>Dispatcher</servlet-name>   
    <servlet-class>com.upeng.webcommons.mvc.DispatcherServlet</servlet-class>   
    <init-param>   
        <param-name>mvc_config</param-name>   
        <param-value>mvc.properties</param-value>   
    </init-param>   
  </servlet>   
  <servlet-mapping>   
    <servlet-name>Dispatcher</servlet-name>   
    <url-pattern>*.do</url-pattern>   
  </servlet-mapping> 
</web-app> 

5、示例Action
public class ProductAction { 

public String index(HttpServletRequest request, HttpServletResponse response){ 
return "product.jsp"; 
} 
} 


6、mvc.properties配置
product =com.upeng.webcommons.test.ProductAction 

7、Test
http://localhost:222/webcommons/product/index.do 或者 http://localhost:222/webcommons/product.do,其中的product为action名,index为action中的方法名,当没指定方法时,默认选择action中的index方法

总结与选型过程:
1、本来想实现基于类的MVC,类似struts2,然后把对应的url parameter注入到action的field中,这样做好处是能跟servletAPI完全解耦,坏处是为每次请求以反射的方式生成一个临时的action object,效率上要打些折扣。

2、也想过实现类似mvc.net 1.0中的把action实现成sigton,然后实现基于方法的MVC把对应的url parameter注入到方法的参数里然后回调,这样做要求action中不能有重名的方法,另外在查找方法的时候要做一次遍历,稍微比直接指定method name跟parameterTypes式的查找慢了那么一点。
但是还需要把string型的url parameters转型为方法参数对应的类型效率上又打了一点点的折扣。综合考虑下便又打消了这个念头

3、这还只是一个MVC雏形,初步实现了url到action乃到action中的method之间的mapping,当然,也可以添加对参数检验,错误处理、国际化等的支持。

你可能感兴趣的:(框架,xml,mvc,jsp,servlet)