JavaEE--无框架开发后台服务,经历造轮子的痛苦

目录

前言

无框架开发后台服务,经历造轮子的痛苦

需求梳理:

需求分析:

编码实现:

使用Session存储验证码,保证一次会话范围内共享:

使用Filter拦截器实现全局拦截

JDBC增删改查、分页、条件查询

使用事务包裹多次数据库操作,支持发生异常时回滚

使用转发实现返回页面,response写回对象信息

问题总结

代码高度耦合,类实例缺少管理

数据库操作实现复杂,存在大量重复编码

事务管理高耦合,高度代码重复

Servlet返回是字串还是页面都不方便


前言

带着问题学java系列博文之java基础篇。从问题出发,学习java知识。


无框架开发后台服务,经历造轮子的痛苦

通过《JavaEE--从文件上传、下载入门Java web》入门,依赖《JavaEE--Java Web基础知识》完成Java web知识的学习。现在我们来实战一下,尝试不依赖任何框架,从零实现一个后台服务,经历一番纯手工造轮子的痛苦。

 

需求梳理:

  1. 带验证码的用户登录;
  2. 实现对用户信息的增删改查;
  3. 实现分页查询和条件查询;
  4. 实现模拟银行账户转账操作;
  5. 实现调用接口返回页面以及返回对象信息;

需求分析:

  • 带验证码的用户登录

需要实现全局拦截,用户登录才放行,未登录则跳转login.jsp;可选实现方案:使用Filter拦截器,使用forword转发请求(想在请求中存入提示信息,所以使用转发请求,不用重定向);

验证码功能:浏览器端请求CheckCodeServlet,获取验证码,CheckCodeServlet将生成的验证码存入session;用户登录时携带验证码参数,后台校验是否和session中一致,一致则通过,同时立即清除session中的验证码,保持验证码的刷新。可选实现方案:使用session实现数据存储(用户会话技术,不用担心存在多用户互相影响的问题)

  • 实现对用户信息的增删改查

用户信息保存在数据库中(Mysql8.0),用户信息的增删改查,就是对数据库用户信息表的增删改查;可选实现方案:java 操作数据库,jdbc

  • 实现分页查询和条件查询

获取所有用户信息,列表展示;要求分页,否则一次加载数据过多,可能导致前端页面内存溢出,也影响后台接口执行效率;支持按照某些字段查询,条件查询;可选方案:jdbc

  • 实现模拟银行账户转账操作

模拟银行两个账户之间的转账,A账户向B账户转账100元,那A账户就减少100元,B账户增加100元。要求不管发生什么异常,都不能存在金额问题(要么转账成功,要么转账失败,两个账户的金额要始终正确)。可选方案:这个需求存在多次操作数据库,为了确保发生异常的时候可以恢复,必须使用事务,利用事务回滚

  • 实现调用接口返回页面以及返回对象信息

浏览器访问servlet后,servlet根据处理结果返回对象信息或者返回一个html页面;可选实现方案:servlet返回具体对象信息,可以通过response写回即可;servlet返回html页面,有两个方案:1.文件下载(读取html文件流,写回浏览器)2.转发/重定向请求到具体的html页面

 

编码实现:

按照上面的需求分析,我们来一一编码实现:

使用Session存储验证码,保证一次会话范围内共享:

