“ 作业帮 “ (Servlet)(下)

简介: “ 作业帮 “ (Servlet)(下)

五、准备好纯前端页面



我认为,在约定前后端的访问路径、HTTP 中正文的内容这些东西之前,需要有一个前端的页面,或者说,需要有一个前端的基本框架。这样一来,就方便前后端的交互了。如果有一个基本的页面,前后端就不至于摸不着头脑,凭空去设计了。


对于前端来说,程序员就明确了哪些地方需要设置点击事件、哪些地方可以写成静态页面、哪些地方可以写成链接的形式了。


对于后端来说,最重要的就是提供 HTTP 响应中的数据,大多数都是以 json 格式写入报文中的,写入数据之前,也要明确前端用这个数据来干什么。


对于前后端交互的接口,后面我会着重介绍,前端需要利用 JS-WebAPI 来实现,后端需要利用 Servlet 实现。现在,请看下面两幅基本的结构图:


OJ 列表页


image.png


OJ 详情页


不知道为什么,详情页通过长截图截不下来,下面两幅图是一个页面。


f0e7f4300dc54a81ad193030cb408fb2.png

247aabb9f8ee4d30abff4ea479ee03f6.png


部署到项目的 webapp 目录下


将纯前端的所有代码文件,都复制到项目的 webapp 目录下,以备后用。后面前端通过 ajax 或 form 表单的形式构造 HTTP 请求,就可以直接对项目中的前端文件进行修改。


b0eb28f8c270438ba41f58d469323b2c.png


六、实现题目列表页



作用:题目列表页主要用来展示所有题目的摘要 ( 编号、标题、难度 )


前端


约定 GET 请求 的路径:" /subjectList "(前端通过 ajax 这种方式来构造请求)


前端代码: 先按照纯前端代码创建相应的节点,之后再挂在 DOM 树上。此外,这里通过 a 标签,约定跳转链接的路径,题目列表页与题目详情页通过 " id " 这个参数进行连接。


<script>
    $.ajax({
        url: "subjectList",
        method: "GET",
        success: function(data, status) {
            // 从 HTTP 响应的正文中获取到的数据赋值给 subjectLists,名字正好对应起来
            let subjectList = data;
            let tbody = document.querySelector(".subjectTable");
            for( let subject of subjectList ) {
                let tr = document.createElement("tr");
                // 题目编号
                let idTd = document.createElement("td");
                idTd.innerHTML = subject.id;
                // 题目标题
                let titleA = document.createElement("a");
                let titleTd = document.createElement("td");
                titleA.innerHTML = subject.title;
                titleA.href = "oj_content.html?id=" + subject.id;
                // 题目难度
                let levelTd = document.createElement("td");
                levelTd.innerHTML = subject.level;
                tr.appendChild(idTd);
                titleTd.appendChild(titleA);
                tr.appendChild(titleTd);
                tr.appendChild(levelTd);
                tbody.appendChild(tr);
            }
        }
    })
</script>


后端


服务器端代码:创建一个 SubjectListServlet 来处理计算响应,在此类中,我们为 HTTP 响应的正文 body 写入 json 格式的数据。


@WebServlet("/subjectList")
public class SubjectListServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置 HTTP 响应的正文格式为 json
        resp.setContentType("application/json; charset=UTF-8");
        SubjectDB subjectDB = new SubjectDB();
        // 从数据库中选取所有题目的信息,保存在一个顺序表中
        List<Subject> subjectList = subjectDB.selectAll();
        // 将 顺序表这个 Java 对象转换成 json 格式的数据,并写入 HTTP 响应的正文中
        String jsonData =  objectMapper.writeValueAsString(subjectList);
        resp.getWriter().write(jsonData);
    }
}


七、实现题目详情页



作用:题目详情页用来展示题目描述、代码编辑框、测试用例,最重要的是,这里需要提交代码到服务器端。


1. 题目描述和代码编辑框


前端


约定 GET 请求 的路径:" /subjectContent " + location.search(前端通过 ajax 这种方式来构造请求)

location.search 就对应着 " id " 这样的参数


270573aa652740d5b5aba580a2cf0400.png


前端代码: 思想与之前一样,先创建节点,后挂在 DOM 树上。


