spring手写---MVC实现

《spring5核心原理》笔记

思路:

1.遍历实例化bean,找到带有@controller 标签的类,

2.解析该类,把@RequestMapping和方法,类名形参映射,封装到Mapping中。

3.接收请求,解析发送过来的request,拿到url,映射到具体的方法

4.拿到请求过来的参数,然后利用反射,调用method,执行获得结果

5.根据不同的返回结果进行返回,可能返回json,可能返回html。

6.增加一个静态页面渲染的功能。(未产生效果,正则表达式可能有问题)

实现代码:

1.XDispatcherServlet类

public class XDispatcherServlet extends HttpServlet {
    private final String LOCATION="contextConfigLocation";
    private List handlerMappings=new ArrayList<>(); //映射,封装
    private Map handlerAdapters=new HashMap<>();
    private List viewResolvers=new ArrayList<>();
    private XApplicationContext context;

    public void init(ServletConfig config) throws ServletException{
        System.out.println("dispatcherServlet init().....");
        System.out.println(config.getInitParameter(LOCATION)); //获取web.xml中配置的值!
        context=new XApplicationContext(config.getInitParameter(LOCATION));
        initStrategies(context);
    }
    protected void initStrategies(XApplicationContext context){
        //有九种策略
        //针对每个用户的请求,都会经过一些处理策略处理,最终才能有结果输出
        //每种策略可以自定义干预,但是最终的结果都一致
        //=====================传说中的九大组件==================
        initMultipartResolver(context); //文件上传解析,如果请求类型是multipart,将通过该方法进行解析
        initLocaleResolver(context); //本地化 解析
        initThemeResolver(context);  //主题 解析
        initHandlerMappings(context);  //将handlerMapping请求映射到 处理器 【实现】
        initHandlerAdapters(context);  //进行多类型的参数 动态匹配          【实现】
        initHandlerExceptionResolvers(context); //如果执行过程中 遇到异常,将交给该方法
        initRequestToViewNameTranslator(context); //直接将 请求解析到 视图名
        initViewResolvers(context);  //将逻辑视图解析到 具体视图实现         【实现】
        initFlashMapManager(context); //flash映射管理器
    }