/**
* 绘制验证码,生成验证码图片jpg,并返回给浏览器
* 绘制完成,将验证码存入session,key:checkcode_session
*/
@WebServlet("/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //暂存随机验证码字符
        StringBuilder strTemp = new StringBuilder("");

        int width = 80;
        int height = 30;
        //1.创建图片对象
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);

        //2.图片内容填充,绘制二维码
        Graphics g = image.getGraphics();
        //填充背景色
        g.setColor(Color.pink);
        g.fillRect(0,0,width,height);
        //画边框
        g.setColor(Color.blue);
        g.drawRect(0,0,width-1,height-1);
        //画验证码
        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        for (int i =0;i<4;i++){
            String checkCodeChar = str.charAt(new Random().nextInt(str.length()))+"";
            //验证码字符
            strTemp.append(checkCodeChar);
            g.drawString(checkCodeChar,width/5*(i+1),height/2);
        }
        //将验证码存储到session中
        request.getSession().setAttribute("checkcode_session",strTemp.toString());

        //画干扰线
        g.setColor(Color.green);
        Random random = new Random();
        for (int i=0;i<6;i++){
            g.drawLine(random.nextInt(width),random.nextInt(height),random.nextInt(width),random.nextInt(height));
        }

        //3.输出图片
        ImageIO.write(image,"jpg",response.getOutputStream());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

使用Filter拦截器实现全局拦截

/**
 * 全局登录拦截器(路径 /* 匹配所有路径)
 * 拦截除静态资源、login.jsp、login(LoginServlet)以及验证码CheckCodeServlet之外的所有请求
 * 验证登录信息,登录过则放行,否则转发请求到login.jsp,让用户先登录
 */
@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        //强制类型转换,为了拿到uri/url
        HttpServletRequest request = (HttpServletRequest) req;
        //获取资源路径
        String uri = request.getRequestURI();
        //判断是否包含登陆相关资源路径
        if (uri.contains("/login.jsp")||uri.contains("login") || uri.contains("/css/")
                || uri.contains("/js/") || uri.contains("/fonts/") || uri.contains("CheckCodeServlet")){
            //放行
            chain.doFilter(req, resp);
        } else {
            //不包含,则验证用户是否已经登录
            //注意与LoginServlet中保存的session一致
            Object user = request.getSession().getAttribute("user");
            if (user != null){
                //说明登陆过了,放行
                chain.doFilter(req, resp);
            } else {
                //没有登陆就试图访问系统,拦截,并跳转登录页面
                request.setAttribute("msg_error","您尚未登陆,请先登录");
                request.getRequestDispatcher("/login.jsp").forward(request,resp);
            }
        }
    }

    @Override
    public void destroy() {}
}

JDBC增删改查、分页、条件查询

/**
 * 数据库工具类
 */
public class JDBCUtil {
    private static final String driverClassName = "com.mysql.cj.jdbc.Driver";
    private static final String url = "jdbc:mysql://127.0.0.1:3306/mydb?useSSL=true&characterEncoding=utf-8&serverTimezone=GMT";
    private static final String username = "root";
    private static final String password = "***";