<script>
    $.ajax({
        url: "subjectContent" + location.search,
        method: "GET",
        success:function(data, status) {
            let subject = data;
            // 题目描述
            let desc1 = document.querySelector(".desc1");
            desc1.innerHTML = subject.id + ".  " + subject.title + " [" + subject.level + "]";
            let desc2 = document.querySelector(".desc2");
            desc2.innerHTML = subject.description;
            // 代码编辑框
            let text = document.querySelector(".form-group textarea");
            text.innerHTML = subject.codeTemplate;
        }
    })
</script>


后端


服务器端代码: 创建一个 SubjectContentServlet 来处理计算响应,在此类中,我们先从 HTTP 请求的 " query string " 中读取 " id " 参数 ,之后根据这个参数来找到对应题目的所有详细数据,并为 HTTP 响应的正文 body 写入 json 格式的数据。


@WebServlet("/subjectContent")
public class SubjectContentServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 以 utf8 编码的方式从 HTTP 请求的正文中读数据
        req.setCharacterEncoding("utf8");
        resp.setContentType("application/json; charset=UTF-8");
        // 获取 参数 id
        String id = req.getParameter("id");
        SubjectDB subjectDB = new SubjectDB();
        Subject subject = subjectDB.selectOne(Integer.parseInt(id));
        // 将 subject 这个 Java 对象转换成 json 格式的数据,并写入 HTTP 响应的正文中
        String jsonData = objectMapper.writeValueAsString(subject);
        resp.getWriter().write(jsonData);
    }
}


注意事项: 我们都知道前端需要从 HTTP 响应的正文中拿到 json 数据,然而,在一大段文字中,我们不但要注意编码格式,同时也要注意,html 是否能够识别数据库的一些符号。


就拿下面的例子来说,Java 一开始往数据库中插入数据的时候,使用 " \n " 作为换行符,所以数据库中的换行符也是 " \n “,然而,在 html 的语法中,” br " 标签才是换行符,所以最终就是,html 并不能识别 " \n " 符号,如果想让 html 能够识别一些特殊符号,就需要为文本套上 " pre " 标签才行。如下代码:


<pre> 
    <p class="desc2"></p> 
</pre>

image.png



2. 提交代码和测试用例


前端


约定 GET 请求 的路径:" /compile "(前端通过 ajax 这种方式来构造请求)

前端代码思想:


(1) 将 " 提交代码 " 按钮设置为一个点击事件,为点击事件设置一个函数,里面使用 ajax 来发送 POST 请求,在请求中,最关键的就是要将用户提交的代码,以 json 的格式传入到 HTTP 请求的正文中。


(2) 如果 POST 请求发送成功后,后端也返回了响应,这个时候,就可以拿着 " 编译运行 " 后的测试数据,展现在前端页面上。


// 提交代码到服务器端
let sbutton = document.querySelector(".sbutton");
sbutton.onclick = function() {
    // 将 题目id 和 我们自己编写的代码封装成一个 body 对象
    let body =  {
        id: subject.id,
        code: template.value
    };
    $.ajax({
        url: "compile",
        method: "POST",
        // 将 body 对象写入 HTTP 请求的正文中,以 json 的格式存放在 HTTP 正文中
        data: JSON.stringify(body),  
        success: function(data, status) {
            // 这里 data 读到的就是测试用例是否通过、以及出错的原因...等等一些数据
            // 状态码 【 0 表示运行无误、1 表示编译出错、2 表示运行出错 】
            let respStatus = document.querySelector(".container .status");
            respStatus.innerHTML = "status: " + data.status + " ( 0 表示运行无误、1 表示编译出错、2 表示运行出错 )";
            // 出错的解释
            let respReason = document.querySelector(".container .reason");
            respReason.innerHTML = "reason: " + "</br>" + data.reason;
            // 运行无误的结果
            let respStdout = document.querySelector(".container .stdout");
            respStdout.innerHTML = "stdout: " +  "</br>" + data.stdout;
            // 运行有误的结果
            let respStderr = document.querySelector(".container .stderr");
            respStderr.innerHTML = "error: " + data.stderr;
        }
    })
}


后端


服务器端代码思想:


(1) 读取 HTTP 请求中的数据,即刚刚用户提交的代码,而这个提交的代码是一个 json 格式的数据,那么,我们就需要将其转换成一个实体类,以便于后面 Java 对象的使用。

(2) 将 用户提交的代码 和 我们自己设计的测试用例,融合在一起。

