在线OJ项目
项目简介
核心功能
- 题目的管理:(题目信息:题干+测试用例)
- 题目列表页:展示题目列表
- 题目详情页:展示题目的详细信息+代码编辑框
- 提交运行题目:点击按钮,网页把源代码交给服务器服务器执行代码,返回是否通过以及测试用例。
- 查看运行结果:展示上次提交是否通过,错误测试用例,历史提交记录.
- 登录页:通过用户名密码登录.
实现效果
登录页面
题目列表页
题目详情页
编译模块设计
实现通过命令行调用程序
创建compile包,CommandUtil类
1.掌握标准输入,标准输出,标准错误的概念
- 标准输入:从标准输入设备(通常为键盘)读取数据,用户输入数据按Enter键输入数据变得可用,read()方法每次返回一个字节的数据.
- 标准输出:显示器.
- 标准错误:显示器,显示错误信息.
2实现手动重定向过程,对标准输出,标准错误进行重定向
3.exec执行过程是异步,使用waitFor方法阻塞等待命令执行结束.
public class CommandUtil {
public static int run(String cmd,String stdoutFile,String stderrFile) {
try {
//1.通过Runtime类得到Runtime实例,执行exec方法
Process process=Runtime.getRuntime().exec(cmd);
//2.获取到标准输出,并写入到指定文件中
if(stdoutFile != null) {
InputStream stdoutFrom = process.getInputStream();
FileOutputStream stdoutTo = new FileOutputStream(stdoutFile);
while (true) {
int ch=stdoutFrom.read();
if(ch == -1) {
break;
}
stdoutTo.write(ch);
}
stdoutFrom.close();
stdoutTo.close();
}
//3.获取到标准错误,并写入到指定文件中
if (stderrFile != null) {
InputStream stderrFrom = process.getErrorStream();
FileOutputStream stderrTo = new FileOutputStream(stderrFile);
while (true) {
int ch = stderrFrom.read();
if(ch == -1) {
break;
}
stderrTo.write(ch);
}
stderrFrom.close();
stderrTo.close();
}
//4.等待子进程结束,拿到子进程的状态码,并返回
int exitCode=process.waitFor();
return exitCode;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return 1;
}
}
验证CommandUtil类
public static void main(String[] args) {
CommandUtil.run("javac","stdout.txt","stderr.txt");
}
实现编译运行过程
在compile包下,创建以下类:
创建Question类
Question类,代表输入的代码.
public class Question {
private String code;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
创建Answer类
Answer类代表输出内容,Answer类中包含错误码,错误提示,运行程序得到的标准输出,运行程序得到的标准错误。
public class Answer {
//错误码,约定error为0表示编译运行都ok,为1表示编译出错,为2表示运行出错
private int error;
//出错的提示信息 如果error为1编译出错,reason中放编译错误信息,为2运行异常,reason放异常错误信息
private String reason;
//运行程序得到的标准输出
private String stdout;
//运行程序得到的标准错误
private String stderr;
public int getError() {
return error;
}
public void setError(int error) {
this.error = error;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public String getStdout() {
return stdout;
}
public void setStdout(String stdout) {
this.stdout = stdout;
}
public String getStderr() {
return stderr;
}
public void setStderr(String stderr) {
this.stderr = stderr;
}
@Override
public String toString() {
return "Answer{" +
"error=" + error +
", reason='" + reason + '\'' +
", stdout='" + stdout + '\'' +
", stderr='" + stderr + '\'' +
'}';
}
}
创建Task类
Task类,每一次的编译运行称为一个Task。
public class Task {
public Answer compileAndRun(Question question) {
//1.创建用来存放临时文件的目录
//2.根据Question创建临时文件
//3.构造编译命令并执行
//4.构建运行命令并执行
//5.将运行结果打包到最终的Answer对象中
}
}
约定临时文件名
为了有效进行”进程间的通信”,首先我们使用一组常量来约束临时文件的名字。
//临时文件所在目录
private static final String WORK_DIR="./tmp/";
//约定代码类名
private static final String CLASS="Solution";
//约定要编译的代码文件名
private static final String CODE=WORK_DIR+"Solution.java";
//约定存放编译错误信息的文件名
private static final String COMPILE_ERROR=WORK_DIR+"compileError.txt";
//约定存放运行时标准输出的文件名
private static final String STDOUT=WORK_DIR+"stdout.txt";
//约定存放运行时标准错误的文件名
private static final String STDERR=WORK_DIR+"stderr.txt";
创建FileUtil类
创建common包,创建FileUtil类进行文件的读写操作.
为了方便后面的文件操作,先进行简单封装.
创建common包,创建FileUtil类
public class FileUtil {
//负责把filePath对应的文件内容读出来,放到返回值中
public static String readFile(String filePath) {
StringBuilder result=new StringBuilder();
try(FileReader fileReader=new FileReader(filePath)) {
while (true) {
int ch= fileReader.read();
if(ch==-1) {
break;
}
result.append((char)ch);
}
} catch (IOException e) {
e.printStackTrace();
}
return result.toString();
}
//负责把content写入到filePath对应的文件中
public static void writeFile(String filePath,String content) {
try(FileWriter fileWriter=new FileWriter(filePath)) {
fileWriter.write(content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
实现Task.compileAndRun方法
使用compileAndRun方法封装编译命令,并得到编译结果.
complieAndRun方向实现步骤
- 创建用来存放临时文件的目录。
- 把question中的内容写到指定的Solution.java文件中。
- 创建子进程调用javac命令进行编译。
- 创建子进程调用java命令并执行。
- 父进程获取到编译执行结果,打包成Answer对象。
public class Task {
//参数:要编译运行的java源代码
//返回值:表示编译运行的结果:编译出错/运行出错/运行正确
public Answer compileAndRun(Question question) {
Answer answer=new Answer();
//0.创建用来存放临时文件的目录
File workDir=new File(WORK_DIR);
if(!workDir.exists()) {
//创建多级目录
workDir.mkdirs();
}
//1.把question中的code写入到一个Solution.java文件中
FileUtil.writeFile(CODE,question.getCode());
//2.创建子进程,调用javac进行编译
//先构造编译命令
String compileCmd=String.format("javac -encoding utf8 %s -d %s",CODE,WORK_DIR);
//对应javac进程来说,标准输出不关注,关注标准错误,一旦编译出错内容就会通过标准错误来反馈
CommandUtil.run(compileCmd,null,COMPILE_ERROR);
//如果编译出错,错误信息就会放到COMPILE_ERROR这个文件中,没有出错文件为空
String compileError=FileUtil.readFile(COMPILE_ERROR);
if(!compileError.equals("")) {
//编译错误 直接返回Answer 约定1表示编译出错
System.out.println("编译出错");
answer.setError(1);
answer.setReason(compileError);
return answer;
}
//3.创建子进程,调用java命令并执行
String runCom = String.format("java -classpath %s %s",WORK_DIR,CLASS);
CommandUtil.run(runCom,STDOUT,STDERR);
String runError=FileUtil.readFile(STDERR);
if(!runError.equals("")) {
System.out.println("运行出错");
answer.setError(2);
answer.setReason(runError);
return answer;
}
//4.父进程获取到刚才的编译执行的结果,并打包成Answer对象
answer.setError(0);
answer.setStdout(FileUtil.readFile(STDOUT));
return answer;
}
}
验证Task类
public static void main(String[] args) {
Task task=new Task();
Question question=new Question();
question.setCode("public class Solution {\n" +
" public static void main(String[] args) {\n" +
" System.out.println(\"hello world\");\n" +
" }\n" +
" \n" +
"}");
Answer answer=task.compileAndRun(question);
System.out.println(answer);
}
题目管理模块设计
数据库设计
create database if not exists oj_databases;
use oj_databases;
drop table if exists oj_table;
create table oj_table(
id int primary key auto_increment,
title varchar(50),
level varchar(20),
description varchar(4096),
templateCode varchar(4096),
testCode varchar(4096)
);
drop table if exists user;
create table user(
userId int primary key auto_increment,
username varchar(128),
password varchar(128)
);
title表示题目标题,level表示难度,description表示题目描述,templateCode表示代码模板,testCode表示测试用例代码.
创建DBUtil类
在common包下创建DBUtil类.
完成数据库连接操作.
public class DBUtil {
private static final String url="jdbc:mysql:///oj_databases?useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC&useSSL=false";
private static final String USERNAME="root";
private static final String PASSWORD="1234";
private static volatile DataSource dataSource=null;
//实现getDataSource,线程安全单例模式
public static DataSource getDataSource() {
if(dataSource==null) {
synchronized (DBUtil.class) {
if(dataSource==null) {
dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setURL(url);
((MysqlDataSource)dataSource).setUser(USERNAME);
((MysqlDataSource)dataSource).setPassword(PASSWORD);
}
}
}
return dataSource;
}
//实现getConnection
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
//实现close
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
if(resultSet!=null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement!=null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
实现题目存储用户存储
创建dao包,以下类都在dao包下创建.
创建Problem类
Problem类表示一道题目。
public class Problem {
private int id;
private String title;
private String level;
private String description;
private String templateCode;
private String testCode;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTemplateCode() {
return templateCode;
}
public void setTemplateCode(String templateCode) {
this.templateCode = templateCode;
}
public String getTestCode() {
return testCode;
}
public void setTestCode(String testCode) {
this.testCode = testCode;
}
@Override
public String toString() {
return "Problem{" +
"id=" + id +
", title='" + title + '\'' +
", level='" + level + '\'' +
", description='" + description + '\'' +
", templateCode='" + templateCode + '\'' +
", testCode='" + testCode + '\'' +
'}';
}
}
创建ProblemDao类
题目管理器类,负责和数据库交互.
public class ProblemDAO {
// 新增题目
public void insert(Problem problem) {
// TODO
}
// 删除题目
public void delete(int id) {
// TODO
}
// 查询所有题目
public List<Problem> selectAll() {
}
// 查询指定题目详情
public Problem selectOne(int problemId) {
}
}
实现insert
public void insert(Problem problem) {
Connection connection=null;
PreparedStatement statement=null;
try {
connection=DBUtil.getConnection();
String sql="insert into oj_table values(null,?,?,?,?,?)";
statement=connection.prepareStatement(sql);
statement.setString(1,problem.getTitle());
statement.setString(2,problem.getLevel());
statement.setString(3,problem.getDescription());
statement.setString(4,problem.getTemplateCode());
statement.setString(5,problem.getTestCode());
int ret=statement.executeUpdate();
if(ret!=1) {
System.out.println("新增题目失败");
} else {
System.out.println("新增题目成功");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,statement,null);
}
}
实现delete
public void delete(int id) {
Connection connection=null;
PreparedStatement statement=null;
try {
connection=DBUtil.getConnection();
String sql="delete from oj_table where id=?";
statement=connection.prepareStatement(sql);
statement.setInt(1,id);
int ret=statement.executeUpdate();
if(ret!=1) {
System.out.println("删除题目失败");
} else {
System.out.println("删除题目成功");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,statement,null);
}
}
实现selectAll
public List<Problem> selectAll() {
List<Problem> list=new ArrayList<>();
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
connection=DBUtil.getConnection();
String sql="select * from oj_table";
statement=connection.prepareStatement(sql);
resultSet=statement.executeQuery();
while (resultSet.next()) {
Problem problem=new Problem();
problem.setId(resultSet.getInt("id"));
problem.setTitle(resultSet.getString("title"));
problem.setLevel(resultSet.getString("level"));
list.add(problem);
}
return list;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
实现selectOne
public Problem selectOne(int id) {
Problem problem=new Problem();
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
connection=DBUtil.getConnection();
String sql="select * from oj_table where id=?";
statement=connection.prepareStatement(sql);
statement.setInt(1,id);
resultSet=statement.executeQuery();
if(resultSet.next()) {
problem.setId(resultSet.getInt("id"));
problem.setTitle(resultSet.getString("title"));
problem.setLevel(resultSet.getString("level"));
problem.setDescription(resultSet.getString("description"));
problem.setTemplateCode(resultSet.getString("templateCode"));
return problem;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
验证insert
private static void testInsert() {
ProblemDao problemDAO=new ProblemDao();
Problem problem=new Problem();
problem.setTitle("两数之和");
problem.setLevel("简单");
problem.setDescription("给定一个整数数组 nums和一个整数目标值 target,请你在该数组中找出 和为目标值 target的那两个整数,并返回它们的数组下标。\n" +
"\n" +
"你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。\n" +
"\n" +
"你可以按任意顺序返回答案。\n" +
"\n" +
"\n" +
"\n" +
"示例 1:\n" +
"\n" +
"输入:nums = [2,7,11,15], target = 9\n" +
"输出:[0,1]\n" +
"解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。\n" +
"示例 2:\n" +
"\n" +
"输入:nums = [3,2,4], target = 6\n" +
"输出:[1,2]\n" +
"示例 3:\n" +
"\n" +
"输入:nums = [3,3], target = 6\n" +
"输出:[0,1]\n" +
"\n" +
"\n" +
"提示:\n" +
"\n" +
"2 <= nums.length <= 104\n" +
"-109 <= nums[i] <= 109\n" +
"-109 <= target <= 109\n" +
"只会存在一个有效答案\n" +
"\n" );
problem.setTemplateCode("class Solution {\n" +
" public int[] twoSum(int[] nums, int target) {\n" +
"\n" +
" }\n" +
"}");
problem.setTestCode("public static void main(String[] args) {\n" +
" Solution solution=new Solution();\n" +
" //testcase1\n" +
" int[] nums={2,7,11,15};\n" +
" int target=9;\n" +
" int[] result=solution.twoSum(nums,target);\n" +
" if(result.length==2 &&result[0]==0&&result[1]==1) {\n" +
" System.out.println(\"testcase1 Ok\");\n" +
" } else {\n" +
" System.out.println(\"testcase1 failed\");\n" +
" }\n" +
" //testcase2\n" +
" int[] nums2={3,2,4};\n" +
" int target2=6;\n" +
" int[] result2=solution.twoSum(nums2,target2);\n" +
" if(result.length==2 &&result2[0]==1&&result2[1]==2) {\n" +
" System.out.println(\"testcase2 Ok\");\n" +
" } else {\n" +
" System.out.println(\"testcase2 failed\");\n" +
" }\n" +
" }");
problemDAO.insert(problem);
}
验证delete
public static void testDelete() {
ProblemDao problemDao=new ProblemDao();
problemDao.delete(5);
}
验证selectAll
public static void testSelectAll() {
ProblemDao problemDao=new ProblemDao();
List<Problem> problems=problemDao.selectAll();
System.out.println(problems);
}
验证selectOne
public static void testSelectOne() {
ProblemDao problemDao=new ProblemDao();
Problem problem=problemDao.selectOne(7);
System.out.println(problem);
}
创建User类
表示一个用户,用户Id,用户名,密码.
public class User {
private int userId;
private String username;
private String password;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
创建UserDao类
用户管理器类,负责和数据库交互.
public class UserDao {
//新增用户
public void insert(User user) {
}
//删除用户
public void delete(int userId) {
}
//根据用户名查询用户
public User select(String username) {
}
//根据用户id查询用户
public User selectId(int userId) {
}
}
实现insert
public void insert(User user) {
Connection connection=null;
PreparedStatement statement=null;
try {
connection= DBUtil.getConnection();
String sql="insert into user values(null,?,?)";
statement=connection.prepareStatement(sql);
statement.setString(1,user.getUsername());
statement.setString(2,user.getPassword());
int ret=statement.executeUpdate();
if(ret!=1) {
System.out.println("新增用户失败");
} else {
System.out.println("新增用户成功");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,statement,null);
}
}
实现delete
public void delete(int userId) {
Connection connection=null;
PreparedStatement statement=null;
try {
connection=DBUtil.getConnection();
String sql="delete from user where userId=?";
statement=connection.prepareStatement(sql);
statement.setInt(1,userId);
int ret=statement.executeUpdate();
if(ret!=1) {
System.out.println("删除用户失败");
} else {
System.out.println("删除用户成功");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,statement,null);
}
}
实现select
public User select(String username) {
User user=new User();
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
connection=DBUtil.getConnection();
String sql="select * from user where username=?";
statement=connection.prepareStatement(sql);
statement.setString(1,username);
resultSet=statement.executeQuery();
if(resultSet.next()) {
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
实现selectId
public User selectId(int userId) {
User user=new User();
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
connection=DBUtil.getConnection();
String sql="select * from user where userId=?";
statement=connection.prepareStatement(sql);
statement.setInt(1,userId);
resultSet=statement.executeQuery();
if(resultSet.next()) {
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,statement,resultSet);
}
return null;
}
验证insert
private static void testInsert() {
UserDao userDao=new UserDao();
User user=new User();
user.setUsername("zhangsan");
user.setPassword("1234");
userDao.insert(user);
}
验证delete
private static void testDelete(int id) {
UserDao userDao=new UserDao();
userDao.delete(id);
}
验证select
private static void testSelect(String name) {
UserDao userDao=new UserDao();
User user=userDao.select(name);
System.out.println(user);
}
验证selectId
private static void testSelectId(int Id) {
UserDao userDao=new UserDao();
User user=userDao.selectId(Id);
System.out.println(user);
}
服务器API
创建API包,以下类都在API包中
实现ProblemServlet类
根据req中是否包含id来确定是获取题目列表页还是获取题目详情页,有id获取题目详情页,无id获取题目列表页.
@WebServlet("/problem")
public class ProblemServlet extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(200);
resp.setContentType("application/json;charset=utf8");
ProblemDao problemDao=new ProblemDao();
String idString=req.getParameter("id");
if(idString==null||idString.equals("")) {
//获取题目列表页
List<Problem> list=problemDao.selectAll();
String respString=objectMapper.writeValueAsString(list);
resp.getWriter().write(respString);
} else {
//获取题目详情页
Problem problem=problemDao.selectOne(Integer.parseInt(idString));
String respString=objectMapper.writeValueAsString(problem);
resp.getWriter().write(respString);
}
}
}
实现CompileServlet
其中包含两个内部类,分别对应编译请求和编译响应.
@WebServlet("/compile")
public class CompileServlet extends HttpServlet {
static class CompileRequest {
public int id;
public String code;
}
static class CompileResponse {
public int error;
public String reason;
public String stdout;
}
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.读取请求并按照json格式解析
//2.根据请求中的id找到对应题目的详情
//3.将用户提交代码和测试用例进行拼接
//4.创建Task类编译运行代码
}
}
实现doPost方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
CompileRequest compileRequest=null;
CompileResponse compileResponse=new CompileResponse();
try {
resp.setStatus(200);
resp.setContentType("application/json;charset=utf8");
//1.读取请求并按照json格式解析
String body=readBody(req);
compileRequest=objectMapper.readValue(body,CompileRequest.class);
//2.根据请求中的id找到对应题目的详情
ProblemDao problemDao=new ProblemDao();
Problem problem=problemDao.selectOne(compileRequest.id);
if(problem==null) {
//找不到对应的题目id
throw new ProblemNotFoundExec();
}
//用户提交的代码
String requestCode=compileRequest.code;
//测试用例
String testCode=problem.getTestCode();
//3.将用户提交代码和测试用例进行拼接
String finalCode=mergeCode(requestCode,testCode);
if(finalCode==null) {
throw new CodeInValidException();
}
//4.创建Task类编译运行代码
Task task=new Task();
Question question=new Question();
question.setCode(finalCode);
Answer answer=task.compileAndRun(question);
compileResponse.error=answer.getError();
compileResponse.reason=answer.getReason();
compileResponse.stdout=answer.getStdout();
} catch (ProblemNotFoundExec problemNotFoundExec) {
compileResponse.error=3;
compileResponse.reason="题目id不存在id="+compileRequest.id;
} catch (CodeInValidException e) {
compileResponse.error=3;
compileResponse.reason="提交代码不合格";
} finally {
String respString=objectMapper.writeValueAsString(compileResponse);
resp.getWriter().write(respString);
}
}
实现readBody方法,把请求中的body全部读出来.
private static String readBody(HttpServletRequest req) throws UnsupportedEncodingException {
int len=req.getContentLength();
byte[] bytes=new byte[len];
try(InputStream inputStream=req.getInputStream()) {
inputStream.read(bytes);
} catch (IOException e) {
e.printStackTrace();
}
return new String(bytes,"utf8");
}
实现mergeCode方法,将用户提交的代码和测试用例进行拼接.
private String mergeCode(String requestCode, String testCode) {
int index=requestCode.lastIndexOf("}");
if(index==-1) {
return null;
}
String subStr=requestCode.substring(0,index);
return subStr+testCode+"\n}";
}
实现LoginServlet
通过from表单进行前后端交互.
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("test/html,charset=utf8");
req.setCharacterEncoding("utf8");
String username=req.getParameter("username");
String password=req.getParameter("password");
if(username==null||password==null||username.equals("")||password.equals("")) {
String html="<h3>登录失败!用户名或密码错误<h3>";
resp.getWriter().write(html);
return;
}
UserDao userDao=new UserDao();
User user=userDao.select(username);
if(user==null) {
String html="<h3>登录失败!用户名或密码错误<h3>";
resp.getWriter().write(html);
return;
}
if(!user.getPassword().equals(password)) {
String html="<h3>登录失败!用户名或密码错误<h3>";
resp.getWriter().write(html);
return;
}
HttpSession session=req.getSession(true);
session.setAttribute("username",username);
resp.sendRedirect("index.html");
}
}