一个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);
}
}
}
这里执行的流程是酱紫的:
- 接收用户请求
- 查找路由
- 找到即执行配置的方法
- 找不到你看到的应该是404
看到这里也许很多同学会有点疑问,我们在说路由、控制器、匹配器,可是我怎么让它运行起来呢? 您可说到点儿上了,几乎在任何框架中都必须有配置这项,所谓的零配置都是扯淡。不管硬编码还是配置文件方式, 没有配置,框架的易用性和快速开发靠什么完成,又一行一行编写代码吗? 如果你说机器学习,至少现在好像没人用吧。
扯淡完毕,下一节来进入全局配置设计 ->