@Slf4j
public class V1DispatcherServlet extends HttpServlet {
private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
private V1ApplicationContext context;
private List<V1HandlerMapping> handlerMappings = new ArrayList<V1HandlerMapping>();
private Map<V1HandlerMapping,V1HandlerAdapter> handlerAdapters = new HashMap<V1HandlerMapping,V1HandlerAdapter>();
private List<V1ViewResolver> viewResolvers = new ArrayList<V1ViewResolver>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try{
this.doDispatch(req,resp);
}catch(Exception e){
resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
e.printStackTrace();
// new V1ModelAndView("500");
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{
//1、通过从request中拿到URL,去匹配一个HandlerMapping
V1HandlerMapping handler = getHandler(req);
if(handler == null){
processDispatchResult(req,resp,new V1ModelAndView("404"));
return;
}
//2、准备调用前的参数
V1HandlerAdapter ha = getHandlerAdapter(handler);
//3、真正的调用方法,返回ModelAndView存储了要穿页面上值,和页面模板的名称
V1ModelAndView mv = ha.handle(req,resp,handler);
//这一步才是真正的输出
processDispatchResult(req, resp, mv);
}
private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, V1ModelAndView mv) throws Exception{
//把给我的ModleAndView变成一个HTML、OuputStream、json、freemark、veolcity
//ContextType
if(null == mv){return;}
//如果ModelAndView不为null,怎么办?
if(this.viewResolvers.isEmpty()){return;}
for (V1ViewResolver viewResolver : this.viewResolvers) {
V1View view = viewResolver.resolveViewName(mv.getViewName(),null);
view.render(mv.getModel(),req,resp);
return;
}
}
private V1HandlerAdapter getHandlerAdapter(V1HandlerMapping handler) {
if(this.handlerAdapters.isEmpty()){return null;}
V1HandlerAdapter ha = this.handlerAdapters.get(handler);
if(ha.supports(handler)){
return ha;
}
return null;
}
private V1HandlerMapping getHandler(HttpServletRequest req) throws Exception{
if(this.handlerMappings.isEmpty()){ return null; }
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
for (V1HandlerMapping handler : this.handlerMappings) {
try{
Matcher matcher = handler.getPattern().matcher(url);
//如果没有匹配上继续下一个匹配
if(!matcher.matches()){ continue; }
return handler;
}catch(Exception e){
throw e;
}
}
return null;
}
@Override
public void init(ServletConfig config) throws ServletException {
//1、初始化ApplicationContext
context = new V1ApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
//2、初始化Spring MVC 九大组件
initStrategies(context);
}
//初始化策略
protected void initStrategies(V1ApplicationContext context) {
//多文件上传的组件
initMultipartResolver(context);
//初始化本地语言环境
initLocaleResolver(context);
//初始化模板处理器
initThemeResolver(context);
//handlerMapping,必须实现
initHandlerMappings(context);
//初始化参数适配器,必须实现
initHandlerAdapters(context);
//初始化异常拦截器
initHandlerExceptionResolvers(context);
//初始化视图预处理器
initRequestToViewNameTranslator(context);
//初始化视图转换器,必须实现
initViewResolvers(context);
//参数缓存器
initFlashMapManager(context);
}
private void initFlashMapManager(V1ApplicationContext context) {
}
private void initViewResolvers(V1ApplicationContext context) {
//拿到模板的存放目录
String templateRoot = context.getConfig().getProperty("templateRoot");
String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
File templateRootDir = new File(templateRootPath);
String[] templates = templateRootDir.list();
for (int i = 0; i < templates.length; i ++) {
//这里主要是为了兼容多模板,所有模仿Spring用List保存
//在我写的代码中简化了,其实只有需要一个模板就可以搞定
//只是为了仿真,所有还是搞了个List
this.viewResolvers.add(new V1ViewResolver(templateRoot));
}
}
private void initRequestToViewNameTranslator(V1ApplicationContext context) {
}
private void initHandlerExceptionResolvers(V1ApplicationContext context) {
}
private void initHandlerAdapters(V1ApplicationContext context) {
//把一个requet请求变成一个handler,参数都是字符串的,自动配到handler中的形参
//可想而知,他要拿到HandlerMapping才能干活
//就意味着,有几个HandlerMapping就有几个HandlerAdapter
for (V1HandlerMapping handlerMapping : this.handlerMappings) {
this.handlerAdapters.put(handlerMapping,new V1HandlerAdapter());
}
}
private void initHandlerMappings(V1ApplicationContext context) {
String [] beanNames = context.getBeanDefinitionNames();
try {
for (String beanName : beanNames) {
Object controller = context.getBean(beanName);
Class<?> clazz = controller.getClass();
if(!clazz.isAnnotationPresent(V1Controller.class)){
continue;
}
String baseUrl = "";
//获取Controller的url配置
if(clazz.isAnnotationPresent(V1RequestMapping.class)){
V1RequestMapping requestMapping = clazz.getAnnotation(V1RequestMapping.class);
baseUrl = requestMapping.value();
}
//获取Method的url配置
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//没有加RequestMapping注解的直接忽略
if(!method.isAnnotationPresent(V1RequestMapping.class)){ continue; }
//映射URL
V1RequestMapping requestMapping = method.getAnnotation(V1RequestMapping.class);
// /demo/query
// (//demo//query)
String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
this.handlerMappings.add(new V1HandlerMapping(pattern,controller,method));
log.info("Mapped " + regex + "," + method);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
private void initThemeResolver(V1ApplicationContext context) {
}
private void initLocaleResolver(V1ApplicationContext context) {
}
private void initMultipartResolver(V1ApplicationContext context) {
}
}