Skip to content

Latest commit

 

History

History
150 lines (119 loc) · 4.75 KB

3.controller.md

File metadata and controls

150 lines (119 loc) · 4.75 KB

控制器设计

一个MVC框架里 C 是核心的一块,也就是控制器,每个请求的接收,都是由控制器去处理的。 在Mario中我们把控制器放在路由对象的controller字段上,实际上一个请求过来之后最终是落在某个方法去处理的。

简单的方法我们可以使用反射实现动态调用方法执行,当然这对性能并不友好,你可以用缓存Method或者更高明的技术去做。 在这里我们不提及太麻烦的东西,因为初步目标是实现MVC框架,所以给大家提醒一下有些了解即可。

控制器的处理部分放在了核心Filter中,代码如下:

/**
 * Mario MVC核心处理器
 * @author biezhi
 *
 */
public class MarioFilter implements Filter {
	
	private static final Logger LOGGER = Logger.getLogger(MarioFilter.class.getName());
	
	private RouteMatcher routeMatcher = new RouteMatcher(new ArrayList<Route>());
	
	private ServletContext servletContext;
	
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		Mario mario = Mario.me();
		if(!mario.isInit()){
			
			String className = filterConfig.getInitParameter("bootstrap");
			Bootstrap bootstrap = this.getBootstrap(className);
			bootstrap.init(mario);
			
			Routers routers = mario.getRouters();
			if(null != routers){
				routeMatcher.setRoutes(routers.getRoutes());
			}
			servletContext = filterConfig.getServletContext();
			
			mario.setInit(true);
		}
	}
	
	private Bootstrap getBootstrap(String className) {
		if(null != className){
			try {
				Class<?> clazz = Class.forName(className);
				Bootstrap bootstrap = (Bootstrap) clazz.newInstance();
				return bootstrap;
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		throw new RuntimeException("init bootstrap class error!");
	}
	
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        
        // 请求的uri
        String uri = PathUtil.getRelativePath(request);
        
        LOGGER.info("Request URI:" + uri);
        
        Route route = routeMatcher.findRoute(uri);
        
        // 如果找到
		if (route != null) {
			// 实际执行方法
			handle(request, response, route);
		} else{
			chain.doFilter(request, response);
		}
	}
	
	private void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Route route){
		
		// 初始化上下文
		Request request = new Request(httpServletRequest);
		Response response = new Response(httpServletResponse);
		MarioContext.initContext(servletContext, request, response);
		
		Object controller = route.getController();
		// 要执行的路由方法
		Method actionMethod = route.getAction();
		// 执行route方法
		executeMethod(controller, actionMethod, request, response);
	}
	
	/**
	 * 获取方法内的参数
	 */
	private Object[] getArgs(Request request, Response response, Class<?>[] params){
		
		int len = params.length;
		Object[] args = new Object[len];
		
		for(int i=0; i<len; i++){
			Class<?> paramTypeClazz = params[i];
			if(paramTypeClazz.getName().equals(Request.class.getName())){
				args[i] = request;
			}
			if(paramTypeClazz.getName().equals(Response.class.getName())){
				args[i] = response;
			}
		}
		
		return args;
	}
	
	/**
	 * 执行路由方法
	 */
	private Object executeMethod(Object object, Method method, Request request, Response response){
		int len = method.getParameterTypes().length;
		method.setAccessible(true);
		if(len > 0){
			Object[] args = getArgs(request, response, method.getParameterTypes());
			return ReflectUtil.invokeMehod(object, method, args);
		} else {
			return ReflectUtil.invokeMehod(object, method);
		}
	}

}

这里执行的流程是酱紫的:

  1. 接收用户请求
  2. 查找路由
  3. 找到即执行配置的方法
  4. 找不到你看到的应该是404

看到这里也许很多同学会有点疑问,我们在说路由、控制器、匹配器,可是我怎么让它运行起来呢? 您可说到点儿上了,几乎在任何框架中都必须有配置这项,所谓的零配置都是扯淡。不管硬编码还是配置文件方式, 没有配置,框架的易用性和快速开发靠什么完成,又一行一行编写代码吗? 如果你说机器学习,至少现在好像没人用吧。

扯淡完毕,下一节来进入全局配置设计 ->

links