    private void initFlashMapManager(XApplicationContext context){}
    private void initRequestToViewNameTranslator(XApplicationContext context){}
    private void initHandlerExceptionResolvers(XApplicationContext context){}
    private void initThemeResolver(XApplicationContext context){}
    private void initLocaleResolver(XApplicationContext context){}
    private void initMultipartResolver(XApplicationContext context){}
    //---------------只 实现其中 三大组件!---------------------
    //将controller 中配置的RequestMapping和Method进行一一对应
    private void initHandlerMappings(XApplicationContext context){
        //一一对应:Map map;( 注意requestMapping  分为两级!)
        //首先从容器中 获取所有的实例
        String []beanNames=context.getBeanDefinitionNames();  //所以返回的是所有bean的小名
        try{
            for(String beanName:beanNames){
                //到了mvc层,对外提供的方法只有一个 getBean()方法
                //返回的对象 不是BeanWrapper 怎么办?
                Object controller=context.getBean(beanName); //直接获取
                Class clazz=controller.getClass();
                if(!clazz.isAnnotationPresent(XController.class))  //这里读取的是标签。
                    continue;
                //从bean中找到所有的controller。 把里面的requestMapping中的value解析出来
                String baseUrl="";
                if(clazz.isAnnotationPresent(XRequestMapping.class)){
                    XRequestMapping requestMapping=clazz.getAnnotation(XRequestMapping.class);
                    baseUrl=requestMapping.value();
                }
                //扫描所有的public 类型的方法
                Method[] methods=clazz.getMethods();
                for(Method method:methods){ //只处理requestMapping注解
                    if(!method.isAnnotationPresent(XRequestMapping.class))
                        continue;
                    XRequestMapping requestMapping=method.getAnnotation(XRequestMapping.class);
                    //这里的baseUrl中 应该有/了,                                   // \*  切换为 *   但是这里一般没有使用\在匹配中。
                    String regex=("/"+baseUrl+requestMapping.value().replaceAll("\\*",".")).replaceAll("/+","/");
                    Pattern pattern=Pattern.compile(regex);
                    //url--controller----method
                    this.handlerMappings.add(new XHandlerMapping(pattern,controller,method));
                    System.out.println("Mapping: "+regex);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //给每一个mapping  配置一个 handler
    private void initHandlerAdapters(XApplicationContext context){
        for(XHandlerMapping handlerMapping:this.handlerMappings){
            //每个方法 有一个参数列表,这里保存的是形参列表
            this.handlerAdapters.put(handlerMapping,new XHandlerAdapter()) ;
        }
    }

    private void initViewResolvers(XApplicationContext context){
        //在页面中输入 http://localhost/first.html
        //解决页面名字和 模板文件 关联的问题
        String templateRoot =context.getConfig().getProperty("templateRoot");  //从application.properties中 读取 参数
        String templateRootPath=this.getClass().getResource(templateRoot).getFile();
        File templateRootDir=new File(templateRootPath);
        //找到所有的 html模板
        for(File template:templateRootDir.listFiles()){ //遍历
            this.viewResolvers.add(new XViewResolver(templateRoot));
        }
    }
//===================================运行时调用====================
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try{
            doDispatch(req,resp);
        }catch (Exception e){
            //这里可以返回一个 html页面
            e.printStackTrace();
        }
    }
    private void doDispatch(HttpServletRequest req,HttpServletResponse resp) throws Exception{
        XHandlerMapping handler=getHandler(req); //根据请求的url 获得一个handler
        if(handler==null){
            processDispatchResult(req,resp,new XModelAndView("404"));
            return;
        }
        //通过key  取value
        XHandlerAdapter ha=getHandlerAdapter(handler);
        //
        XModelAndView mv=ha.handle(req,resp,handler);
        processDispatchResult(req,resp,mv); //真的输出
    }
    private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,XModelAndView mv)throws Exception{
        if(null==mv)
            return;
        if(this.viewResolvers.isEmpty())
            return;
        if(this.viewResolvers!=null){
            for(XViewResolver viewResolver:this.viewResolvers){
                XView view=viewResolver.resolveViewName(mv.getViewName(),null);
                if(view!=null){
                    view.render(mv.getModel(),request,response);
                    return;
                }
            }
        }
    }
    private XHandlerAdapter getHandlerAdapter(XHandlerMapping handler){
        if(this.handlerAdapters.isEmpty())
            return null;
        XHandlerAdapter ha=this.handlerAdapters.get(handler);
        if(ha.supports(handler))
            return ha;
        return null;
    }
    private XHandlerMapping getHandler(HttpServletRequest req){
        if(this.handlerMappings.isEmpty())
            return null;
        String url=req.getRequestURI();
        String contextPath=req.getContextPath();
        url=url.replace(contextPath,"").replaceAll("/+","/");
        //通过url ,找到对应的handlerMapping,然后再去拿对应的map的value
        for(XHandlerMapping handler:this.handlerMappings) {
            Matcher matcher=handler.getPattern().matcher(url);
            if (!matcher.matches())
                continue;
            return handler;
        }
        return null;
    }
}

2.XHandlerMapping类

@Data
public class XHandlerMapping {
    private Object controller;// 目标方法所在的controller对象
    private Method method;// URL对应的目标方法
    private Pattern pattern; //url的封装
    public XHandlerMapping(Pattern pattern,Object controller,Method method){
        this.controller=controller;
        this.pattern=pattern;
        this.method=method;
    }

}

3.XHandlerAdapter

//映射 适配器
public class XHandlerAdapter {
    //处理器 是否合法?
    public boolean supports(Object handler){
        return (handler instanceof  XHandlerMapping);
    }
    public XModelAndView handle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
        XHandlerMapping handlerMapping=(XHandlerMapping) handler;
        //每个方法有一个参数列表(形参),
        Map paramMapping=new HashMap(); //形参名字+排第几个
      //  Annotation[] pa=handlerMapping.getMethod().getDeclaredAnnotations(); //这里抓的是注解 ----url映射 对应的method
        Annotation[][] pa=handlerMapping.getMethod().getParameterAnnotations(); //上面抓是的声明注解,这里要抓参数注解
        for(int i=0;i[] paramTypes=handlerMapping.getMethod().getParameterTypes(); //这里抓的是形参
       for(int i=0;i type=paramTypes[i];
           if(type==HttpServletRequest.class||type==HttpServletResponse.class){   //把两个特殊的 形参 也放入 形参标中。而且这个序号不会和上面的重合,因为用if 隔离了!
               paramMapping.put(type.getName(),i);
           }
       }

        //2.得到自定义命名参数所在的位置
        //用户通过uri传来的参数列表
        Map reqParameterMap=request.getParameterMap();//一步直接抓到 传入的参数
        //3.构造实参列表
        Object []paramValues=new Object[paramTypes.length]; //成立一个 参数组,与上面解析出来的参数 组长度一样!
        //对传过来的参数 进行解析
        for(Map.Entry param:reqParameterMap.entrySet()){
            //为什么把string组,换成一个 value,然后进行匹配呢?
            String value= Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").replaceAll("\\s","");
            if(!paramMapping.containsKey(param.getKey()))  //如果形参列表中,没有这个参数名字,那么就【忽略】
                continue;
            int index=paramMapping.get(param.getKey()); //把值放到 参数组中
            paramValues[index]=caseStringValue(value,paramTypes[index]);
        }
        //两个 参数特殊处理 直接注入的办法(从servlet中 拿到的两个对象,注入进去)
        if(paramMapping.containsKey(HttpServletRequest.class.getName())){
            int reqIndex=paramMapping.get(HttpServletRequest.class.getName());
            paramValues[reqIndex]=request;
        }
        if(paramMapping.containsKey(HttpServletResponse.class.getName())){
            int respIndex=paramMapping.get(HttpServletResponse.class.getName());
            paramValues[respIndex]=response;
        }
        //4.从handler中取出 controller,method 然后利用反射进行调用 得到返回的结果
        Object result=handlerMapping.getMethod().invoke(handlerMapping.getController(),paramValues); //【执行】
        if(result ==null)
            return null;
        boolean isModelAndView =handlerMapping.getMethod().getReturnType()==XModelAndView.class;
        if(isModelAndView){
            return (XModelAndView)result;
        }else{
            return null;
        }
    }
    //这个是int 的类型转换 绑定。
    private Object caseStringValue(String value,Class clazz){
        if(clazz==String.class){
            return value;
        }else if(clazz==Integer.class){
            return Integer.valueOf(value);
        }else if(clazz==int.class){
            return Integer.valueOf(value).intValue();
        }else
            return null;
    }
}

辅助类

1.XModelAndView

@Data
public class XModelAndView {
    private String viewName; //页面模板的名称
    private Map model;//往页面传送的参数

    public XModelAndView(String viewName,Map model){
        this.viewName=viewName;
        this.model=model;
    }
    public XModelAndView(String viewName){
        this.viewName=viewName;
    }
}

2.XView

public class XView {
    public static final String DEFAULT_CONTENT_TYPE ="text/html;charset=utf-8";
    private File viewFile;
    public XView(File viewFile){
        this.viewFile=viewFile;
    }
    public String getContentTYpe(){
        return DEFAULT_CONTENT_TYPE;
    }
    public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception{
        StringBuffer sb=new StringBuffer();
        RandomAccessFile ra=new RandomAccessFile(this.viewFile,"r");
        System.out.println("render() html content: ");
        try{
            String line=null;
            while(null!=(line=ra.readLine())){
                line=new String(line.getBytes("ISO-8859-1"),"utf-8"); //解码(因为默认iso 使用的是 iso好像是这样
                System.out.println(line);
                Pattern pattern=Pattern.compile("$\\{[^\\}]+\\}",Pattern.CASE_INSENSITIVE);//这里是形成一个 匹配符号把
                Matcher matcher=pattern.matcher(line); //这里使用匹配 进行对字符串 超过。。好像是对\进行转义标注。
                while(matcher.find()){
                    String paramName=matcher.group();
                    paramName=paramName.replaceAll("$\\{|\\}",""); //将¥{} 内容取出来(自定义 动态html,渲染)
                    Object paramValue=model.get(paramName);  //从ModelAndView中 获得数据值,进行渲染
                    if(null==paramValue)
                        continue;
                    line=matcher.replaceFirst(makeStringForRegExp(paramValue.toString()));
                    matcher=pattern.matcher(line);
                }
                sb.append(line);
            }
        }finally {
            ra.close();
        }
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(sb.toString()); //输出到前台
    }
    public static String makeStringForRegExp(String str){
        return str.replace("\\","\\\\").replace("*","\\*")
                .replace("+","\\+").replace("|","\\|")
                .replace("{","\\{").replace("}","\\}")
                .replace("(","\\(").replace(")","\\)")
                .replace("^","\\^").replace("$","\\$")
                .replace("[","\\[").replace("]","\\]")
                .replace("?","\\?").replace(",","\\,")
                .replace(".","\\.").replace("&","\\&")
                ;
    }
}

3.XViewResolver

//涉及这个类的主要目的:
//1. 将一个静态文件 变为一个 动态文件
//2. 根据用户传送不同的 参数,产生不同的结果。
//最终输出字符串,交给response输出。
//这个就是找到 .html页面了!,就是对文件流的处理了
public class XViewResolver {
    private final String DEFAULT_TEMPLATE_SUFFIX=".html";
    private File templateRootDir;
    private String viewName;
    //先把文件 的指针拿到
    public XViewResolver(String templateRoot){
        String templateRootPath=this.getClass().getResource(templateRoot).getFile(); //这个有深度!
        this.templateRootDir=new File(templateRootPath);
    }
    public XView resolveViewName(String viewName, Locale locale) throws Exception{
        this.viewName=viewName;
        if(null==viewName||"".equals(viewName.trim()))
            return null;
        viewName=viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX)?viewName:(viewName+DEFAULT_TEMPLATE_SUFFIX); //如果有了后缀,就不用再加了!容错性
        File templateFile=new File((templateRootDir.getPath()+"/"+viewName).replaceAll("/+","/"));
        return new XView(templateFile);
    }

    public String getViewName(){
        return viewName;
    }
}

 

你可能感兴趣的:(spring)