Servlet
1.1、 Servlet概念
Servlet是服务器端的一个java小程序。它用于:处理,响应请求。
1.2、 Servlet的创建方式
在创建Servlet时一定要在web.xml中添加<servlet></servlet>和<servlet-mapping><servlet-mapping>
标签
为什么要在web.xml中设置url-pattern访问实现Servlet接口的类?
在项目src编写的实现类,tomcat将其编译成字节码文件放在WEB-INF中,外界不能直接访问,因此通过映射路径访问。
方式一:实现Servlet接口
方式二:继承GenericServlet
方式三:继承HttpServlet【开发中使用】
1.3、 Servlet的生命周期
- 实例化
-
初始化init
第一次请求时初始化一次。
-
服务service
每次请求都要调用一次service()方法
-
销毁destroy
服务器停止或者服务器移除该项目时销毁。
1.4、 Web.xml配置的背后细节
输入地址:http://localhost:8080/项目名称/映射路径
<servlet>与<servlet-mapping>中的两个<servlet-name>中的值要一样才能寻找到
<url-pattern>
中的映射路径要以 / 打头,不要落了。-
让Servlet实现类一开始就进行实例化和初始化
如果想让Servlet的实现类在一开始就进行实例化和初始化,只需要在```<servlet>标签中``` 添加```<load-on-startup>2</load-on-startup>```
背后细节:
<!--
这是创建的一个Servlet,Servlet的类的路径为当前项目下的Servlet包下的Servlet类,为这个Servlet起了个别名ServletDemo
网页访问路径:http://localhost:8080/项目名称/类的映射路径
-->
<servlet>
<servlet-name>ServletDemo</servlet-name>
<servlet-class>ServletDemo.ServletDemo</servlet-class>
</servlet>
<!--
两个Servlet-name必须一致,url-pattern中为servlet-class的映射路径,通过它可以访问到这个Servlet
-->
<servlet-mapping>
<servlet-name>ServletDemo</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
第一步:服务器通过映射路径url-pattern的demo对应的servlet-mapping的servlet-name
第二步:根据得到的servlet-name在web.xml中寻找和它名字相同的servlet标签下的servlet-name
第三步:根据对应的servlet-class中真实路径寻找到Servlet实现类的全路径从而访问
1.4.1、 Web.xml中映射路径的配置
-
全路径匹配方式 : 以 / 开始
例如:http://localhost:8080/项目名称/demo /demo : 既是具体的全路径配置
-
目录匹配方式 : 以 / 开始 ,以 * 结束
流:http://localhost:8800/项目名称/aa/rqqhqih/whoqho http://localhost:8800/项目名称/aa/ss/wr/q/fg /aa/* : 即只要在aa目录下的任意路径都可以
-
扩展名匹配 : 以 * 开始 , 开头没有斜杠/
例如:http://localhost:8080/项目名称/aa/bb/cc/*.do *.do 或者 *.(任意) : 只要以对应扩展名结尾的都可以
1.5、 继承HttpServlet类创建一个Servlet
public class ServletDemo2 extends HttpServlet {
@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*******");
}
}
写完java的Servlet实现类后,完成web.xml配置。
<servlet>
<servlet-name>ServletDemo</servlet-name>
<servlet-class>ServletDemo2.ServletDemo2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
结果:每访问一次输出一次doGet。(doGet和doPost服务器根据提交方式选择执行)
1.5.1、 HttpServlet子类调用doGet或doPost方法的背后细节
1:浏览器输入: http://localhost:8080/项目名/demo
2: 在web.xml里面找到了ServletDemo这个类
3: 获得请求,调用ServletDemo这个类中的service方法。
4: 子类使用继承的父类HttpServlet中的Service(ServletRequest req,ServletResponse res)方法,
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
5: 调用service(HttpServletRequest request, HttpServletResponse response)方法,根据提交方式调用对应的doXxx()方法
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals("GET")) {
long lastModified = getLastModified(req);
if (lastModified == -1L) {
doGet(req, resp);
}else if (method.equals("POST")){
doPost(req, resp);
}
......
6: HttpServlet父类中有doGet()和doPost()方法,但是我们自己重写了doGet和doPost方法,
因此执行自己的doGet(),打印***doGet*****
2、 登陆案例
2.1 需求:写一个表单html登陆网页,提交账号和密码到服务器,服务器进行到数据库中进行校对,如果存在这样的用户数据,就登陆成功,反之就登录失败!
2.2 准备工作:
- mysql驱动,C3P0的jar包,DBUtils的jar包,C3P0配置文件
- 编写bean:User类,C3P0Utils工具类,数据库提供对应的User账号密码数据
- 编写用户登录表单页面及处理请求Servlet登陆类
2.3 实现步骤:
1:导入相应的jar包,并添加至构建路径
2: 编写bean:User类,C3P0Utils工具类,并创建对应的数据库user
数据库user
配置c3p0-config.xml配置文件
编写C3P0Utils工具类
- 提供获取连接池,获取连接及释放资源的方法
public class C3P0Utils {
// 提供获取连接池,获取连接和释放资源的方法
// 根据c3p0-config.xml配置文件创建连接池
private static DataSource ds = new ComboPooledDataSource();
// 获取连接
public static Connection getConnection() {
Connection connection = null;
try {
connection = ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
// 释放资源
public static void release(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//获取连接池的方法
public static DataSource getDataSource() {
return ds;
}
}
编写bean:User类
-
提供与数据库列名相同的变量,提供get/set方法
public class User { private String username; private String userpsd; public User() { super(); // TODO Auto-generated constructor stub } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserpsd() { return userpsd; } public void setUserpsd(String userpsd) { this.userpsd = userpsd; } @Override public String toString() { return "User [username=" + username + ", userpsd=" + userpsd + "]"; } }
编写用户登录页面及处理请求Servlet登陆类
- 用户提交的数据发送至服务器的Servlet处理
- 表单标签中action属性填写提交数据的地址,我们填写Servlet实现类的映射路径
用户表单页面Login.html
处理请求Servlet登陆类
获取提交数据的账号和密码
-
获取数据库中对应的信息,查询是否有符合条件的
public class Login extends HttpServlet { // doGet()方法 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub super.doGet(req, resp); } // doPost() @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 首先配置对应的xml文件,让提交的请求发送到该Servlet处理 // 获取数据名为username的值 String username = req.getParameter("username"); // 获取数据名为userpsd的值 String userpsd = req.getParameter("userpsd"); // 使用DBUtils框架查询数据库中所有账号和密码与其输入的账号密码匹配的记录 // 1:创建QueryRunner对象 QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource()); // 2:编写sql语句 String sql = "select * from User where username=? and userpsd=?"; // 3:执行sql语句 Object[] params = { username, userpsd }; // 占位符问号处的值 try { List<User> users = runner.query(sql, new BeanListHandler<>(User.class), params); if (users.size() > 0) { resp.getWriter().println("Login Succed!"); } else { resp.getWriter().println("Login Failed!"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
配置HttpServlet子类Login类的web.xml文件
<servlet>
<servlet-name>Login</servlet-name>
<servlet-class>Login.Login</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
将这里url-pattern的映射路径写到Login.html的表单标签的action属性中,提交的数据就能来到Login类处理
到这里,登陆案例就完成了,查看运行结果。
浏览器中输入地址: http://localhost:8080/day14_ServletLoginDemo/login.html 。
在登陆的表单页面中填入账号密码
username : admin , userpsd : admin
数据库中没有这个用户的账号密码,登陆失败
username : lzl , userpsd : lzl
数据库中有该用户的对应的信息,登陆成功
3、ServletContext
3.1、 ServletContext概念
它是Servlet的上下文 , 每次项目加载到服务器时该项目都会实例化一个ServletContext对象,一个项目有且只有一个ServletContext对象。
3.2、 生命周期
何时创建:
1.服务器加载时,被服务器托管的项目都会实例化一个ServletContext对象
2.项目发布时,实例化一个SoervletConetxt对象
3.3、 ServletContext的作用:
存/取数据
getAttribute(String name) : 获取属性
setAttribute(String name , Object obj) : 设置属性
//1:存数据到关联的web.xml中
context.setAttribute("name", "aobama");
//2:获取存入到关联的web.xml中的属性
String str = (String) context.getAttribute("name");
获取项目资源
方式一:
获取到ServletContext对象,调用getResourceAsStream(String path)方法得到资源的流对象。
将获取到的流对象用Properties加载出来。
要注意的是getResourceAsStrem方法中的路径默认是在Tomcat/Webapps/项目名工程下的路径查找,而我们
的资源被Tomcat默认放在项目工程下的的WEB-INF/classes中保存的,因此填入的路径就填相对路径就可以了
//需求:获取该项目src下的my.properties文件
//获取能够加载流文件的对象Properties
Properties properties = new Properties();
//获取当前项目的ServletContext对象
ServletContext context = this.getServletContext();
//获取my.properties文件的流对象
InputStream inputStream = context.getResourceAsStream("WEB-INF/classes/my.properties");
//将当前流对象用properties加载读取
properties.load(inputStream);
//获取my.properties文件中的值
String username = properties.getProperty("username");
String password = properties.getProperty("password");
//输出值
System.out.println("username: "+username);
System.out.println("password: "+password);
方式二:getRealPath(String path)
自己创建输入流读取my.properties文件
// 获取能够加载流对象的Properties对象
Properties properties = new Properties();
// 因为FileInputStream中要填入全路径 , 因此调用getRealPath()方法获取文件全路径
String path = this.getServletContext().getRealPath("WEB-INF/classes/my.properties");
// 获取文件流对象
InputStream is = new FileInputStream(path);
// 加载文件并获取文件中的内容
properties.load(is);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println("username: " + username);
System.out.println("password: " + password);
方式三:类加载器读取my.properties文件
//使用类加载器获取my.properties文件资源,这里的字节码对象可以是任意的class对象
InputStream is = this.getClass().getClassLoader().getResourceAsStream("my.properties");
//获取Properties对象,将该文件流对象加载
Properties properties = new Properties();
properties.load(is);
//获取该资源中的内容
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println("username: "+username);
System.out.println("password: "+password);
浏览器输入:http://localhost:8080/项目名/映射路径
结果为: username: jack
password: 123
类加载器获取流对象的默认路径直接是在WEB-INF/classes下的,因此直接在后面拼接my.properties就可以正确加载。
获取全局参数
在web.xml中在<context-param></context-param>中设置全局参数,与局部参数一样,
有<param-name></param-name>和<param-value></param-value>两个值,设置它们的值,
在与其关联的Servlet中可以通过SevletContext对象的getInitParam(String)方法获得全局参数。
在web.xml中设置全局参数:
可以把<param-name>和<param-value>看做键值对
<context-param>
<param-name>姓名</param-name>
<param-value>凌霄</param-value>
</context-param>
ServletContextDemo中获取全局参数代码:
//获得当前项目的ServletContext对象
ServletContext context = this.getServletContext();
//获取全局参数
System.out.println(context.getInitParameter("姓名"));
浏览器输入 : http://localhost:8080/ServletContextDemo3/demo
结果 : 凌霄
3.4、ServletContext案例一
需求:
在上面的登陆案例基础上,额外添加校对正确后,跳转到登录成功页面,显示内容:登陆成功,等待5秒后跳转。
跳转到新的页面,显示:欢迎某某,您是第n位登陆的用户!
思路:
在校对账密的Servlet中,如果登陆成功就跳转到登陆成功页面
编写登陆成功页面,实现5秒后跳转新页面的功能。(实现倒计时)
-
编写跳转后的页面。
由于有计数,因此肯定要在某个地方定义一个计数器,将其定义在web.xml中, 通过ServletContext对象可以方便的存入与取出该数据进行操作。
实现:
核对密码的Servlet代码:
// 首先配置对应的xml文件,让提交的请求发送到该Servlet处理
// 获取数据名为username的值
String username = req.getParameter("username");
// 获取数据名为userpsd的值
String userpsd = req.getParameter("userpsd");
// 获取校对后的用户
List<User> users = Service.check(username, userpsd, resp);
if (users.size() > 0) {
// 计数
// 获取ServletContext对象,并取得属性为count的值
ServletContext context = getServletContext();
Object obj = context.getAttribute("count");// 获得了属性为count的值
if (obj == null) { // 证明第一个登陆,设置计数器为1
context.setAttribute("count", 1);
} else { // 证明不是第一个登陆,取出count的值并+1存入
context.setAttribute("count", (Integer) obj + 1);
}
resp.sendRedirect("Login_Succed.html");// html页面就在项目根目录下,跳转到该页面
} else {
resp.getWriter().write("Login Failed");
}
}
登陆成功页面Login_Succed.html
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
var count = 5;
//完成倒计时功能
function change() {
setInterval(function name() {
count--;
//设置<span>标签内的值
document.getElementById("span1").innerText = count;
//5秒后跳转页面
if (count == -1) {
location.href = "login2";
}
}, 1000);
};
</script>
</head>
<body onload="change()">
<h3>
登陆成功,<span id="span1">5</span>秒钟后跳转!
</h3>
</body>
</html>
映射路径为login2的LoginSucced的Servlet类
public class LoginSucced extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 防止中文乱码,让客户端和服务端使用的码表一致
response.setContentType("text/html;charset=UTF-8");
// 获取ServletContext对象
ServletContext context = getServletContext();
// 获得当前属性值count的值
int count = (Integer) context.getAttribute("count");
response.getWriter().write("<h3>欢迎光临,您是第" + count + "位登陆的用户!</h3>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
****欢迎交流~~****