# 控制器設計
一個MVC框架里 `C` 是核心的一塊,也就是控制器,每個請求的接收,都是由控制器去處理的。
在Mario中我們把控制器放在路由對象的controller字段上,實際上一個請求過來之后最終是落在某個方法去處理的。
簡單的方法我們可以使用反射實現動態調用方法執行,當然這對性能并不友好,你可以用緩存Method或者更高明的技術去做。
在這里我們不提及太麻煩的東西,因為初步目標是實現MVC框架,所以給大家提醒一下有些了解即可。
控制器的處理部分放在了核心Filter中,代碼如下:
```java
/**
* 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
看到這里也許很多同學會有點疑問,我們在說路由、控制器、匹配器,可是我怎么讓它運行起來呢?
您可說到點兒上了,幾乎在任何框架中都必須有配置這項,所謂的零配置都是扯淡。不管硬編碼還是配置文件方式,
沒有配置,框架的易用性和快速開發靠什么完成,又一行一行編寫代碼嗎? 如果你說機器學習,至少現在好像沒人用吧。
扯淡完畢,下一節來進入全局配置設計 ->