学习总结与分享-手写Spring MVC完善篇

在上章分享中我们完成了基础的构建,已经可以根据url访问到controller并对相应的参数根据参数名匹配完成赋值,将参数信息打印到网页中,下面我们需要根据ModelAndView中的视图信息字符串,跳转到相应的页面中,并把model中相对应的参数信息替换到页面的指定符号。
代码:

@XxxController
@XxxRequestMapping("/user")
public class UserController {

    @XxxAutowired
    private  UserService userService;

    @XxxRequestMapping("/getuser")
    public ModleAndView getUserInfo(String userName, Long age){
        ModleAndView modleAndView=new ModleAndView();
        //设置要跳转的视图字符串
        modleAndView.setViewData("mvc");
        //设置model,绑定参数
        HashMap mode=new HashMap();
        mode.put("userName",userName);
        mode.put("age",age);
        modleAndView.setModel(mode);
        userService.getUserInfo("张三");
        return modleAndView;
    }


    @XxxRequestMapping("/getJson")
    public String getJson(String userName, Long age){
        return "userName="+userName+"age="+age;
    }
}

设置请求路径,绑定参数信息和视图信息。通过视图解析器解析。

@Data
public class ModleAndView {

     //视图名称
      private Object viewData;

      //响应参数
       private HashMap model;

       //是否为一个视图对象
       private boolean hasView;

}

