Servlet是在服务器端的应用程序,本身不能单独运行,需要配合web应用来完成它的功能和使用,目前我们主要使用servlet完成前后端交互以及访问数据库,使用servlet与html结合就能够搭建出一个完整的web应用项目。可见它的功能还是很强大的。但是servlet尽管很强大,用它去搭建web项目时会感觉很吃力,比如服务器端的servlet处理完一个请求时,需要对客户端完成响应,有的时候需要在后台代码中完成html代码内容的编写,很不方便。而且一个servlet只能映射到一个类中。当web项目的功能很复杂时,需要配置更多的servlet容器完成相应的功能。
servlet的主要工作流程:我们在客户端发出servlet请求到服务器,服务器识别这个请求发送到servlet进行处理,servlet完成处理后发送响应到服务器,服务器根据http协议再讲响应告诉客户端,完成流程。
一:单独的java项目与结合tomcat服务器完成servlet的使用:
1. 使用servlet的jar包:servlet-api.jar,在资源网站中可以轻易的下载得到。
2. 这里以eclipse为例,创建一个java project项目,导入jar包(右击项目-properties-java build path-libraries-add external jars)。
3. 创建HelloServlet类,继承HttpServlet。该类下提供了doGet(处理get请求)、doPost(处理post请求)、service(自动识别请求进行处理,常使用的方法)等主要使用的方法,在该方法中有两个参数分别是request和response。通过以下代码向客户端完成相应。在客户端中打印Hello Servlet和当前时间。
public void doGet(HttpServletRequest request, HttpServletResponse response){
try {
response.getWriter().println("<h1>Hello Servlet!</h1>");
response.getWriter().println(new Date().toLocaleString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
4. 在项目下配置web.xml文件,新建该文件在web - WEB-INF - web.xml。完成以下配置:
<web-app>
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
servlet-name就是给它起得名字,servlet-class就是对应的类,servlet-mapping是对name=HelloServlet这个servlet的映射,url-pattren代表的是当遇见/hello这个请求的时候,就交给这个servlet去处理。
5. 配置服务器读取的编译文件classes:
在WEB-INF下创建classes文件夹,然后项目右键->properties->Java Build Path->Source->右下角的 Brower-> 指定位置是 j2ee/web/WEB-INF/classes。点击OK,因为tomcat启动之后会自动去这个路径找classes文件。
6. 配置tomcat:
将tomcat的sever.xml文件打开后,在下图所示位置添加:
<Context path="/" docBase="e:\\project\\j2ee\\web" debug="0" reloadable="false" />
告诉tomcat应该如何访问这个项目以及把这个项目的路径告诉tomcat
7. 启动tomcat,访问localhost:8080/hello就可以看到servlet完成的响应内容了
二: 对于servlet的一些方法和使用规则介绍
1. 如何获取请求带来的参数:
我们通常可以在html页面中通过form表单或者ajax向服务器发出请求,点击登录时,会发送一个方法为post的login请求,此时也就是会把地址栏变为:localhost:8080/login?name=&password=进行访问服务器,此时服务器会找web.xml中是否有转向该servlet请求的类。
注:post请求时:地址栏不会显示参数,get请求时:地址栏会显示参数。不写时默认为get请求。
<body>
<form action="login" method="post">
账号: <input type="text" name="name"> <br>
密码: <input type="password" name="password"> <br>
<input type="submit" value="登录">
</form>
</body>
web.xml中配置该servlet,并新建Login类,在该类中:
public class Login extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");
System.out.println("name:" + name);
System.out.println("password:" + password);
}
}
通过request.getParameter(“参数的名字”)获取参数的值。此时我们就能在tomcat的console中看到打印的参数值了。这个请求的流程强调一下,理解很关键:客户端通过form提交post请求---服务器在web.xml中发现了/login对应的servlet---跳转到Login类中找寻doPost方法,在该方法中处理。
2. servlet如何对客户端的请求作出相应:
那我们再增加一个判断,在Login的doPost方法中判断一下客户端传来的账号密码是否正确,然后告诉客户端你输入的对不对。
通过response对象写入html内容,完成响应。代码如下:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");
String html = null;
if ("root".equals(name) && "root".equals(password))
html = "<div style='color:green'>success</div>";
else
html = "<div style='color:red'>fail</div>";
PrintWriter pw = response.getWriter();//通过response的getWriter创建PrintWriter对象
pw.println(html);//根据这个response生成html 字符串,然后再通过HTTP协议,把这个html字符串,回发给浏览器,浏览器再根据HTTP协议获取这个html字符串,并渲染在界面上。
}
此时客户端中回显示:
3. service()方法:service方法会自动识别post或者get请求,一般都用这个的多。
4. 中文乱码问题:html页面中:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
java代码中:如果是参数是中文的,对参数进行编解码设置:request.setCharacterEncoding("UTF-8");
如果要返回中文内容:设置html内容格式:response.setContentType("text/html;charset=UTF-8");
就完成了中文乱码问题。
5. servlet的生命周期:
在此周期中:构造方法(实例化)和init()(初始化)方法都只会执行一次 ,然后在调用service方法,完成之后调用destory()摧毁,变成可回收对象等待GC回收。
6. 跳转: 准备两个html页面,success和fail,内容只显示成功和失败两个字就好。我们用来验证登录的账号密码是否正确然后完成跳转。servlet的跳转分为服务器跳转和客户端跳转。
服务器跳转:在service方法中判断完了之后,直接在服务器中调到指定的文件中,然后响应在客户端中。
客户端跳转:service方法中判断完了,告诉客户端下一步去访问谁,客户端收到该响应后,再次去访问服务器,服务器再把它访问的页面返回给它。
代码如下:
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");
if ("admin".equals(name) && "admin".equals(password)) {
request.getRequestDispatcher("success.html").forward(request, response);//这是服务器端跳转
}
else{
response.sendRedirect("fail.html");//客户端跳转,重定向
}
}
7. servlet的自启动:随着tomcat的启动,自动初始化。即访问init()方法。完成一些初始化的工作。
告诉服务器谁需要自启动:在web.xml文件中添加<load-on-startup>10</load-on-startup>,10代表启动的优先级,越小越高。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
在该类中重写public void init(ServletConfig config) {业务代码}
8. 其他:
response的301或者302客户端跳转、response设置不使用缓存、如何上传文件等方法,敬请了解。
二:动态WEB项目:
1. 创建,导包:创建Dynamic Web Project,导入jar包到WebContent/WEB-INF/lib 路径下,WEB-INF创建web.xml文件,配置tomcat并部署在tomcat中。启动tomcat。
注:类文件会被输出到build里,而不是WEB-INF/classes目录下;
WebContent会被整个复制到 E:\project\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\j2ee 这个位置下面去,Eclipse中启动的tomcat其实是访问的这个位置。所以当WebContent里的内容比较多的时候,就会花较长时间复制;
tomcat启动失败:可能是端口被占用或者该tomcat已经启动了;
选好tomcat路径后无法点击finish,解决办法:
如何将普通的java project项目变为动态web项目:
右键项目j2ee->properties->Project Facets->Convert to faceted form...
勾选Dynamic Web Module
勾选之后,会出现 Furthe configuration available ..., 点击
动态web项目的默认内容目录是WebContent,而 j2ee这个项目的对应目录是 web, 所以这里要输入web
OK
三:使用servlet完成与数据库的交互。也就是对数据库进行增删改查。
我们创建一个小demo,以增加英雄为例,首先我们在本地数据库(用的mysql)中创建test数据库,并新建表hero,分别有以下字段,id为自增长。
我们通过html页面完成对该数据库表的操作。
1. 新建addHero.html页面。简单设置为姓名,血量,伤害input框,并设置新增按钮提交form表单,form表单action指向AddHero
2. 设置web.xml添加AddHero的servlet的配置,映射向AddHero.java,然后我们需要新建Hero.java实体类,定义id/name/hp/damage/并且有get/set方法。然后新建HeroDao类,提供JDBC数据库连接、增删改查SQL语句的执行。具体HeroDao.java如下:
package dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import bean.Hero;
public class HeroDao {
//无参构造方法,在实例化的时候就已经加载了JDBC类。
public HeroDao() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
}
}
//设置公共方法连接数据库
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
}
//获取总条数
public int getTotal() {
int total = 0;
try(Connection c = getConnection();Statement s = c.createStatement()) {
String sql = "select count(*) from hero";
ResultSet rs = s.executeQuery(sql);
while(rs.next()) {
total = rs.getInt(1);
}
System.out.println("total:" + total);
} catch (Exception e) {
// TODO: handle exception
}
return total;
}
//新增
public void add(Hero hero) {
String sql = "insert into hero values(null,?,?,?)";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.execute();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
int id = rs.getInt(1);
hero.id = id;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//修改
public void update(Hero hero) {
String sql = "update hero set name= ?, hp = ? , damage = ? where id = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.setInt(4, hero.id);
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
//删除
public void delete(int id) {
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "delete from hero where id = " + id;
s.execute(sql);
} catch (SQLException e) {
e.printStackTrace();
}
}
//根据ID获取某条数据
public Hero get(int id) {
Hero hero = null;
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "select * from hero where id = " + id;
ResultSet rs = s.executeQuery(sql);
if (rs.next()) {
hero = new Hero();
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.name = name;
hero.hp = hp;
hero.damage = damage;
hero.id = id;
}
} catch (SQLException e) {
e.printStackTrace();
}
return hero;
}
//配合下面有参的方法完成对数据库列表的查询
public List<Hero> list() {
return list(0, Short.MAX_VALUE);
}
public List<Hero> list(int start, int count) {
List<Hero> heros = new ArrayList<Hero>();
String sql = "select * from hero order by id asc limit ?,? ";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setInt(1, start);
ps.setInt(2, count);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Hero hero = new Hero();
int id = rs.getInt(1);
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.id = id;
hero.name = name;
hero.hp = hp;
hero.damage = damage;
heros.add(hero);
}
} catch (SQLException e) {
e.printStackTrace();
}
return heros;
}
}
3. 在AddHero.java中,集成httpservlet类,方法service,获取前台input传来的参数,放在herod对象中,将该对象作为参数放入HeroDao的add方法中,执行该方法。完成数据库的添加。最终客户端跳转到heroList页面中。显示已经添加的信息。代码如下:
public class AddHero extends HttpServlet {
protected void service(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
Hero hero = new Hero();
hero.setName(request.getParameter("name"));
hero.setHp(Integer.parseInt(request.getParameter("hp")));
hero.setDamage(Integer.parseInt(request.getParameter("damage")));
HeroDao hd = new HeroDao();
hd.add(hero);
response.sendRedirect("/j2ee_1/heroList");
}
}
4. heroList.java中,用table将数据库中的数据展示出来,用servlet写html页面内容,传给客户端进行显示,代码如下:
public class HeroList extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
response.setContentType("text/html;charset=UTF-8");
List<Hero> heros = new HeroDao().list();//获取数据库中所有数据列表
StringBuffer sb = new StringBuffer();//完成html页面内容设置
sb.append("<table align='center' border='1' cellspacing='0'>\r\n");
sb.append("<tr><td>id</td><td>name</td><td>hp</td><td>damage</td><td>删除</td><td>修改</td></tr>\r\n");
//占位符的使用%d%s%f...
String trFormat = "<tr><td>%d</td><td>%s</td><td>%f</td><td>%d</td><td><a href='delHero?id=%d'>删除</a></td><td><a href='editHero?id=%d'>修改</a></td></tr>\r\n";
//将数据库中的数据遍历显示在table中。
for (Hero hero : heros) {
String tr = String.format(trFormat, hero.getId(),hero.getName(),hero.getHp(),hero.getDamage(),hero.getId(),hero.getId());
sb.append(tr);
}
sb.append("</table>");
//servlet告诉客户端显示这些东西。
response.getWriter().write(sb.toString());;
}
}
注:href="delHero?id=%d"herf也可以请求servlet。
4. 删除、修改内容同理,这就是使用servlet完成对数据库的操作。
5. servlet还可以将html页面中的json数据传递到后台,以及将后台中的json数据发送到页面中显示,以对象的形式传递。
6. 接下来学习使用jsp。
本人声明:以上学习来自how2j.cn的学习笔记。如有侵权,请联系本人删除。