(3) 将刚刚拼接好的代码,创建编译任务、创建执行任务。

(4) 把测试完的结果,包装成一个实体类,再以 json 格式写入 HTTP 响应中。


@WebServlet("/compile")
public class CompileServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();
    // 将从 HTTP 请求中读取的 json 数据封装成一个实体类
    static class CompileRequest{
        // 题目 id
        public int id;
        // 用户提交的代码
        public String code;
        @Override
        public String toString() {
            return "CompileRequest{" +
                    "id=" + id +
                    ", code='" + code + '\'' +
                    '}';
        }
    }
    // 将返回的数据封装成一个实体类,以备后续写入 HTTP 响应中
    static class CompileResponse{
        // 状态码 【 0 表示运行无误、1 表示编译出错、2 表示运行出错 】
        public int status;
        // 出错的解释
        public String reason;
        // 运行无误的结果
        public String stdout;
        // 运行有误的结果
        public String stderr;
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 临时加一个这样的代码,来获取到 SmartTomcat 的工作目录
        System.out.println("用户当前的工作目录: " + System.getProperty("user.dir"));
        req.setCharacterEncoding("utf8");
        resp.setContentType("application/json; charset=UTF-8");
        CompileRequest compileRequest = new CompileRequest();
        // 1. 读 HTTP 请求的数据
        // readBody 方法专门用来读取 HTTP 请求的正文中的数据
        // 拿字符串来接收 HTTP 请求中的数据,一来可以很好地验证是否接受到了数据;二来可以方便后续的使用
        String body = readBody(req);
        //System.out.println(body); // 验证
        // 将 HTTP 请求中 json 数据转换成一个 Java 实体类,以备后续使用 Java 对象
        compileRequest = objectMapper.readValue(body, CompileRequest.class);
        //System.out.println(compileRequest); // 验证
        // 代码走到这里,说明刚刚用户在前端提交的代码,已经完完全全地以 Java 的形式放入了 compileRequest 对象中
        // 接下来要做的就是,将前端代码与测试用例一拼接,来验证代码是否正确,
        // 而从 HTTP 请求传过来的 id 就能够找到当前是哪一题,从而就能找到当前这一题的测试用例
        // 2. 融合代码
        SubjectDB subjectDB = new SubjectDB();
        Subject subject = subjectDB.selectOne(compileRequest.id);
        String submitCode = compileRequest.code; // 用户提交的代码
        String testCode = subject.getTestCase(); // 测试用例的代码
        String finalCode = mergeCode(submitCode, testCode);
        // 代码走到这里,说明两个代码已经合并完成
        // 接下来要做的是,将刚刚融合的代码,用来编译,用来运行
        // 3. 创建任务。并执行任务
        Task task = new Task();
        Answer answer = task.compileRun(finalCode);
        // 4. 将编译运行后的结果存入实体类中
        CompileResponse compileResponse = new CompileResponse();
        compileResponse.status = answer.getStatus();
        compileResponse.reason = answer.getReason();
        compileResponse.stdout = answer.getStdout();
        compileResponse.stderr = answer.getStderr();
        // 5. 将 Java 转换成 json 格式的数据,并写入 HTTP 响应的正文中
        String jsonData = objectMapper.writeValueAsString(compileResponse);
        resp.getWriter().write(jsonData);
    }
    /**
     * 将用户提交的代码与测试用例拼接
     * 方法:
     * <1> 将用户提交的代码的最后一个大括号去掉
     * <2> 将用户提交的代码直接去拼接测试用例的代码
     * <3> 补全最后一个大括号
     */
    private String mergeCode(String submitCode, String testCode) {
        // <1>
        int index = submitCode.lastIndexOf("}");
        String newStr = submitCode.substring(0, index);
        // <2> + <3>
        return newStr + testCode + "\n" +"}";
    }
    /**
     * 以字节流的方式读取 HTTP 请求中的正文数据
     */
    private String readBody(HttpServletRequest req) throws IOException {
        // 得到 HTTP 请求正文的字节总数
        int contentLength = req.getContentLength();
        // 以刚刚得到的字节总数,new 一个字节数组
        byte[] bytes = new byte[contentLength];
        // 以流对象的形式获取 HTTP 请求的正文
        InputStream inputStream = req.getInputStream();
        // 一次性将正文中所有的数据读到字节数组中
        inputStream.read(bytes);
        // 将字节数组构造成一个字符串,并返回
        return new String(bytes, "utf8");
    }  
}