模型与视图实体类,绑定参数信息。

	public class DispatchServlet extends HttpServlet {


    private Properties vewConfig;

    private List<View> viewList = new ArrayList();

    private ConcurrentHashMap<String, HandleMapping> handleMappings = new ConcurrentHashMap();
    private ConcurrentHashMap<HandleMapping, HandlerAdapter> handleradapters = new ConcurrentHashMap();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        processRequest(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        processRequest(req, resp);
    }

    private void processRequest(HttpServletRequest req, HttpServletResponse resp) {
        //获取初始化的语言环境
        // 把request和response包装成ServletRequestAttributes对象
        //异步请求管理
        //统一调用doService
        doService(req, resp);

    }

    private void doService(HttpServletRequest req, HttpServletResponse resp) {
        doDispatch(req, resp);
    }


    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        try {
            //获取请求url
            String requestUrl = req.getRequestURI();
            //根据URL获得HandleMapping
            HandleMapping handleMapping = getHandleMapping(requestUrl);
            //返回404
            if (handleMapping == null) {
                resp.getWriter().write("404");
                return;
            }

            //获得参数适配器
            HandlerAdapter ha = handleradapters.get(handleMapping);
            //执行方法,得到视图信息
            ModleAndView modleAndView = ha.handle(req, resp, handleMapping);
            //把视图名称解析成对应文件名
            applyDefaultViewName(modleAndView);

            //返回json
            if (!modleAndView.isHasView()) {
                resp.getWriter().write(modleAndView.getViewData().toString());
                return;
            }
            //根据视图名称找到视图文件
            View view = getView(modleAndView.getViewData().toString());

            //读取jsp文件,替换${}内容
            view.render(modleAndView, resp);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private View getView(String viewName) {
        for (View view : viewList) {
            if (view.getViewName().equals(viewName)) return view;
        }
        return null;
    }

    /**
     * 把返回字符串解析成文件名mvc=>mvc.jsp
     *
     * @param modleAndView
     */
    private void applyDefaultViewName(ModleAndView modleAndView) {
        if (modleAndView.isHasView()) {
            //mvc
            String viewName = modleAndView.getViewData().toString();
            String prefix = vewConfig.get("view.prefix").toString();
            String suffix = vewConfig.get("view.suffix").toString();
            viewName = prefix + viewName + suffix;
            modleAndView.setViewData(viewName);
        }
    }


    private HandleMapping getHandleMapping(String requestUrl) {
        return this.handleMappings.get(requestUrl);
    }


    @Override
    public void init() throws ServletException {

        loadProperties();
        //初始化IOC容器
        AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext("com.spring.mvc.bean");

        //初始化HandleMapping  保存controller,Method ,url关系
        initHandlerMappings(configApplicationContext);

        //初始化参数信息
        initHandlerAdapter();

        //初始化视图信息
        initViewResolver();
        System.out.println("=init");
    }

    private void loadProperties() {
        try {
            InputStream is = this.getClass().getClassLoader().getResourceAsStream("dispatcherServlet.properties");
            vewConfig = new Properties();
            vewConfig.load(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initViewResolver() {
        try {
            //jsp文件路径
            String path = vewConfig.getProperty("view.rootPath");

            //遍历文件夹获取到所有jsp文件
            URL url = this.getClass().getClassLoader().getResource(path);
            //拿到文件夹
            File file = new File(url.toURI());
            doLoadFile(file);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void doLoadFile(File file) {
        String suffix = vewConfig.getProperty("view.suffix");
        //文件夹
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                doLoadFile(f);
            }

        } else {
            //是否为jsp结尾的文件
            if (file.getName().endsWith(suffix)) {
                View view = new View(file.getName(), file);
                viewList.add(view);//保存文件
            }

        }
    }

    private void initHandlerAdapter() {

        try {
            //遍历所有方法,获取参数名
            for (HandleMapping handleMapping : handleMappings.values()) {
                //参数类型
                Class<?>[] parameterTypes = handleMapping.getMethod().getParameterTypes();
                //参数名
                List<String> parameterNames = MethodHelper.getMethodParamNames(handleMapping.getController().getClass(), handleMapping.getMethod());
                List<Parameter> parameters = new ArrayList<>();
                //保存对应方法所有参数
                for (int i = 0; i < parameterNames.size(); i++) {
                    Parameter parameter = new Parameter(parameterNames.get(i), parameterTypes[i]);
                    parameters.add(parameter);
                }
                HandlerAdapter handlerAdapter = new HandlerAdapter(parameters);
                handleradapters.put(handleMapping, handlerAdapter);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    private void loadViewConfig(String path) {
        try {
            InputStream is = this.getClass().getClassLoader().getResourceAsStream(path);
            vewConfig = new Properties();
            vewConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private void initHandlerMappings(AnnotationConfigApplicationContext configApplicationContext) {
        //拿到所有controller
        Map<String, BeanDefinition> beanDefinitionMap = configApplicationContext.getBeanDefinitions();
        for (BeanDefinition beanDefinition : beanDefinitionMap.values()) {
            if (!beanDefinition.getBeanClass().isAnnotationPresent(XxxController.class)) continue;
            //获取controller所有method
            Class cls = beanDefinition.getBeanClass();
            Method[] methods = cls.getMethods();
            for (Method method : methods) {
                //判断带有RequesMapping 注解method
                if (!method.isAnnotationPresent(XxxRequestMapping.class)) continue;
                //获取RequesMapping value
                String reqUrl = "";
                String controllerUrl = "";
                XxxRequestMapping methodRequestMapping = method.getAnnotation(XxxRequestMapping.class);
                String methodUrl = methodRequestMapping.value();
                if (cls.isAnnotationPresent(XxxRequestMapping.class)) {
                    XxxRequestMapping controllerRequestMapping = (XxxRequestMapping) cls.getAnnotation(XxxRequestMapping.class);
                    controllerUrl = controllerRequestMapping.value();
                }
                reqUrl = reqUrl + controllerUrl + methodUrl;
                HandleMapping handleMapping = new HandleMapping(reqUrl, configApplicationContext.getBean(beanDefinition.getBeanName()), method);
                handleMappings.put(reqUrl, handleMapping);
            }

        }
    }
}

在init()方法中添加
initHandlerAdapter()方法初始化参数适配器信息:将hadleMapping匹配的参数信息HandlerAdapter储存到map中。
HandlerAdapter:

public class HandlerAdapter {

    private List<Parameter> parameterList;

    public HandlerAdapter(List<Parameter> parameterList) {
        this.parameterList = parameterList;
    }

    public ModleAndView handle(HttpServletRequest request, HttpServletResponse response, HandleMapping handleMapping){
        ModleAndView modleAndView=new ModleAndView();
        try {
        //获取请求参数
        Map<String, String[]> parameterMap= request.getParameterMap();

        //最终传到方法的参数
        Object [] params=new Object[parameterList.size()];

        //遍历方法的参数
        for(int i=0;i<parameterList.size();i++){
           Parameter parameter= parameterList.get(i);
            boolean flag=false;
            for(String key:parameterMap.keySet()){
                if (key.equals(parameter.getParameterName())){
                    //赋值,转型
                    params[i]= ClassHelper.typeConversion(parameter.getParameterType(),request.getParameter(key));
                    flag=true;
                }
            }
            //请求传参则赋默认值
            if(!flag){
                params[i]=null;
            }

        }
     Object result= handleMapping.getMethod().invoke(handleMapping.getController(),params);

        //需要返回视图
        if( result instanceof ModleAndView){
            modleAndView= (ModleAndView)result;
            modleAndView.setHasView(true);
        }else{
            //非视图返回Json
            modleAndView.setHasView(false);
            modleAndView.setViewData(result);
        }
        }catch (Exception e){
        e.printStackTrace();
        }
        return modleAndView;
    }

}

Parameter:

@Data
public class Parameter {

    public Parameter(String parameterName, Class<?> parameterType) {
        this.parameterName = parameterName;
        this.parameterType = parameterType;
    }

    private  String parameterName;

    private  Class<?> parameterType;

}

initViewResolver()方法初始化视图信息:
dispatcherServlet.properties:

view.rootPath=/view
view.prefix=
view.suffix=.jsp

根据视图的根路径和后缀名,找到视图,并根据名称匹配%{}中的参数
mvc.jsp:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>mvc page userName is %{userName},age is %{age}</title>
</head>
<body>

</body>
</html>

请求路径/user/getuser,请求会先经过dispatcher的inti方法,保存好路径的匹配信息,根据拿到的Handlmapping获取HandlerAdapter对象,调用对象的处理方法,将ModelAndView返回,最后将ModelAndView中的View匹配页面并替换页面中的关键字,将页面输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值