#### 1.Servlet开发入门
- ##### 1.1 Servlet由来
- 第一天我们就说过,需要服务器,使用Java代码,访问数据库。因此,我们需要书写java程序,去访问数据库,那么java程序在哪里运行?
- 用户的请求从浏览器发出,由服务器接收,因此,用户需要什么服务器才知道由此可见,这些java程序必须安装到服务器中,让服务器调用执行。
- 那么我们可以随意写java程序码?不能,因为就像螺丝和螺帽一样,必须按照一定大小尺寸规定,生成,后期才可以匹配使用。
- 因此sun公司为了让程序员书写的java程序可以严丝合缝的安装到服务器中,定义了一个接口Servlet
- ##### 1.2 Servlet介绍
- servlet是运行在Web服务器中的小型java程序,servlet通常通过HTTP(超文本传输协议)接收和响应来自Web客户端的请求。
- 要实现此接口,可以编写一个扩展javax.servlet.http.HttpServlet的HTTP servlet
- servlet生命周期方法的调用顺序
- 1.构造servlet,然后使用init方法将其初始化
- 2.处理来自客户端的对servlce方法的所有调用
- 3.从服务中取出servlet,然后使用destroy方法销毁它,最后进行垃圾回收并中止它。
- 什么是servlet?
- 处理请求和响应请求的java程序
- 怎么创建servlet?
- 继承HttpServlet类,在web.xml中配置,servlet 3.0版本之后不需要在web.xml中配置
#### 2.Servlet开发的步骤
- ##### 2.1 在cn.igeek.web包下 创建一个类 实现 HttpServlet 类
```
package cn.yiran.web.servlet;
import javax.servlet.http.HttpServlet;
public class ServletDemo extends HttpServlet {
}
```
- ##### 2.2 重写doGet和doPost,init()方法
```
package cn.yiran.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException{
System.out.println("init()....");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet....");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost....");
}
}
```
- ##### 2.3 在当前项目中的web.xml文件中配置当前开发好的Servlet程序
```
<!-- servlet路径 -->
<servlet>
<!-- servlet的名称 -->
<servlet-name>ServletDemo</servlet-name>
<!-- servlet的具体实现类 -->
<servlet-class>cn.yiran.web.servlet.ServletDemo</servlet-class>
</servlet>
<!-- servlet映射 -->
<servlet-mapping>
<!-- servlet的名称 -->
<servlet-name>ServletDemo</servlet-name>
<!-- 访问这个servlet程序的请求路径 -->
<!-- 可以定义多个Servlet,这里配置请求路径为,所有的servlet -->
<url-pattern>/ServletDemo</url-pattern>
</servlet-mapping>
```
- ##### 2.4 发布这个项目,运行服务器,测试
- 直接在网页链接后面输入servlet类名即可,如:http://localhost:8080/web/ServletDemo
- 在Tomcat server信息中查看调用方法为:init()... Get()...
#### 3.Servlet中的细节
- ##### 3.1 http://localhost:8080/web/ServletDemo
- http:// --> 协议
- localhost --> 电脑
- 8080 --> 服务器
- web --> web目录
- ServletDemo --> web.xml中的/ServletDemo --> 所有的ServletDemo类 --> 某个ServletDemo类 --> init()方法+doGet()方法
- 总结:通过请求路径,查询web.xml中servlet配置,获取当前servlet对象,调用java程序。
#### 4.使用IDEA模版开发Servlet(重点):可以更快捷的创建servlet对象
- ##### 4.1 如何直接创建servlet对象
- 选中包名 --> New --> servlet
- Create Java EE 6 annotated class ,创建Java EE 6 注解类,servlet3.0之后,可以利用注解来配置servlet,而不需要在web.xml中配置
- 会自动创建doGet和doPost方法
#### 5.servlet3.0注解
- ##### 5.1 @WebListener注解
- 表示在web.xml中配置的
```
<listener>
<listener-class>ListenerClass</listener-class>
</listener>
```
- ##### 5.2 @WebFilter
- 这个注解就是表示的拦截器
- 注解的主要参数及其含义
![image](https://images2015.cnblogs.com/blog/593110/201611/593110-20161123225559315-2079803493.png)
- ##### 5.3 @WebServlet
- 这个注解表示的就是一般的Servlet
- 注解的主要参数及其含义,参数和Filter差不多
![image](https://images2015.cnblogs.com/blog/593110/201611/593110-20161123230047534-1707818143.png)
- ##### 5.4 @WebInitParam
- 表示的就是参数,相当于<init-param>
- 注解的主要参数及其含义
![image](https://images2015.cnblogs.com/blog/593110/201611/593110-20161123230313425-824229219.png)
- ##### 5.5 @MultipartConfig
- 该注解主要是为了辅助 Servlet 3.0 中 HttpServletRequest 提供的对上传文件的支持。
- 该注解标注在 Servlet 上面,以表示该 Servlet 希望处理的请求的 MIME 类型是 multipart/form-data。
- 另外,它还提供了若干属性用于简化对上传文件的处理。具体如下
![image](https://images2015.cnblogs.com/blog/593110/201611/593110-20161123231817503-47345623.png)
#### 6.获取请求
- ##### 6.1 准备html
- 在web目录下新建一个regist.jsp
```
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<form action="MyServlet" method="post">
用户名:<input type="text" name="username" /><br />
爱 好:<input type="checkbox" name="hobby" value="football" />足球
<input type="checkbox" name="hobby" value="basketball" />篮球<br />
<input type="submit" value="提交" />
</form>
</body>
</html>
```
- ##### 6.2 完善MyServlet方法
- 给@WebServlet加上value属性 --> value = "/MyServlet"
- 完善doPost方法
- 服务器给我们提供了一个Request对象,为HTTP servlet 提供请求信息
- getParameter(String name)
- 返回类型 --> String
- 根据name 获取对应的值
- getParameterMap()
- 返回类型 --> Map
- 将请求信息中,参数名作为key,参数值作为value,封装到map中
- getParameterValues(String name)
- 返回类型 --> String[]
- 获取name相同的所有value 例如复选框
```
package cn.yiran.web.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "MyServlet",value = "/MyServlet")
public class MyServlet extends HttpServlet {
@Override
public void init() throws ServletException {
super.init();
System.out.println("3.0 init()");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("3.0 doPost");
// getParameter(name) --> 根据name属性获取值
String username = request.getParameter("username");
System.out.println("username:"+username);
// getParameterValues(name) --> 根据name属性获取多个值,得到的是一个String数组
String[] hobby = request.getParameterValues("hobby");
for (String str:hobby){
System.out.println("hobby:"+str);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
```
- ##### 6.3 测试控制台是否能输出
- 注意:在输入框中输入中文,读取出来输出为? 这是因为没有设置字符的编码格式
- request.setCharacterEncoding("utf-8") --> 设置接收数据的字符编码格式为utf-8
- response.setCharacterEncoding("utf-8"),同理。一般设置字符集,位于方法的最前。
- response.setContentType("text/html; charset=utf-8"); --> 设置返回内容类型
#### 7.发送响应
- ##### 7.1 发送响应的对象Response对象
- 提供特定于HTTP的发送响应功能。
- servlet容器创建HttpServletResPonse对象,并将该对象作为参数传递给servlet的service方法(doGet,doPost,等等,这些都属于service方法)
- ##### 7.2 Response方法
- getWriter()
- 返回 --> java.io.PrintWriter
- 返回可将字符文本发送到客户端的 PrintWriter 对象
- PrintWriter对象
- 继承于 --> java.io.Writer
- 方法 --> writer(String s) --> 写入字符串
- ##### 7.3 发送响应
- 直接在service(服务)方法中调用Response对象使用方法即可
```
System.out.println("========发出响应===========");
response.getWriter().write("test OK!!!!");
```
#### 8.Servlet开发应用——登录案例
- ##### 8.1 准备数据表
- 需要3个字段,id、username、password
```
create database JavaEE charset=utf8;
use javaee
create table user(
id int auto_increment primary key not null,
username varchar(20) not null,
password varchar(20) not null
);
insert into user(username,password) value('yiran','yiran'),('chunjue','chunjue'),('miku','miku');
```
- ##### 8.2 导入jar包
- c3p0-0.9.1.2.jar
- commons-dbutils-1.6.jar
- mysql-connector-java-5.0.8-bin.jar
- ##### 8.3 新建包结构
- src
- cn.yiran.web
- dao --> 数据访问对象,实现对数据库表user的增删改查
- UserDAO
- domain --> 实体层,javabean,mvc中的modle
- User
- service --> 业务逻辑层
- UserService
- servlet --> 控制器,接收和返回结果
- LoginServlet
- utils --> 工具层
- JDBCUtils
- c3p0-config-xml --> c3p0连接池的配置文件
- ##### 8.4 dao层 -- UserDAO
```
package cn.yiran.web.dao;
import cn.yiran.web.domain.User;
import cn.yiran.web.utils.JDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.SQLException;
/**
* 数据访问对象,实现对数据库表user的增删改查
* */
public class UserDAO {
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
// 定义一个实现登录的方法
public User login(User user){
String sql = "select * from user where username=? and password=?";
try{
return qr.query(sql,new BeanHandler<User>(User.class),user.getUsername(),user.getPassword());
} catch (SQLException e){
e.printStackTrace();
throw new RuntimeException("登录失败");
}
}
}
```
- ##### 8.5 damain层 -- User
```
package cn.yiran.web.domain;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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 [id=" + id + ", username=" + username + ", password=" + password + "]";
}
}
```
- ##### 8.6 service层 -- UserService
```
package cn.yiran.web.service;
import cn.yiran.web.dao.UserDAO;
import cn.yiran.web.domain.User;
/**
* 封装对用户进行操作的业务逻辑
* */
public class UserService {
private UserDAO userDAO = new UserDAO();
// 实现用户登录的业务逻辑
public User login(User user){
return userDAO.login(user);
}
}
```
- ##### 8.7 servlet层 -- LoginServlet
```
package cn.yiran.web.servlet;
import cn.yiran.web.domain.User;
import cn.yiran.web.service.UserService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LoginServlet",value = "/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");
// 1.获取请求页面中的数据
String username = request.getParameter("username");
String password = request.getParameter("password");
// 2.将获取的数据进行封装
User user = new User();
user.setUsername(username);
user.setPassword(password);
// 3.进行用户登录业务逻辑的判断
UserService userService = new UserService();
User loginUser = userService.login(user);
// 4.判断返回值,做出响应
if (loginUser == null){
response.getWriter().write("账号或密码错误");
}
else{
response.getWriter().write("登录成功");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
```
- ##### 8.8 utils -- JDBCUtils
```
package cn.yiran.web.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JDBCUtils {
// 创建c3p0的连接池
static ComboPooledDataSource cpds = new ComboPooledDataSource();
// 获得连接
public static Connection getConnection() throws Exception {
// 获得连接
return cpds.getConnection();
}
public static DataSource getDataSource() {
return cpds;
}
// 释放资源
public static void release(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
release(conn, stmt);
}
// 释放资源
public static void release(Connection conn, Statement stmt) {
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
```
- ##### 8.9 src层 -- c3p0-config.xml
```
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://192.168.13.22:3306/javaee</property>
<property name="user">root</property>
<property name="password">root</property>
</default-config>
</c3p0-config>
```
- ##### 8.10 报错500
- 错误信息:java.lang.NoClassDefFoundError:org/apache/commons/dbutils/ResultSetHandler
- 由于Tomcat没有找到jar包,自己放在lib中的jar包没有添加到项目资源中,项目依赖出了问题。需要在Artifacts点击fix,将lib包重新加载一次
- 解决教程:https://blog.csdn.net/silentkare/article/details/78160735o)
#### 9.servlet生命周期
- ##### 9.1 生命周期图
-
![image](https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531193504331&di=477bc7eabe67bde16fec4af18233fe78&imgtype=jpg&src=http%3A%2F%2Fimg4.imgtn.bdimg.com%2Fit%2Fu%3D124009709%2C4150369873%26fm%3D214%26gp%3D0.jpg)
- 上图描述了servlet的生命周期。按照功能的不同,大致分为三个阶段,分别是 初始化阶段,运行阶段(最频繁) 和 销毁阶段。
注意:当服务器关闭的时候,项目中的servlet就调用第8步销毁。
- 总结:Servlet 的创建,在用户第一次请求,之后,创建。销毁,是发生在服务器关闭的时候。
- ##### 9.2 初始化阶段
- 当客户端向tomcat发送http请求访问servelt程序,tomcat首先会解析请求,检查内存中是否已经有了该servlet对象:
- 如果有直接使用对应的servlet对象
- 如果没有就创建servlet实例对象,然后通过调用init()方法实现Servlet的初始化工作。
- 需要注意的是,在整个servlet的生命周期内,init方法只被调用了一次。除非配置发生改变。
- ##### 9.3 运行阶段
- 在这个阶段,tomcat服务器会为这个请求 创建 代表HTTP请求的ServletRequest对象 和 代表HTTP响应的 ServletResponse对象,然后将他们作为参数传递给service() 方法。
- servcie() 方法从ServletRequest对象获取请求的信息并做出处理;通过ServletResponse 对象生成响应的结果。
- 在servlet的整个生命周期内,对于servlet的每一次访问请求,tomcat都会调用servlet的service方法,并且创建新的ServletRequest对象和ServletResponse对象。也就是说service() 方法在servlet的生命周期内会被调用多次。
- ##### 9.4 销毁阶段
- 当服务器关闭时,servlet会随着Web应用的销毁而销毁
- 在销毁serlvet之前,tomcat会调用Servlet的destory方法,以便让Servlet对象释放他所占用的资源。
#### 10.路径书写问题
- 在学习Servlet的时候,每个Servlet程序都需要在当前的这个项目的web.xml文件中注册和映射。
- 在映射中书写的url-pattern标签的书写方式:
- ##### 10.1 全路径匹配
- 在书写url-pattern的时候,必须以/开始,后面书写具体浏览器访问时的路径。
- 配置:<url-pattern>/DemoServlet</url-pattern>
- 外界的访问方式:
- http://localhost:8080/web/DemoServlet
- ##### 10.2 路径通配符匹配
- 在书写url-pattern 的时候,以/开始,后面可以使用*号表示任意的匹配
- 配置:<url-pattern>/DemoServlet/*</url-pattern>
- 外界在访问的时候,只要能够和/demo2匹配上,后面写任何东西都可以:
- http://localhost:9090/test/DemoServlet/11111/aaa
- ##### 10.3 扩展名匹配
- 在书写url-pattern 的时候,不能以/开始,以*开始,后面书写扩展名
- 配置: <url-pattern>*.do</url-pattern>
- 常见的扩展名书写:
- *.action *.do *.go
- 访问的方式:
- http://localhost:9090/test/xxxx.do
- ##### 10.4 优先级
- url-pattern标签中的路径可以按照上述的三种书写,它们的优先级:
- 全路径匹配 > 路径通配符匹配 > 扩展名匹配