融合代码的思想:


融合代码的思想如下所示,其实就是利用了字符串提供的一些方法,来实现字符串的拼接过程而已,这部分的思想,如果平时有小伙伴经常刷字符串的题,很好理解。


image.png


示例:


下面是我自己设计的一个拼接测试,看看输出就能很好理解。


public class Test {
    public static void main(String[] args) {
        String str1 = "abc}}";
        String str2 = "xyz";
    // 获取到最后一个大括号的位置
        int index = str1.lastIndexOf("}");
        System.out.println(index); // 4
    // substring 遵循左闭右开
        String str3 = str1.substring(0, index);
        System.out.println(str3); // abc
    // 两者对比
        System.out.println(str1 + str2);
        System.out.println(str3 + str2);
    }
}


输出结果:


image.png


八、优化项目



1. 处理异常


当我提交的代码如下所示,这就会造成服务器端出现异常,那么客户端最终为用户呈现的可能就是 " 500 " 这样的状态码,此时如果刷题的人是一个小白用户,那么他就很懵。所以,作为开发人员,应该在后端将这些问题考虑进去。


class Solution {
    public int[] twoSum(int[] nums, int target) {


优化方案:


通过 try - catch - finally,将异常查出来,并为非法代码设置一个额外提示。


try{  
    String submitCode = compileRequest.code; // 用户提交的代码
    String testCode = subject.getTestCase(); // 测试用例的代码
    String finalCode = mergeCode(submitCode, testCode);
    if (finalCode == null) {
        throw new RuntimeException();
    }
} catch (RuntimeException e) {
    // 处理一些代码不合法的异常
    compileResponse.status = 2;
    compileResponse.reason = "代码不合法";
    compileResponse.stdout = null;
    compileResponse.stderr = null;
} finally {
    // 5. 将 Java 转换成 json 格式的数据,并写入 HTTP 响应的正文中
    String jsonData = objectMapper.writeValueAsString(compileResponse);
    resp.getWriter().write(jsonData);
}


2. 校验代码安全性


之前我提到,有些用户提交的代码就是不安全的,例如下面的代码:


学过 Linux 的小伙伴都知道,如果用户故意搞破坏,写了下面的语句,就直接对操作系统上的文件进行操作,而下面的 " rm -rf / " 就表示,将 Linux 系统上的数据全部清空,这是一个不可逆操作,很危险。所以作为后端开发人员,依旧要考虑这些特殊情况,毕竟我们控制不了别人的思想,但我们能够阻止类似危险的情况发生。


Runtime runtime = Runtime.getRuntime();
 Process process = runtime.exec("rm-rf /");


优化方案:


在 Task 类中,专门写一个函数,用来校验安全,在函数中,先要明确哪些是危险操作,之后,遍历用户提交的代码字符串,只要发现有危险代码,直接返回错误信息。


// 检验代码安全性
if (!checkCodeSafe(preparedCode)) {
    System.out.println("用户提交了不安全的代码");
    answer.setStatus(1);
    answer.setReason("您提交的代码可能会危害到服务器,禁止运行!");
    return answer;
}
...
/**
 * 检验代码安全性
 */
private boolean checkCodeSafe(String preparedCode) {
    // 创建一个黑名单
    List<String> blackList = new ArrayList<>();
    // 防止提交的代码运行恶意程序
    blackList.add("Runtime");
    blackList.add("exec");
    // 禁止提交的代码读写文件
    blackList.add("java.io");
    // 禁止提交的代码访问网络
    blackList.add("java.net");
    for (String target : blackList) {
        int pos = preparedCode.indexOf(target);
        if (pos >= 0) {
            // 找到任意的恶意代码特征,返回 false 表示不安全
            return false;
        }
    }
    return true;
}


3. 利用 UUID 生成不同目录


按照之前 Task 类的设计,后端为用户提交的代码进行测试,测试完之后,都是放到了一个固定目录 temp 下,然而,下一次提交、下下一次提交,就会覆盖之前的代码,长此以往,我们看到的测试信息永远是最新的,这很不合理。


所以解决上述问题的思想就是:我们可以使用 " 唯一 ID " 来为不同时刻生成不同目录,典型的方法就是使用 " UUID “,” UUID " 是计算机中常用的概念,表示 " 全世界唯一的 ID ",Java 也为我们提供了一个方法,请继续往下看。


优化方案:


(1) 首先,我们在 CompileServlet 类的开头,添加下面语句,方便后面我们找到 Tomcat 底下的目录。


// 临时加一个这样的代码,来获取到 SmartTomcat 的工作目录
System.out.println("用户当前的工作目录: " + System.getProperty("user.dir"));


(2) 其次,我们重新设置目录,取消之前的 " final " 关键字,利用 Task 类的构造方法,外部每一次 new 一个 Task 对象,都会重新生成一个目录。


public class Task {
    // 将一些 "编译+运行" 的临时文件放在此目录下
    private static String WORK_DIR;
    // 表示 ".java 文件",里面放着待编译的代码,等待 "javac" 命令编译
    private static  String PREPARED_CODE;
    // 表示 ".class 文件",里面放着二进制字节码,等待 "java" 命令运行
    private static  String CLASS_FILE;
    // 将 "编译出错" 的信息放在此文件中
    private static  String COMPILE_ERROR;
    // 将 "运行无误" 的信息放在此文件中
    private static  String STDOUT;
    // 将 "运行出错" 的信息放在此文件中
    private static  String STDERR;
    public Task() {
        WORK_DIR = "./temp/" + UUID.randomUUID().toString() + "/";
         PREPARED_CODE = WORK_DIR + "Solution.java";
         CLASS_FILE = "Solution";
         COMPILE_ERROR = WORK_DIR +"compile_error.txt";
         STDOUT = WORK_DIR +"stdout.txt";
         STDERR = WORK_DIR +"stderr.txt";
    }
}



4. 引入合理的代码编辑框


之前我们在前端页面写的代码编辑框,是利用 " textarea " 生成的,它只能够用来多行输入,并不能使用 " 代码补全 " 、" 语法高亮 " …等一系列代码优化操作,所以我们考虑从第三方库引入一个新的代码编辑框,提高用户体验。


引入的第三库名为 " ace.js ",这部分的代码我就不展示了,都是前端的一些固定写法,我们可以根据自己的需要来自定义代码编辑框。


image.png


页面展示结果

题目列表页


image.png


题目详情页


image.png



总结页面的交互逻辑



题目列表页


57af1760cd66414ca9ce69a01894cb0d.png


题目详情页


题目描述和代码编辑框


c4de90a1dbc84128a5f1a45e4ceb96a0.png


提交代码和测试用例


此过程是整个 OJ系统 最核心的地方,后端不但需要从 HTTP 请求中拿数据,也需要往 HTTP 响应中放数据。而在拿放数据之间,不但需要用到 json 数据与 Java 之间的转换,还需要通过 " 编译 + 运行 " 机制 进行检测代码是否合理。


c45810cd2eae40f18138513b5c8cee8b.png


总结



这是我做的第二个独立项目,刚开始觉得很难,不管是使用 Java 流对象来操作文件,还是通过 " javac " 和 " java " 这两个命令来编译运行,这些都让我有些措手不及。但


实际上,它确实很难,不仅要考虑到前后端交互的约定,还需要考虑到后端对于用户提交过来代码的业务处理,很多细节都需要顾虑到。


例如:后端需要考虑用户不能直接收到 " 500 " 这样状态码,后端应该人性化地考虑到每个用户使用的场景,以及提交之后发生的异常情况。


再例如:测试用例的设计是一个很不好处理的事情,因为每道题的测试用例不一样,而且每道题的测试情况,一般人是很难考虑周全。而当前,我只是用到了代码拼接这一简单的逻辑,但实际上,代码效率并不高。


相比于之前写的博客系统的项目中,我发现自己又进步了一点,实际上准确地说,自己掌握了更多细节的地方,以及更加 Java 面向对象编程的思想。


不管是前端,还是数据库,Java 始终是作为一个很重要的角色存在,很多操作都是需要先有类,再生成 Java 对象,最后才能进行与前端和数据库沟通。此外,HTTP 协议用的越来越熟了,可能是因为实践多了的缘故,这次项目,我抓包更少了,遇到问题,调试的更快了。


目录
相关文章
|
SQL 存储 JSON
“ 作业帮 “ (Servlet)(上)
“ 作业帮 “ (Servlet)(上)
132 0
“ 作业帮 “ (Servlet)(上)
|
4月前
|
缓存 安全 Java
Java服务器端技术:Servlet与JSP的集成与扩展
Java服务器端技术:Servlet与JSP的集成与扩展
41 3
|
4月前
|
存储 缓存 前端开发
Servlet与JSP在Java Web应用中的性能调优策略
Servlet与JSP在Java Web应用中的性能调优策略
42 1
|
4月前
|
存储 Java 关系型数据库
基于Servlet和JSP的Java Web应用开发指南
基于Servlet和JSP的Java Web应用开发指南
79 0
|
4月前
|
前端开发 安全 Java
在Java服务器端开发的浩瀚宇宙中,Servlet与JSP犹如两颗璀璨的明星,它们联袂登场,共同编织出动态网站的绚丽篇章。
在Java服务器端开发的浩瀚宇宙中,Servlet与JSP犹如两颗璀璨的明星,它们联袂登场,共同编织出动态网站的绚丽篇章。
31 0
|
6月前
|
自然语言处理 前端开发 Java
Servlet与JSP:Java Web开发的基石技术详解
【6月更文挑战第23天】Java Web的Servlet与JSP是动态网页的核心。Servlet是服务器端的Java应用,处理HTTP请求并响应;JSP则是结合HTML与Java代码的页面,用于动态内容生成。Servlet通过生命周期方法如`init()`、`service()`和`destroy()`工作,而JSP在执行时编译成Servlet。两者在MVC架构中分工,Servlet处理逻辑,JSP展示数据。尽管有Spring MVC等框架,Servlet和JSP仍是理解Web开发基础的关键。
114 12
|
6月前
|
存储 Java 关系型数据库
基于Servlet和JSP的Java Web应用开发指南
【6月更文挑战第23天】构建Java Web应用,Servlet与JSP携手打造在线图书管理系统,涵盖需求分析、设计、编码到测试。通过实例展示了Servlet如何处理用户登录(如`LoginServlet`),JSP负责页面展示(如`login.jsp`和`bookList.jsp`)。应用基于MySQL数据库,包含用户和图书表。登录失败显示错误信息,成功后展示图书列表。部署到Tomcat服务器测试功能。此基础教程为深入Java Web开发奠定了基础。
131 10
|
6月前
|
缓存 小程序 前端开发
Java服务器端技术探秘:Servlet与JSP的核心原理
【6月更文挑战第23天】Java Web开发中的Servlet和JSP详解:Servlet是服务器端的Java小程序,处理HTTP请求并响应。生命周期含初始化、服务和销毁。创建Servlet示例代码展示了`doGet()`方法的覆盖。JSP则侧重视图,动态HTML生成,通过JSP脚本元素、声明和表达式嵌入Java代码。Servlet常作为控制器,JSP处理视图,遵循MVC模式。优化策略涉及缓存、分页和安全措施。这些技术是Java服务器端开发的基础。
69 9
|
6月前
|
缓存 安全 Java
Java服务器端技术:Servlet与JSP的集成与扩展
【6月更文挑战第23天】Java Web开发中,Servlet和JSP是构建动态Web应用的基础。Servlet处理逻辑,JSP专注展示。示例展示了Servlet如何通过`request.setAttribute`传递数据给JSP渲染。JSP自定义标签提升页面功能,如创建`WelcomeTag`显示欢迎消息。Servlet过滤器,如`CacheControlFilter`,用于预处理数据或调整响应头。这些集成和扩展技术增强了应用效率、安全性和可维护性,是Java服务器端开发的关键。
73 7
|
6月前
|
前端开发 安全 Java
Java服务器端开发实战:利用Servlet和JSP构建动态网站
【6月更文挑战第23天】**Servlet和JSP在Java Web开发中扮演关键角色。Servlet处理业务逻辑,管理会话,JSP则结合HTML生成动态页面。两者协同工作,形成动态网站的核心。通过Servlet的doGet()方法响应请求,JSP利用嵌入式Java代码创建动态内容。实战中,Servlet处理数据后转发给JSP展示,共同构建高效、稳定的网站。虽然新技术涌现,Servlet与JSP仍为Java Web开发的基石,提供灵活且成熟的解决方案。**
87 8