拿起笔来做刀枪 · 之四 再造一个struts

首先,我们在web.xml中定义一个servlet,

	<servlet>
		<servlet-name>Mvc4meServlet</servlet-name>
		<servlet-class>net.csdn.blog.deltatang.mvc4me.Mvc4meServlet</servlet-class>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>Mvc4meServlet</servlet-name>
		<url-pattern>/mvc/*</url-pattern>
	</servlet-mapping>

然后做一个实现:

package net.csdn.blog.deltatang.mvc4me;

public class Mvc4meServlet extends HttpServlet {
	

	public void init(ServletConfig config) throws ServletException {
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		String uri = request.getRequestURI();
		
		//根据uri取得对应的Controller
		Controller controller = getControllerByUriMapping(uri);
		
		Map<String, Object> modelAndView = controller.process(request);

		String jspPath = controller.getJspPath();
		String classPath = controller.getClassPath();		
		
		JspHandler handler = JspCompiler.compile(jspPath, classPath);
		
		String content = handler.buildContent(modelAndView);
		
		response.getWriter().println(content);
	}

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

}

请注意,这里面 jspPath, classPath 开始的流程是从上一个章节《再造一个jsp》从直接引用而来。

我们新定义了一个 Controller,希望它能提供一切我们需要的数据,用以让程序正常执行。

当然,因为我的开发方式是,先确定需求,在去做实现,因此,在IDE中,代码现在的表现是这样的:

没有关系,让我们使用IDE的帮助工具,迅速构建:



我们得到了接口:

package net.csdn.blog.deltatang.mvc4me;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

interface Controller {

	public Map<String, Object> process(HttpServletRequest request);

	public String getJspPath();

	public String getClassPath();

}

现在,不急着实现这个接口,我们继续从需求入手,梳理我们的想法:

我们需要一个配置文件,类似于spring.xml,在这个配置文件中,我们需要定义一个类,

提供他的 uri(为了简便起见,我们用bean id 代替),类名,对应的jsp模板文件,类路径信息。

有了这些信息,我们就能够提供 我们的主分发器

Mvc4meServlet 
足够的数据。

先定义xml格式如下:

<beans >

	<bean id=="/mvc/search" view="./search.template.jsp" class="net.csdn.blog.deltatang.mvc4me.SearchCtrol"/>

</beans>

接着,是实现Mvc4meServlet中 getControllerByUriMapping 方法的时候了:)

别忘了,我们已经实现了dom4me 和 spring4me,因此, 剩下的过程是一个 weave的过程。

通缉令/刺客联盟 里面的 weaver 是很吊的说:)

再来看BeanFactory,我们遇到了一个麻烦,因为它的简陋,我们并没有进行传递属性的操作,因此,

<bean id=="/mvc/search" view="./search.template.jsp" class="net.csdn.blog.deltatang.mvc4me.SearchCtrol"/>

中的view属性无法传递给controller实例,

解决方式有两种:第一、丰富我们的spring4me,提供更多的xml构造类的方式,第二、修改我们的接口,在初始化之后做cast。

简便起见,我选择第二种,让我们重新定义controller接口:

package net.csdn.blog.deltatang.mvc4me;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

public abstract class Controller {
	
	protected String jspPath;
	
	protected String classPath;

	public abstract Map<String, Object> process(HttpServletRequest request);

	public String getJspPath() {
		return jspPath;
	}

	public void setJspPath(String jspPath) {
		this.jspPath = jspPath;
	}

	public String getClassPath() {
		return classPath;
	}

	public void setClassPath(String classPath) {
		this.classPath = classPath;
	}


}

以上将接口改造为一个抽象类,内置了 setter 方法,供BeanFactory调用设置属性。

然后,我们在BeanFactory 中的

public static Object getBean(String id)
方法返回之前,简单的做一个判断 obj 是否是 controller 的实例, 如果是,设置属性值:)

	public static Object getBean(String id) {
		
		Object obj = delegate_getBean(id);
		
		if(obj instanceof Controller) {
			
			Controller ctrl = (Controller) obj;
			
			Node n = domTree.getById(id);
			
			String jspPath = n.getAttributes().get("view");
			String classPath = n.getAttributes().get("class");
			
			ctrl.setJspPath(jspPath);
			ctrl.setClassPath(classPath);
		}
		
		return obj;
	}
		
        //原有的方法被改名为:
	public static Object delegate_getBean(String id) {

这样,我们的中心转发器Mvc4meServlet就完成了:

package net.csdn.blog.deltatang.mvc4me;

import net.csdn.blog.deltatang.dom4me.DomTree;
import net.csdn.blog.deltatang.dom4me.Node;
import net.csdn.blog.deltatang.dom4me.XmlParser;
import net.csdn.blog.deltatang.jsp4me.JspCompiler;
import net.csdn.blog.deltatang.jsp4me.JspHandler;
import net.csdn.blog.deltatang.spring4me.BeanFactory;

public class Mvc4meServlet extends HttpServlet {
	

	public void init(ServletConfig config) throws ServletException {
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		String uri = request.getRequestURI();
		
		//根据uri取得对应的Controller
		Controller controller = getControllerByUriMapping(uri);
		
		Map<String, Object> modelAndView = controller.process(request);

		String jspPath = controller.getJspPath();
		String classPath = controller.getClassPath() + "_JSP";    		
		
		JspHandler handler = JspCompiler.compile(jspPath, classPath);
		
		String content = handler.buildContent(modelAndView);
		
		response.getWriter().println(content);
	}
	
	private static Map<String, Controller> ctrlMapping = null; 

	private Controller getControllerByUriMapping(String uri) {
		return (Controller)BeanFactory.getBean(uri);
	}

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

}


现在,是我们实现Controller的时间了,还记得以前写的SearchServlet么?直接把中间的业务逻辑代码copy过来就好了:)

package net.csdn.blog.deltatang.mvc4me;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import cn.com.sitefromscrath.BeanFactory;
import cn.com.sitefromscrath.service.SearchService;

public class SearchCtrol extends Controller {

	@Override
	public Map<String, Object> process(HttpServletRequest request) {
		
		String keywords = request.getParameter("keywords");
		if(keywords == null) keywords = "";
		
		SearchService searchService = (SearchService)BeanFactory.getBean("searchService");	
		List results = searchService.search(keywords);	
		
		Map<String, Object> mov = new HashMap<String, Object>();
		
		mov.put("keywords", keywords);
		mov.put("results", results);
		
		return mov;
	}

}

最后一步,view模板,当然,因为我们的JavaSign Pages兼容 JavaServer Pages, 所以,简单的复制就可以了:)

当然,要注意,我们的jsp可是和web 容器分离的,所以 request 、page 是不能用的了,当然,好消息是:out可以用呢!

<%@page pageEncoding="UTF-8"%>
<%@page import="java.util.List"%>
<%@page import="cn.com.sitefromscrath.entity.Result"%>
<%
	String keywords = (String)request.getAttribute("keywords");	
	List results = (List)request.getAttribute("results");	
%>
改成最终的结果:
<%@page import="java.util.List"%>
<%@page import="cn.com.sitefromscrath.entity.Result"%>
<%
	String keywords = (String)context.get("keywords");	
	List results = (List)context.get("results");	
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  
  <body>
    <h1>search it!</h1>
    
    <form action="/mvc/search" method="get">
    	<input type="text" value="<%=keywords %>" name="keywords" />    
    	<input type="submit" value="search!" />
    </form>
    
    <%
    if(!results.isEmpty()) {
    %>
    <table border="1" bordercolor="grey" >
    	<%
    	for(int i = 0; i < results.size(); i++) {
    		Result result = (Result)results.get(i);
    	%>
    	<tr>
    		<td><%=result.title %></td><td><%=result.content %></td>
    	</tr>    	
    	<%
    	} 
    	%>    	    	    	
    </table>    
    <%
    } 
    %>
    
  </body>
</html>

好了 , run一把试试吧:)

问题大条了。。。

D:\dev\Tomcat 6.0\webapps\ROOT\WEB-INF\classes\net\csdn\blog\deltatang\mvc4me\SearchCtrol_JSP.java:3: 错误: 程序包net.csdn.blog.deltatang.jsp4me不存在
import net.csdn.blog.deltatang.jsp4me.JspHandler;
                                     ^
D:\dev\Tomcat 6.0\webapps\ROOT\WEB-INF\classes\net\csdn\blog\deltatang\mvc4me\SearchCtrol_JSP.java:5: 错误: 程序包cn.com.sitefromscrath.entity不存在
import cn.com.sitefromscrath.entity.Result;
                                   ^
D:\dev\Tomcat 6.0\webapps\ROOT\WEB-INF\classes\net\csdn\blog\deltatang\mvc4me\SearchCtrol_JSP.java:6: 错误: 找不到符号
public class SearchCtrol_JSP implements JspHandler {
                                        ^
  符号: 类 JspHandler
D:\dev\Tomcat 6.0\webapps\ROOT\WEB-INF\classes\net\csdn\blog\deltatang\mvc4me\SearchCtrol_JSP.java:29: 错误: 找不到符号
    		Result result = (Result)results.get(i);
    		^
  符号:   类 Result
  位置: 类 SearchCtrol_JSP
D:\dev\Tomcat 6.0\webapps\ROOT\WEB-INF\classes\net\csdn\blog\deltatang\mvc4me\SearchCtrol_JSP.java:29: 错误: 找不到符号

产生这个的原因,就是编译没有通过,因为classpath的设置有问题,没有包含我们部署的class文件,

另一方面,在运行的时候,classLoader 采用的是 system默认的,而不是tomcat 的 WebClassLoader,这样,显然在加载的时候(容器内加载)会出问题。

因此,我们做一个调整:

首先是 修改 Mvc4meServlet

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		String uri = request.getRequestURI();
		
		//根据uri取得对应的Controller
		Controller controller = getControllerByUriMapping(uri);
		
		Map<String, Object> modelAndView = controller.process(request);

		String jspPath = controller.getJspPath();
		String classPath = controller.getClassPath() + "_JSP";		
		
//		JspHandler handler = JspCompiler.compile(jspPath, classPath);
		ClassLoader classLoader = this.getClass().getClassLoader();
		JspHandler handler = JspCompiler.compile(jspPath, classPath, classLoader);
		
		String content = handler.buildContent(modelAndView);
		
		response.getWriter().println(content);
	}

我们获得web容器的classLoader,作为参数船体给 JspCompiler。当然,

JspCompiler.compile
也需要同步调整:

	public static JspHandler compile(String jspPath, String classPath, ClassLoader classLoader) {
		
		String className = classPath.substring(classPath.lastIndexOf('.') + 1);
		String source = new JspToJavaLangBuilder().convert(jspPath, className);
		
		System.out.println(source);
		
		StringToObjectBuilder builder = new StringToObjectBuilder();
		JspHandler handler = (JspHandler)builder.build(source, classPath, classLoader);
		
		return handler;
	}

最后,到达了我们的真正实现类StringToObjectBuilder

	public Object build(String source, String classPath, ClassLoader classLoader) {
		
		String javaFilePath = classPath.replaceAll("\\.", "/") + ".java";
		
		try {
			
			File root = new File(workdir); 
			File sourceFile = new File(root, javaFilePath);
			sourceFile.getParentFile().mkdirs();
			new FileWriter(sourceFile).append(source).close();

//			JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();						
//			compiler.run(null, null, null, sourceFile.getPath());			
			javaCodeCompile(sourceFile);
			
			if(classLoader == null) {
				URL[] urls = new URL[]{root.toURI().toURL()};
				classLoader = URLClassLoader.newInstance(urls);				
			}

			Class<?> cls = Class.forName(classPath, true, classLoader); 
			Object instance = cls.newInstance(); 
			
			return instance;
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}
我们将classLoader作为参数读取,null的情况,才调用我们原有的实现:
				URL[] urls = new URL[]{root.toURI().toURL()};
				classLoader = URLClassLoader.newInstance(urls);	

同时,取消了

//			JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();						
//			compiler.run(null, null, null, sourceFile.getPath());	
代之以:

    public void javaCodeCompile(File sourceFile) {  
    	
    	JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();  
    	  
    	DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();  
    	StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);  
    	  
    	Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(new File[]{sourceFile}));  
    	  
    	List<String> optionList = new ArrayList<String>();  
    	
    	String classpath = System.getProperty("java.class.path");
    	classpath += ";" + workdir;
    	optionList.addAll(Arrays.asList("-classpath", classpath)); 
    	
    	JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, optionList, null, compilationUnits);  
    	task.call();  
    } 
这里我们改为使用
compiler.getTask
因为通过它,可以传递classpath等编译参数进去。

现在,万事大吉,

run一把试试:

成功了~~~!

而且,提请注意,虽然作为工具打造者,我们拉拉杂杂写了很多,

但是作为工具使用者,你只需要:

写一个Controller 的实现

写一个 JSignP 模板

配置我们的 xml文件

万事大吉~~是不是跟 spring-mvc 或者 structs 体验一致呢:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值