JEE6 CDI 扩展实现 MVC (二)

        在上一篇中,我们完成了对MVC的Controller 的基本信息的初始化。现在我们注册一个Servlet 来拦截我们的请求然后我们根据请求路径来匹配找到对应的 Controller 的某一个方法。

public class DelegatingServlet extends HttpServlet {

	private static final long serialVersionUID = -3941041507536315510L;

	@Inject
	private MvcHandler mvcHandler;

	private static final Logger logger = LoggerFactory
			.getLogger(DelegatingServlet.class);

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doHandleRequest(req, resp, RequestMethod.GET);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doHandleRequest(req, resp, RequestMethod.POST);
	}

	public void doHandleRequest(HttpServletRequest request,
			HttpServletResponse response, RequestMethod requestMethod) {
		RequestContext context = new RequestContext(getServletContext(),
				request, response, requestMethod);
		logger.debug("Servlet delegating the {} request from {}",
				requestMethod, request.getRequestURI());
		mvcHandler.handleRequest(context);
	}

}

然后,我们在 web.xml 添加配置,拦截所有 /cdi/* 下面的请求。我们对一次请求里面所涉及到的 对象数据 定义了一个实体。

public class RequestContext {
	
	private final ServletContext servletContext;
	private final HttpServletRequest request;
	private final HttpServletResponse response;
	private final RequestMethod requestMethod;
	private Object controller;  // 对应的 controller
	private ControllerMethod method;  // controller 的相关参数
	private Object outcome;   // controller 的返回值

         // gets, sets
}

然后把这个实体传给 MvcHandler 处理, MvcHandler 通过请求路径和我们初始化好的Controller 来匹配找出对应的Controller。

public void handleRequest(RequestContext context) {
		context.setMethod(matcher.findMatching(controllerInfo, context)); //查找出Controller 
                if (context.getMethod() != null) {
			Object controller = locateController(context.getMethod()
					.getControllerClass());
			context.setController(controller);
			// start executing the controller method
			executeControllerMethod(context); // 找到对应的Controller后,开始执行对应的方法 			
                        handleResponse(context);
		} else {
			throw new RuntimeException("Unable to find method for "
					+ context.getRequestMethod() + " request with url "
					+ context.getPath());
		}
	}


public ControllerMethod findMatching(ControllerInfo info,
			RequestContext context) {
		for (ControllerMethod method : info.getControllerMethods()) {
			if (matches(method, context)) {
				return method;
			}
		}
		return null;
	}

	protected boolean matches(ControllerMethod methodToTest, RequestContext context) {
		String path = context.getPath();
		boolean result = methodToTest.matchesRequestMethod(context.getRequestMethod())
				&& (methodToTest.getPrefix() == null || path.startsWith(methodToTest.getPrefix()))
				&& (methodToTest.getSuffix() == null || path.endsWith(methodToTest.getSuffix()));

		logger.debug("match result = " + result);
		return result;
	}

找到对应的Controller后,我们开始调用方法。这里分为带参数和不带参数的两种情况。目前参数还是不支持实体。

private void executeControllerMethod(RequestContext context) {
		ControllerMethod controllerMethod = context.getMethod();
		Method javaMethod = controllerMethod.getMethod();
		Object outcome = null;
		try {
			if (!controllerMethod.getArgs().isEmpty()) {
				// 处理带参数的 Controller 方法 				
                        HttpServletRequest request = context.getRequest();
				outcome = javaMethod.invoke(context.getController(),
						handleMethodArgs(controllerMethod,request));
			} else {
				outcome = javaMethod.invoke(context.getController());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		context.setOutcome(outcome);
	}

        处理带参数的方法

private Object[] handleMethodArgs(ControllerMethod controllerMethod,HttpServletRequest request) throws InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
		List<Map<String, Class<?>>> args = controllerMethod.getArgs();
		List<Object> postArgs = new ArrayList<Object>();
		for (Map<String, Class<?>> map : args) {
			for (Iterator<String> iterator = map.keySet().iterator(); iterator
					.hasNext();) {
				String key = iterator.next();
				String postValue = request.getParameter(key);
				if (postValue == null
						|| postValue.equals("")) {
					if(map.get(key).isPrimitive()){
						throw new ClassCastException("null value can not be cast to " + map.get(key)+" : " + key);
					}
					postArgs.add(map.get(key).newInstance());
				}else{
					if (map.get(key).isPrimitive()) {  // 这里对基本数据类型处理,需要先获得其包装类 					 
                                        Class<?> wrapClass = ReflectionUtil.getWrapClass(map.get(key));
						postArgs.add(wrapClass.getMethod("valueOf",
								String.class).invoke(wrapClass,
										request.getParameter(key)));
					} else {
						postArgs.add(map.get(key).cast(
								request.getParameter(key)));
					}
				}
			}
		}
		return postArgs.toArray();
	}

执行完 Controller 方法,我们根据其返回的 View 的路径来跳转到对应的页面。

private void handleResponse(RequestContext context) {
		if (context.getOutcome() instanceof String) {
			String outcome = (String) context.getOutcome();
			String view = "/" + outcome + ".jsp";
			logger.debug("resolved outcome {} to view {}", outcome, view);
			context.forwardTo(view);
		}
	}


public void forwardTo(String url) {
		if(url == null){
			throw new NullPointerException();
		}
		if (!url.startsWith("/")) {
			url = "/" + url;
		}
		try {
			servletContext.getRequestDispatcher(url).forward(request, response);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

        这样,我们就完成了 MVC 一个处理过程。但是我们 Controller 如何向 View 传值呢, CDI 本身是支持 EL 表达式的,所以我们这里可以直接使用 EL 表达式来把 Controller 的数据在 View 上面展示出来。

      基本的MVC demo

        编写我们的Controller,如果需要通过 EL 来传值给 View ,那么就需要加上 @Named 注解。并且为变量添加get方法。

@Controller
@RequestMapping("/user/")
@Named
@RequestScoped
public class UserController {	
	
	private String username;
	private int age;
	@Inject
	public void Page_Load(){
		username = "Feng J";
	}
	
	@RequestMapping(value = "userinfo")
	public String userInfo() {
		return "userInfo";
	}
	
	@RequestMapping(value = "edit", method = RequestMethod.POST)
	public String userInfo(@Param("username") String _username,@Param("age") int _age) {
		System.out.println(_username + ":" + _age);
		username = _username;
		age = _age;
		return "viewUser";
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

        我们首先访问 /cdi/user/userinfo 。MVC就会帮我们跳转到userInfo.jsp页面。

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
	<head>
	</head>
	<body>
		<form method="post" action="user/edit">
			姓名<br/>
			<input value="${userController.username}" name="username"><br/>
			年龄<br/>
			<input  name="age"><br/>
			
			<input type="submit" value="Update" />
		</form>
	</body>
</html>

JEE6 CDI 扩展实现 MVC (二)

我们看到姓名已经有值,说明通过 EL 表达式拿到了后台 Controller 的值。然后我们修改提交,这里Form的Action是user/edit ,会执行 UserController 的 userInfo 方法,并将参数传过去。然后跳转到 viewUser.jsp 页面。

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  <body>
    ${userController.username} : ${userController.age}
  </body>
</html>

在这个页面上面显示修改后的 username 和 age 的值。

JEE6 CDI 扩展实现 MVC (二)

提交Form:


        这样我们就完成了一个简单的MVC 处理,当然功能还是比较弱,下面会加上 对整个Form提交的支持,POJO实体提交,返回普通字符串,返回JSon。如果可能的话,还有对 freemarker 这样的支持。





你可能感兴趣的:(mvc,CDI,JEE6)