美文网首页web程序员Java开发那些事
如何设计一个Java Web MVC框架

如何设计一个Java Web MVC框架

作者: IT程序狮 | 来源:发表于2016-01-15 15:21 被阅读3803次

    作者:biezhi
    原文地址:https://github.com/biezhi/java-bible/blob/master/mvc/index.md

    通过使用Java语言实现一个完整的框架设计,这个框架中主要内容有第一小节介绍的Web框架的结构规划,例如采用MVC模式来进行开发,程序的执行流程设计等内容;第二小节介绍框架的第一个功能:路由,如何让访问的URL映射到相应的处理逻辑;第三小节介绍处理逻辑,如何设计一个公共的调度器,对象继承之后处理函数中如何处理response和request;第四小节至第六小节介绍如何框架的一些辅助功能,例如配置信息,数据库操作等;最后介绍如何基于Web框架实现一个简单的增删改查,包括User的添加、修改、删除、显示列表等操作。

    通过这么一个完整的项目例子,我期望能够让读者了解如何开发Web应用,如何搭建自己的目录结构,如何实现路由,如何实现MVC模式等各方面的开发内容。在框架盛行的今天,MVC也不再是神话。经常听到很多程序员讨论哪个框架好,哪个框架不好, 其实框架只是工具,没有好与不好,只有适合与不适合,适合自己的就是最好的,所以教会大家自己动手写框架,那么不同的需求都可以用自己的思路去实现。

    项目源码:https://github.com/junicorn/mario
    示例代码:https://github.com/junicorn/mario-sample

    接下来开始我们的框架之旅。

    项目规划

    做任何事情都需要做好规划,那么我们在开发博客系统之前,同样需要做好项目的规划,如何设置目录结构,如何理解整个项目的流程图,当我们理解了应用的执行过程,那么接下来的设计编码就会变得相对容易了

    创建一个maven项目

    约定一下框架基础信息

    • 假设我们的web框架名称是 mario
    • 包名是 com.junicorn.mario

    命令行创建

    mvn archetype:create -DgroupId=com.junicorn -DartifactId=mario -DpackageName=com.junicorn.mario
    

    Eclipse创建


    创建好的基本结构是这样的

    初始化一下 pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.junicorn</groupId>
        <artifactId>mario</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>mario</name>
        <url>https://github.com/junicorn/mario</url>
    
        <properties>
            <maven.compiler.source>1.6</maven.compiler.source>
            <maven.compiler.target>1.6</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <servlet.version>3.0.1</servlet.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    OK,项目创建好了,这个将是我们的框架。

    框架流程

    web程序是基于 M(模型)V(视图)C(控制器)设计的。MVC是一种将应用程序的逻辑层和表现层进行分离的结构方式。在实践中,由于表现层从 Java 中分离了出来,所以它允许你的网页中只包含很少的脚本。

    • 模型 (Model) 代表数据结构。通常来说,模型类将包含取出、插入、更新数据库资料等这些功能。
    • 视图 (View) 是展示给用户的信息的结构及样式。一个视图通常是一个网页,但是在Java中,一个视图也可以是一个页面片段,如页头、页尾。它还可以是一个 RSS 页面,或其它类型的“页面”,Jsp已经很好的实现了View层中的部分功能。
    • 控制器 (Controller) 是模型、视图以及其他任何处理HTTP请求所必须的资源之间的中介,并生成网页。

    设计思路

    mario 是基于servlet实现的mvc,用一个全局的Filter来做核心控制器,使用sql2o框架作为数据库基础访问。 使用一个接口Bootstrap作为初始化启动,实现它并遵循Filter参数约定即可。

    建立路由、数据库、视图相关的包和类,下面是结构:

    路由设计

    现代 Web 应用的 URL 十分优雅,易于人们辨识记忆。 路由的表现形式如下:

    /resources/:resource/actions/:action
    http://bladejava.com
    http://bladejava.com/docs/modules/route
    

    那么我们在java语言中将他定义一个 Route 类, 用于封装一个请求的最小单元, 在Mario中我们设计一个路由的对象如下:

    /**
     * 路由
     * @author biezhi
     */
    public class Route {
    
        /**
         * 路由path
         */
        private String path;
    
        /**
         * 执行路由的方法
         */
        private Method action;
    
        /**
         * 路由所在的控制器
         */
        private Object controller;
    
        public Route() {
        }
    
        public String getPath() {
            return path;
        }
    
        public void setPath(String path) {
            this.path = path;
        }
    
        public Method getAction() {
            return action;
        }
    
        public void setAction(Method action) {
            this.action = action;
        }
    
        public Object getController() {
            return controller;
        }
    
        public void setController(Object controller) {
            this.controller = controller;
        }
    
    }
    

    所有的请求在程序中是一个路由,匹配在 path 上,执行靠 action,处于 controller 中。

    Mario使用一个Filter接收所有请求,因为从Filter过来的请求有无数,如何知道哪一个请求对应哪一个路由呢? 这时候需要设计一个路由匹配器去查找路由处理我们配置的请求, 有了路由匹配器还不够,这么多的路由我们如何管理呢?再来一个路由管理器吧,下面就创建路由匹配器和管理器2个类:

    /**
     * 路由管理器,存放所有路由的
     * @author biezhi
     */
    public class Routers {
    
        private static final Logger LOGGER = Logger.getLogger(Routers.class.getName());
    
        private List<Route> routes = new ArrayList<Route>();
    
        public Routers() {
        }
    
        public void addRoute(List<Route> routes){
            routes.addAll(routes);
        }
    
        public void addRoute(Route route){
            routes.add(route);
        }
    
        public void removeRoute(Route route){
            routes.remove(route);
        }
    
        public void addRoute(String path, Method action, Object controller){
            Route route = new Route();
            route.setPath(path);
            route.setAction(action);
            route.setController(controller);
    
            routes.add(route);
            LOGGER.info("Add Route:[" + path + "]");
        }
    
        public List<Route> getRoutes() {
            return routes;
        }
    
        public void setRoutes(List<Route> routes) {
            this.routes = routes;
        }
    
    }
    

    这里的代码很简单,这个管理器里用List存储所有路由,公有的 addRoute 方法是给外部调用的。

    /**
     * 路由匹配器,用于匹配路由
     * @author biezhi
     */
    public class RouteMatcher {
    
        private List<Route> routes;
    
        public RouteMatcher(List<Route> routes) {
            this.routes = routes;
        }
    
        public void setRoutes(List<Route> routes) {
            this.routes = routes;
        }
    
        /**
         * 根据path查找路由
         * @param path  请求地址
         * @return      返回查询到的路由
         */
        public Route findRoute(String path) {
            String cleanPath = parsePath(path);
            List<Route> matchRoutes = new ArrayList<Route>();
            for (Route route : this.routes) {
                if (matchesPath(route.getPath(), cleanPath)) {
                    matchRoutes.add(route);
                }
            }
            // 优先匹配原则
            giveMatch(path, matchRoutes);
    
            return matchRoutes.size() > 0 ? matchRoutes.get(0) : null;
        }
    
        private void giveMatch(final String uri, List<Route> routes) {
            Collections.sort(routes, new Comparator<Route>() {
                @Override
                public int compare(Route o1, Route o2) {
                    if (o2.getPath().equals(uri)) {
                        return o2.getPath().indexOf(uri);
                    }
                    return -1;
                }
            });
        }
    
        private boolean matchesPath(String routePath, String pathToMatch) {
            routePath = routePath.replaceAll(PathUtil.VAR_REGEXP, PathUtil.VAR_REPLACE);
            return pathToMatch.matches("(?i)" + routePath);
        }
    
        private String parsePath(String path) {
            path = PathUtil.fixPath(path);
            try {
                URI uri = new URI(path);
                return uri.getPath();
            } catch (URISyntaxException e) {
                return null;
            }
        }
    
    }
    

    路由匹配器使用了正则去遍历路由列表,匹配合适的路由。当然我不认为这是最好的方法, 因为路由的量很大之后遍历的效率会降低,但这样是可以实现的,如果你有更好的方法可以告诉我 :)

    在下一章节我们需要对请求处理做设计了~

    控制器设计

    一个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);
            }
        }
    
    }
    

    这里执行的流程是酱紫的:

    1. 接收用户请求
    2. 查找路由
    3. 找到即执行配置的方法
    4. 找不到你看到的应该是404

    看到这里也许很多同学会有点疑问,我们在说路由、控制器、匹配器,可是我怎么让它运行起来呢? 您可说到点儿上了,几乎在任何框架中都必须有配置这项,所谓的零配置都是扯淡。不管硬编码还是配置文件方式, 没有配置,框架的易用性和快速开发靠什么完成,又一行一行编写代码吗? 如果你说机器学习,至少现在好像没人用吧。

    扯淡完毕,下一节来进入全局配置设计 ->

    配置设计

    Mario中所有的配置都可以在 Mario 全局唯一对象完成,将它设计为单例。

    要运行起来整个框架,Mario对象是核心,看看里面都需要什么吧!

    • 添加路由
    • 读取资源文件
    • 读取配置
    • 等等

    由此我们简单的设计一个Mario全局对象:

    /**
     * Mario
     * @author biezhi
     *
     */
    public final class Mario {
    
        /**
         * 存放所有路由
         */
        private Routers routers;
    
        /**
         * 配置加载器
         */
        private ConfigLoader configLoader;
    
        /**
         * 框架是否已经初始化
         */
        private boolean init = false;
    
        private Mario() {
            routers = new Routers();
            configLoader = new ConfigLoader();
        }
    
        public boolean isInit() {
            return init;
        }
    
        public void setInit(boolean init) {
            this.init = init;
        }
    
        private static class MarioHolder {
            private static Mario ME = new Mario();
        }
    
        public static Mario me(){
            return MarioHolder.ME;
        }
    
        public Mario addConf(String conf){
            configLoader.load(conf);
            return this;
        }
    
        public String getConf(String name){
            return configLoader.getConf(name);
        }
    
        public Mario addRoutes(Routers routers){
            this.routers.addRoute(routers.getRoutes());
            return this;
        }
    
        public Routers getRouters() {
            return routers;
        }
    
        /**
         * 添加路由
         * @param path          映射的PATH
         * @param methodName    方法名称
         * @param controller    控制器对象
         * @return              返回Mario
         */
        public Mario addRoute(String path, String methodName, Object controller){
            try {
                Method method = controller.getClass().getMethod(methodName, Request.class, Response.class);
                this.routers.addRoute(path, method, controller);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            }
            return this;
        }
    
    }
    

    这样在系统中永远保持一个Mario实例,我们用它来操作所有配置即可。

    Boostrapinit方法中使用

    @Override
    public void init(Mario mario) {
        Index index = new Index();
        mario.addRoute("/", "index", index);
        mario.addRoute("/html", "html", index);
    }
    

    这样,一个简单的MVC后端已经形成了!接下来我们要将结果展现在JSP文件中,要做视图的渲染设计 LET'S GO!

    视图设计

    我们已经完成了MVC中的C层,还有M和V没有做呢。这一小节来对视图进行设计,从后台到前台的渲染是这样的 后台给定一个视图位置,输出到前端JSP或者其他模板引擎上,做一个非常简单的接口:

    /**
     * 视图渲染接口
     * @author biezhi
     *
     */
    public interface Render {
    
        /**
         * 渲染到视图
         * @param view      视图名称
         * @param writer    写入对象
         */
        public void render(String view, Writer writer);
    
    }
    

    具体的实现我们先写一个JSP的,当你在使用Servlet进行开发的时候已经习惯了这句语法:

    servletRequest.getRequestDispatcher(viewPath).forward(servletRequest, servletResponse);
    

    那么一个JSP的渲染实现就很简单了

    /**
     * JSP渲染实现
     * @author biezhi
     *
     */
    public class JspRender implements Render {
    
        @Override
        public void render(String view, Writer writer) {
    
            String viewPath = this.getViewPath(view);
    
            HttpServletRequest servletRequest = MarioContext.me().getRequest().getRaw();
            HttpServletResponse servletResponse = MarioContext.me().getResponse().getRaw();
            try {
                servletRequest.getRequestDispatcher(viewPath).forward(servletRequest, servletResponse);
            } catch (ServletException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        private String getViewPath(String view){
            Mario mario = Mario.me();
            String viewPrfix = mario.getConf(Const.VIEW_PREFIX_FIELD);
            String viewSuffix = mario.getConf(Const.VIEW_SUFFIX_FIELD);
    
            if (null == viewSuffix || viewSuffix.equals("")) {
                viewSuffix = Const.VIEW_SUFFIX;
            }
            if (null == viewPrfix || viewPrfix.equals("")) {
                viewPrfix = Const.VIEW_PREFIX;
            }
            String viewPath = viewPrfix + "/" + view;
            if (!view.endsWith(viewSuffix)) {
                viewPath += viewSuffix;
            }
            return viewPath.replaceAll("[/]+", "/");
        }
    
    }
    

    配置 JSP 视图的位置和后缀可以在配置文件或者硬编码中进行,当然这看你的习惯, 默认设置了 JSP 在 /WEB-INF/ 下,后缀是 .jsp 你懂的!

    怎么用可以参考 mario-sample 这个项目,因为真的很简单 相信你自己。

    在下一节中我们就要和数据库打交道了,尝试新的旅程吧 :)

    数据库操作

    这一小节是对数据库操作做一个简单的封装,不涉及复杂的事务操作等。

    我选用了Sql2o作为底层数据库框架作为支持,它的简洁易用性让我刮目相看,后面我们也会写如何实现一个ORM框架。

    /**
     * 数据库支持
     * @author biezhi
     *
     */
    public final class MarioDb {
    
        private static Sql2o sql2o = null;
    
        private MarioDb() {
        }
    
        /**
         * 初始化数据库配置
         * @param url
         * @param user
         * @param pass
         */
        public static void init(String url, String user, String pass){
            sql2o = new Sql2o(url, user, pass);
        }
    
        /**
         * 初始化数据库配置
         * @param dataSource
         */
        public static void init(DataSource dataSource){
            sql2o = new Sql2o(dataSource);
        }
    
        /**
         * 查询一个对象
         * @param sql
         * @param clazz
         * @return
         */
        public static <T> T get(String sql, Class<T> clazz){
            return get(sql, clazz, null);
        }
    
        /**
         * 查询一个列表
         * @param sql
         * @param clazz
         * @return
         */
        public static <T> List<T> getList(String sql, Class<T> clazz){
            return getList(sql, clazz, null);
        }
    
        /**
         * 查询一个对象返回为map类型
         * @param sql
         * @return
         */
        public static Map<String, Object> getMap(String sql){
            return getMap(sql, null);
        }
    
        /**
         * 查询一个列表并返回为list<map>类型
         * @param sql
         * @return
         */
        public static List<Map<String, Object>> getMapList(String sql){
            return getMapList(sql, null);
        }
    
        /**
         * 插入一条记录
         * @param sql
         * @param params
         * @return
         */
        public static int insert(String sql, Object ... params){
            StringBuffer sqlBuf = new StringBuffer(sql);
            sqlBuf.append(" values (");
    
            int start = sql.indexOf("(") + 1;
            int end = sql.indexOf(")");
            String a = sql.substring(start, end);
            String[] fields = a.split(",");
    
            Map<String, Object> map = new HashMap<String, Object>();
    
            int i=0;
            for(String name : fields){
                sqlBuf.append(":" + name.trim() + " ,");
                map.put(name.trim(), params[i]);
                i++;
            }
    
            String newSql = sqlBuf.substring(0, sqlBuf.length() - 1) + ")";
    
            Connection con = sql2o.open();
            Query query = con.createQuery(newSql);
    
            executeQuery(query, map);
    
            int res = query.executeUpdate().getResult();
    
            con.close();
    
            return res;
        }
        /**
         * 更新
         * @param sql
         * @return
         */
        public static int update(String sql){
            return update(sql, null);
        }
    
        /**
         * 带参数更新
         * @param sql
         * @param params
         * @return
         */
        public static int update(String sql, Map<String, Object> params){
            Connection con = sql2o.open();
            Query query = con.createQuery(sql);
            executeQuery(query, params);
            int res = query.executeUpdate().getResult();
            con.close();
            return res;
        }
    
        public static <T> T get(String sql, Class<T> clazz, Map<String, Object> params){
            Connection con = sql2o.open();
            Query query = con.createQuery(sql);
            executeQuery(query, params);
            T t = query.executeAndFetchFirst(clazz);
            con.close();
            return t;
        }
    
        @SuppressWarnings("unchecked")
        public static Map<String, Object> getMap(String sql, Map<String, Object> params){
            Connection con = sql2o.open();
            Query query = con.createQuery(sql);
            executeQuery(query, params);
            Map<String, Object> t = (Map<String, Object>) query.executeScalar();
            con.close();
            return t;
        }
    
        public static List<Map<String, Object>> getMapList(String sql, Map<String, Object> params){
            Connection con = sql2o.open();
            Query query = con.createQuery(sql);
            executeQuery(query, params);
            List<Map<String, Object>> t = query.executeAndFetchTable().asList();
            con.close();
            return t;
        }
    
        public static <T> List<T> getList(String sql, Class<T> clazz, Map<String, Object> params){
            Connection con = sql2o.open();
            Query query = con.createQuery(sql);
            executeQuery(query, params);
            List<T> list = query.executeAndFetch(clazz);
            con.close();
            return list;
        }
    
        private static void executeQuery(Query query, Map<String, Object> params){
            if (null != params && params.size() > 0) {
                Set<String> keys = params.keySet();
                for(String key : keys){
                    query.addParameter(key, params.get(key));
                }
            }
        }
    }
    

    设计MVC框架部分已经完成,下一节是一个增删改查的例子

    增删改查

    /**
     * 用户控制器
     */
    public class UserController {
    
        /**
         * 用户列表
         * @param request
         * @param response
         */
        public void users(Request request, Response response){
            List<User> users = MarioDb.getList("select * from t_user", User.class);
            request.attr("users", users);
            response.render("users");
        }
    
        /**
         * 添加用户界面
         * @param request
         * @param response
         */
        public void show_add(Request request, Response response){
            response.render("user_add");
        }
    
        /**
         * 保存方法
         * @param request
         * @param response
         * @throws ParseException
         */
        public void save(Request request, Response response) throws ParseException{
            String name = request.query("name");
            Integer age = request.queryAsInt("age");
            String date = request.query("birthday");
    
            if(null == name || null == age || null == date){
                request.attr("res", "error");
                response.render("user_add");
                return;
            }
    
            Date bir = new SimpleDateFormat("yyyy-MM-dd").parse(date);
    
            int res = MarioDb.insert("insert into t_user(name, age, birthday)", name, age, bir);
            if(res > 0){
                String ctx = MarioContext.me().getContext().getContextPath();
                String location = ctx + "/users";
                response.redirect(location.replaceAll("[/]+", "/"));
            } else {
                request.attr("res", "error");
                response.render("user_add");
            }
        }
    
        /**
         * 编辑页面
         * @param request
         * @param response
         */
        public void edit(Request request, Response response){
            Integer id = request.queryAsInt("id");
            if(null != id){
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("id", id);
                User user = MarioDb.get("select * from t_user where id = :id", User.class, map);
                request.attr("user", user);
                response.render("user_edit");
            }
        }
    
        /**
         * 修改信息
         * @param request
         * @param response
         */
        public void update(Request request, Response response){
            Integer id = request.queryAsInt("id");
            String name = request.query("name");
            Integer age = request.queryAsInt("age");
    
            if(null == id || null == name || null == age ){
                request.attr("res", "error");
                response.render("user_edit");
                return;
            }
    
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("id", id);
            map.put("name", name);
            map.put("age", age);
    
            int res = MarioDb.update("update t_user set name = :name, age = :age where id = :id", map);
            if(res > 0){
                String ctx = MarioContext.me().getContext().getContextPath();
                String location = ctx + "/users";
                response.redirect(location.replaceAll("[/]+", "/"));
            } else {
                request.attr("res", "error");
                response.render("user_edit");
            }
        }
    
        /**
         * 删除
         * @param request
         * @param response
         */
        public void delete(Request request, Response response){
            Integer id = request.queryAsInt("id");
            if(null != id){
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("id", id);
                MarioDb.update("delete from t_user where id = :id", map);
            }
    
            String ctx = MarioContext.me().getContext().getContextPath();
            String location = ctx + "/users";
            response.redirect(location.replaceAll("[/]+", "/"));
        }
    }
    

    相关文章

      网友评论

      本文标题:如何设计一个Java Web MVC框架

      本文链接:https://www.haomeiwen.com/subject/niehkttx.html