    static {
        try {
            Class.forName(driverClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(url,username,password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    public static void release(Connection conn, Statement stat, ResultSet rs){
        try {
            if (null != rs){
                rs.close();
            }
            if (null != stat){
                stat.close();
            }
            if (null != conn){
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


/**
 * 用户信息实体类,对应数据库表user_info
 */
public class UserInfo implements Serializable {
    private static final long serialVersionUID = -6620605799090612569L;

    private Integer id;
    private String username;
    private String password;
    private String address;
    private Integer age;

    //省略getter和setter
}

/**
 * 用户信息表数据库操作类
 * 1.根据主键id查询
 * 2.保存用户信息
 * 3.根据主键id删除用户
 * 4.根据主键id修改用户密码
 * 5.分页查询+条件查询
 */
public class UserInfoDao {

    /**
     * 根据主键id查询
     * @param id
     * @return
     */
    public UserInfo queryByPrimarykey(Integer id){
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        String sql = "select * from user_info where id = ?";
        try {
            ps = connection.prepareStatement(sql);
            ps.setInt(1,id);
            rs = ps.executeQuery();
            if (rs.next()){
                UserInfo userInfo = new UserInfo();
                userInfo.setUsername(rs.getString("username"));
                userInfo.setPassword(rs.getString("password"));
                return userInfo;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(connection,ps,rs);
        }
        return null;
    }

    /**
     * 保存用户
     * @param userInfo
     * @return
     */
    public boolean save(UserInfo userInfo){
        Connection conn = JDBCUtil.getConnection();
        PreparedStatement ps = null;
        boolean result = false;
        String sql = "INSERT INTO user_info (username,password) values (?,?)";
        try {
            ps = conn.prepareStatement(sql);
            ps.setString(1,userInfo.getUsername());
            ps.setString(2,userInfo.getPassword());
            result = ps.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(conn,ps,null);
        }
        return result;
    }

    /**
     * 删除用户
     * @param id
     * @return
     */
    public boolean delete(Integer id){
        Connection conn = JDBCUtil.getConnection();
        PreparedStatement ps = null;
        boolean result = false;
        String sql = "delete from user_info where id = ?";
        try {
            ps = conn.prepareStatement(sql);
            ps.setInt(1,id);
            result = ps.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(conn,ps,null);
        }

        return result;
    }


    /**
     * 更新用户密码
     * @param id
     * @param password
     * @return
     */
    public int update(Integer id,String password){
        Connection conn = JDBCUtil.getConnection();
        PreparedStatement ps = null;
        int count = 0;
        String sql = "update user_info set password = ? where id = ?";
        try {
            ps = conn.prepareStatement(sql);
            ps.setString(1,password);
            ps.setInt(2,id);
            count = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(conn,ps,null);
        }
        return count;
    }

    /**
     * 根据用户名和密码查询,用于账户登录
     * @param username
     * @param password
     * @return
     */
    public UserInfo queryByUsernameAndPassword(String username,String password){
        Connection connection = JDBCUtil.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        String sql = "select * from user_info where username = ? and password = ?";
        try {
            ps = connection.prepareStatement(sql);
            ps.setString(1,username);
            ps.setString(2,password);
            rs = ps.executeQuery();
            if (rs.next()) {
                UserInfo userInfo = new UserInfo();
                userInfo.setUsername(username);
                userInfo.setPassword(password);
                return userInfo;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(connection,ps,rs);
        }
        return null;
    }


    /**
     * 分页查询,支持条件查询
     * @param _currentPage
     * @param _rows
     * @param conditions
     * @return
     */
    public PageBean<UserInfo> findUserByPage(String _currentPage, String _rows, Map<String, String[]> conditions) {
        int currentPage = Integer.parseInt(_currentPage);
        int rows = Integer.parseInt(_rows);
        int totalCount = findTotalCount(conditions);
        //分页数
        int totalPage = (totalCount % rows == 0) ? (totalCount / rows) : (totalCount / rows + 1);
        //如果当前页《=0,则认为是第一页
        if (currentPage<=0){
            currentPage = 1;
        }
        //如果当前页》= totalpage,则认为是最后一页
        if (currentPage >= totalPage){
            currentPage = totalPage;
        }
        PageBean<UserInfo> pb = new PageBean<>();
        pb.setCurrentPage(currentPage);
        pb.setRows(rows);
        pb.setTotalCount(totalCount);
        pb.setTotalPage(totalPage);
        int start = (currentPage-1)*rows;
        pb.setUsers(queryUserByPage(start,rows,conditions));
        return pb;
    }

    /**
     * 条件查询,分页查询合并
     * @param start
     * @param rows
     * @param conditions
     * @return
     */
    private List<UserInfo> queryUserByPage(int start, int rows, Map<String, String[]> conditions) {
        String sql = "select * from user_info where 1=1 ";
        //遍历map,添加条件
        StringBuilder sb = new StringBuilder(sql);
        List<Object> params = new ArrayList<>();
        joinSQL(conditions, sb, params);
        //添加分页查询和参数
        sb.append(" limit ?,?");
        params.add(start);
        params.add(rows);
        Connection conn = JDBCUtil.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(sb.toString());
            for (int i = 1; i <= params.size(); i++) {
                ps.setObject(i,params.get(i-1));
            }
            rs = ps.executeQuery();
            ArrayList<UserInfo> users = new ArrayList<>();
            while (rs.next()){
                UserInfo user = new UserInfo();
                user.setUsername(rs.getString("username"));
                user.setPassword(rs.getString("password"));
                users.add(user);
            }
            return users;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(conn,ps,rs);
        }
        return null;
    }

    /**
     * 条件查询记录总数
     * @param conditions
     * @return
     */
    private int findTotalCount(Map<String,String[]> conditions){
        Connection conn = JDBCUtil.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        String sql = "select count(*) as count from user_info where 1=1 ";
        StringBuilder sb = new StringBuilder(sql);
        List<Object> params = new ArrayList<>();
        joinSQL(conditions,sb,params);
        System.out.println(sb.toString());
        try {
            ps = conn.prepareStatement(sb.toString());
            for (int i = 1; i <= params.size(); i++) {
                ps.setObject(i,params.get(i-1));
            }
            rs = ps.executeQuery();
            if (rs.next()){
                return rs.getInt("count");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(conn,ps,rs);
        }
        return 0;
    }


    //根据条件集,动态拼接sql语句
    private void joinSQL(Map<String, String[]> conditions, StringBuilder sb, List<Object> params) {
        for (String key : conditions.keySet()) {
            //仅符合设定条件的参数才可以添加
            if ("username".equals(key) || "address".equals(key) || "email".equals(key)) {
                String value = conditions.get(key)[0];
                //判断是否有值,有值的条件才会添加
                if (value != null && !"".equals(value.trim())) {
                    sb.append(" and " + key + " like ? ");
                    params.add("%" + value + "%");//条件值
                }
            }
        }
    }
}

使用事务包裹多次数据库操作,支持发生异常时回滚

/**
 * 事务管理类
 */
public class TransactionManager {
    private ThreadLocal<Connection> tl = new ThreadLocal<>();

    public TransactionManager() {
        if (null == tl.get()){
            tl.set(JDBCUtil.getConnection());
        }
    }

    public Connection getConnection(){
        return tl.get();
    }
    
    public void beginTransaction() throws SQLException {
        tl.get().setAutoCommit(false);
    }

    public void commit() throws SQLException {
        tl.get().commit();
    }

    public void rollback() throws SQLException {
        tl.get().rollback();
    }

    public void release() throws SQLException {
        tl.get().close();
        tl.remove();
    }
}


/**
 * 银行账户数据库操作类
 * 使用事务,支持回滚
 */
public class AccountDao {

    public boolean transferAccount(Integer from,Integer to,double money){
        PreparedStatement ps = null;
        ResultSet rs = null;
        boolean result = false;
        //使用事务
        TransactionManager manager = new TransactionManager();
        Connection conn = manager.getConnection();

        //第一步,查询账户是否余额充足
        String sql = "select money from bank_account where id = ?";
        try {
            //开始事务
            manager.beginTransaction();

            ps = conn.prepareStatement(sql);
            ps.setInt(1,from);
            rs = ps.executeQuery();
            double oldMoney = 0;
            if (rs.next()){
                oldMoney = rs.getDouble("money");
                if (money > oldMoney){
                    System.out.println("账户余额不足");
                    return false;
                }
            } else {
                System.out.println("查无此账户");
                return false;
            }

            //第二步,修改转出账户金额,减掉转出的钱
            sql = "update bank_account set money = ? where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setDouble(1,oldMoney - money);
            ps.setInt(2,from);
            int update = ps.executeUpdate();
            if (1 == update){
            } else {
                System.out.println("转出金额失败");
                return false;
            }

            //手动制造异常,测试事务回滚
            //int j = 1/0;

            //第三步,修改转入账户金额,加上转出的钱
            sql = "select money from bank_account where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,to);
            rs = ps.executeQuery();
            if (rs.next()){
                oldMoney = rs.getDouble("money");
            }
            sql = "update bank_account set money = ? where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setDouble(1,oldMoney + money);
            ps.setInt(2,to);
            update = ps.executeUpdate();
            if (1 == update){
                result = true;
            } else {
                System.out.println("转入金额失败");
                result = false;
            }

            //提交事务
            manager.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滚
            try {
                System.out.println("发生异常,回滚");
                manager.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            try {
                manager.release();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            JDBCUtil.release(conn,ps,rs);
        }
        return result;
    }
}

使用转发实现返回页面,response写回对象信息

@WebServlet("/htmlApi")
public class HtmlServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String path = req.getParameter("path");
        if (null == path && path.contains(".html")){
            //使用转发实现页面返回
            req.getRequestDispatcher(path).forward(req,resp);
        } else {
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().write("这是响应的具体信息!");
        }
    }
}

 

问题总结

经历过上面的几个具体需求实现,很明显有很多地方让我们实现得很痛苦,整个代码存在很多问题:高度耦合,很多重复代码等等。现在我们来一一分析下:

代码高度耦合,类实例缺少管理

为了实现用户信息表的增删改查,我们编写了一个UserInfoDao类,实现了一系列的增删改查方法。每一个Servlet需要用到的时候,都是先new UserInfoDao()创建一个Dao对象实例,然后调用对象方法。显然,这里对UserInfoDao对象实例是缺乏管理的,所有用到的地方都会先创建一个,存在代码耦合;其次每次创建一个对象实例都需要消耗cpu的处理时间片,每个对象实例都会占用一定的内存;再就是可能存在忘记释放无用对象造成内存泄漏。

这里优雅的实现应该是创建一个dao对象实例,全局共享,要用的时候去全局共享中取就好。既可以减少代码耦合,也可以全局管理dao对象实例,全生命周期,减少内存占用,避免内存泄漏。这就是spring框架的两大核心功能之一:IOC,关于IOC的具体实现可以参考《Java基础篇--反射和注解》--(实战)模拟spring框架,简单实现IOC

 

数据库操作实现复杂,存在大量重复编码

上面的UserInfoDao类,实现了对user_info表的增删改查、以及分页、条件查询等,可以看到每个方法实现都大致包含几个步骤:

1.获取数据库connection连接

2.编写sql语句

3.获取preparestatement对象实例;

4.设置条件参数

5.执行具体操作

6.解析ResultSet结果,封装为所需要的对象

7.释放rs、ps和conn对象等资源

显然,每个方法都存在相同的几个步骤(1,3,7),我们多次重复实现这些代码;其次不管什么操作,都需要我们自己编写sql语句,要求我们不仅要会java,还必须熟悉sql;另外每次查询返回的都是ResultSet对象,需要我们进行结果解析和封装;最主要的,当前实体类UserInfo和数据库表user_info之间是没有任何关联的,所有的都是依赖sql语句和结果封装。正因为存在这么多缺点,所以才导致Dao编码异常复杂,一个简单的分页查询,就编写了100多行代码;一个条件查询,需要多次拼接sql字串,而且非常容易出错;等等诸多痛苦相信在之前的实现过程中一定深有体会。为了解决这些问题,java大佬们封装了数据库操作框架也叫对象关系映射框架,目前主流的主要是两个:Hibernate和Mybatis,分别对应着后台开发经典架构SSH中的H和SSM中的M。关于这两大框架的功能特点和区别,详见《JavaEE--数据库框架篇》。

 

事务管理高耦合,高度代码重复

上例我们模拟了一个银行转账的操作,编写了一个AccountDao实现具体的转账逻辑。由于转账存在多次操作数据库:先要查询转出账户是否金额充足,然后要修改转出账户金额(减去转出的金额),再就是查询出转入账户的原有金额,拿原有金额加上转出金额,计算得出最后金额后,修改回转入账户。整个过程不能发生任何异常,否则就可能存在金额不一致问题,比如我们设置的:

            //手动制造异常,测试事务回滚
            int j = 1/0;

当异常发生时,如果没有事务回滚,则会发生转出账户已经扣掉了100元,但是转入账户并没有收到钱,这100元也就凭空消失了,显然这肯定是不允许发生的。为了保证转账的系列数据库操作准确无误,当异常发生时也能回退到原来的状态,所以加上了事务管理。具体步骤是:

  1. 实现事务管理工具类TransactionManager;
  2. 改写需要事务管理的方法,在方法起始处创建TransactionManager对象实例,并调用方法beginTransaction()开启事务;
  3. 对整个方法进行try-catch包裹,捕获方法抛出的所有异常,确保当任何异常发生的时候,都可以触发回滚,在catch方法体中加上rollback(),执行回滚;
  4. 在原方法尾加上commit(),提交事务,保证正常逻辑的数据库操作执行;
  5. 在finally方法体中释放事务管理相关资源。

整个实现过程:事务管理工具类是自己造轮子,对整个转账方法的改写是重复代码,所有的需要加上事务管理的方法都是同样的改写方式,而且当我们想加上事务的时候,需要去修改Dao,很不方便。正确的实现方式应该是:使用代理对需要事务管理的方法进行动态增强。关于动态代理,如果还不熟悉,可以回顾《Java基础篇--设计模式》--代理模式。这正是spring框架的两大核心功能之一:AOP。主要的实现步骤是:通过IOC创建对象实例(比如Dao)的同时,扫描其方法上是否有事务注解(@transaction),如果有,则创建动态代理,对该方法进行事务包裹,动态增强该方法。大致代码实现如下:(代码中用就是我们造的轮子TransactionManager)

/**
 * 用于创建service的代理对象工厂
 */
@Component
public class BeanFactory {
    @Autowired
    private IAccountService accountService;

    @Autowired
    private TransactionManager txManager;

    /**
     * 获取代理对象
     * @return
     */
    public IAccountService getAccountService() {
        Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces()
                , new InvocationHandler() {
                    /**
                     * 添加事务支持
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object rtValue = null;
                        try {
                            //01.开始事务
                            txManager.benginTransaction();
                            rtValue = method.invoke(accountService, args);
                            //02.提交事务
                            txManager.commit();
                            return rtValue;
                        } catch (Exception e) {
                            //03.回滚事务
                            txManager.rollback();
                            throw new RuntimeException(e);
                        } finally {
                            //04.释放连接
                            txManager.release();
                        }
                    }
                });
        return accountService;
    }
}

 

Servlet返回是字串还是页面都不方便

在上例通过HtmlServlet体验了如何返回html或者返回json字串。返回html页面,我们知道其实就是文件下载,这里我们省了个懒,利用Tomcat容器的特性使用转发来直接返回页面资源;返回json字串(后台返回的时候不管是具体对象,还是字符串,都是转换为json字串的形式再传输),就是通过response写回信息即可,需要注意的是要先设置好response响应头的格式(contentType)。

显然这里也是可以更加优雅的实现,减少重复代码:1.可以在容器初始化设定封装HttpServletResponse对象时需要设置的ContentType,保证所有的response都是预设了ContentType,不用在每个servlet中都设置一下;2.通过注解,表明当前servlet是返回页面,还是返回字串。对于返回页面的,自动通过视图映射找到对应的页面资源,返回给浏览器;对于返回字串信息的,自动通过response写回。视图解析器、视图映射、区分结果返回等功能正是spring mvc的核心功能。

 

经历过了自己造轮子,繁复的数据库操作实现,重复的事务控制代码,手动if判断来达到视图映射等等。可以看到不使用任何框架直接来开发后台应用,还是很繁琐的,而且还不能保证实现的效果(俗称出力不讨好,事倍功半)。实际的后台服务开发还是要从经典SSH或者SSM开始,SSH(Spring  SpringMVC  Hibernate) SSM(Spring SpringMVC Mybatis)。下一篇将具体讲解使用SSM开发后台服务,体验一下畅快的后台开发,让你爱上编码,爱上这种感觉!


以上系个人理解,如果存在错误,欢迎大家指正。原创不易,转载请注明出处!

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 1024 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值