Java学习 Servlet
常见服务器
开源:
- Tomcat
- jetty
- resin
收费:
- WebLogic(oracle)
- WebSphere(IBM)
Servlet
Servlet∶
- ServerApplet的简称,是服务器端的程序(代码、功能实现),可交互式的处理客户端发送到服务端的请求,并完成操作响应。
- 动态网页技术
- JavaWeb程序开发的基础,JavaEE规范(一套接口)的一个组成部分。
作用
- 接收客户端请求,完成操作。
- 动态生成网页(页面数据可变)
- 将包含操作结果的动态网页响应给客户端。
编写
实现Servlet接口,重写 init(ServletConfig servletConfig) throw ServletException,service(ServletRequest request,ServletResponse response) throws ServletException,IOException,destroy,getServletConfig,getServletInfo方法
配置
配置web.xml
<servlet>
<servlet-name>别名</selvet-name>
<servlet-class>类名</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>和上面的别名一致</servlet-name>
<url-pattern>/myservlet(将来要访问的路径)</url-pattern>
</servlet-mapping>
HTTP协议
什么是HTTP协议
超文本传输协议(HTTP,HyperText TansferProtocol)是互联网上应用最为广泛的一种网络协议,是一个基于请求与响应模式的、无状态的、应用层的协议,运行于TCP协议基础之上。
特点
流程
- 客户与服务器建立连接(三次握手)。
- 客户向服务器发送请求。
- 服务器接受请求,并根据请求返回相应的文件作为应答。
- 客户与服务器关闭连接(四次挥手)。
常见状态码
Servlet详解
1、Servlet核心接口和类
在Servlet体系结构中,除了实现Servlet接口,还可以通过继承GenericServlet或 HttpServlet类,完成编写。
1、GenericServlet
GenericServlet抽象类 :GenericServlet使编写Servlet变得更容易。它提供生命周期方法 init和 destroy的简单实现,要编写一般的 Servlet,只需重写抽象service 方法即可。
2、HttpServlet类
HttpServlet是继承GenericServlet的基础上进一步的扩展。提供将要被子类化以创建适用于Web站点的HTTPservlet的抽象类。HttpServet的子类至少必须重写一个方法,该方法通常是以下这些方法之一
一∶doGet,如果 servlet支持HTTP GET请求
二: doPost,用于HTTP POST 请求
三、doPut,用于 HTTP PUT 请求
四、doDelete,用于 HTTP DELETE 请求
3、URL匹配规则
4、注解匹配
@WebServlet("地址")
常用属性:
name:servlet名字(可选)
value:配置url卢杰,可以配置多个
urlPatterns:配置url路径和value作用一样,不能同时使用
loadOnStartUp:访问优先级,默认值是1.
如果配置了web.xml,也配置了注解,两者不冲突。
2、应用整合
1、request对象
在Servle中用来处理客户端请求需要用doGet或doPost方法的request对象
get和post区别
### 2、request主要方法
String getParameter(String name) 根据表单逐渐名称获取提交数据
void setCharacterEncoding(String charset) 指定每个请求的编码
3、解决get中文乱码
4、post中文乱码
5、response对象
用于响应客户请求并向客户端输出信息
主要方法
解决乱码问题
- 设置服务器端响应的编码格式
- 设置客户端响应内容的头内容的文件类型及编码格式
response.setContentType(“text/html”,“charset=UTF-8”);
6、简单的示例,实现登录
以上是目录结构
- StudentDao .java
package com.lang.servlet.dao;
import com.lang.servlet.entity.Student;
import java.util.List;
public interface StudentDao {
public int insert(Student student);
public int delete(String stuname);
public int update(Student student);
public Student select(String stuname);
public List<Student> selectAll();
}
- StudentDaoImpl .java
package com.lang.servlet.dao.impl;
import com.lang.servlet.dao.StudentDao;
import com.lang.servlet.entity.Student;
import com.lang.servlet.utils.DButils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import javax.swing.plaf.multi.MultiSeparatorUI;
import java.sql.SQLException;
import java.util.List;
public class StudentDaoImpl implements StudentDao {
private QueryRunner queryRunner=new QueryRunner();
@Override
public int insert(Student student) {
return 0;
}
@Override
public int delete(String stuname) {
return 0;
}
@Override
public int update(Student student) {
return 0;
}
@Override
public Student select(String stuname) {
try {
System.out.println(DButils.getConnection());
Student student = queryRunner.query(DButils.getConnection(),"select * from student where stuname=?;",new BeanHandler<Student>(Student.class),stuname);
return student;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public List<Student> selectAll() {
try {
List<Student> students=queryRunner.query(DButils.getConnection(),"select * from student",new BeanListHandler<Student>(Student.class));
return students;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
}
- Student.java
package com.lang.servlet.entity;
public class Student {
private int stuid;
private String stuname;
private String password;
private String phone;
public Student() {
}
public Student(String stuname, String password, String phone) {
this.stuname = stuname;
this.password = password;
this.phone = phone;
}
public Student(int stuid, String stuname, String password, String phone) {
this.stuid = stuid;
this.stuname = stuname;
this.password = password;
this.phone = phone;
}
public int getStuid() {
return stuid;
}
public void setStuid(int id) {
this.stuid = stuid;
}
public String getStuname() {
return stuname;
}
public void setStuname(String stuname) {
this.stuname = stuname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Student{" +
"stuid=" + stuid +
", stuname='" + stuname + '\'' +
", password='" + password + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
- StudentService.java
package com.lang.servlet.service;
import com.lang.servlet.entity.Student;
import java.util.List;
public interface StudentService {
public Student login(String stuname, String password);
public List<Student> showAll();
}
- StudentServiceImpl
package com.lang.servlet.service;
import com.lang.servlet.dao.StudentDao;
import com.lang.servlet.dao.impl.StudentDaoImpl;
import com.lang.servlet.entity.Student;
import com.lang.servlet.utils.DButils;
import java.util.List;
public class StudentServiceImpl implements StudentService{
private StudentDao studentDao=new StudentDaoImpl();
@Override
public Student login(String stuname, String password) {
Student result=null;
try {
DButils.begin();
Student student=studentDao.select(stuname);
if(student!=null){
if(student.getPassword().equals(password)){
result=student;
}
}
DButils.commit();
} catch (Exception e) {
DButils.rollback();
e.printStackTrace();
}
return result;
}
@Override
public List<Student> showAll() {
return null;
}
}
- LoginServlet.java
package com.lang.servlet.servlet;
import com.lang.servlet.entity.Student;
import com.lang.servlet.service.StudentService;
import com.lang.servlet.service.StudentServiceImpl;
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;
import java.io.PrintWriter;
@WebServlet(value = "/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
String stuname= req.getParameter("stuname");
String password= req.getParameter("password");
StudentService studentService=new StudentServiceImpl();
Student student=studentService.login(stuname,password);
if(student!=null){
PrintWriter printWriter=resp.getWriter();
printWriter.println("<html>");
printWriter.println("<head>");
printWriter.println("<meta charset='UTF-8'>");
printWriter.println("<title> 结果页面</title>");
printWriter.println("</head>");
printWriter.println("<body>");
printWriter.println("<h1>登录成功</h1>");
printWriter.println("</body>");
printWriter.println("</html>");
}else {
PrintWriter printWriter=resp.getWriter();
printWriter.println("<html>");
printWriter.println("<head>");
printWriter.println("<meta charset='UTF-8'>");
printWriter.println("<title> 结果页面</title>");
printWriter.println("</head>");
printWriter.println("<body>");
printWriter.println("<h1>登录失败</h1>");
printWriter.println("</body>");
printWriter.println("</html>");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
- DButils.java
package com.lang.servlet.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.stream.IntStream;
public class DButils {
private static DruidDataSource druidDataSource=null;
private static final ThreadLocal<Connection> THREAD_LOCAL=new ThreadLocal<>();
static {
Properties properties=new Properties();
InputStream inputStream=DButils.class.getResourceAsStream("/database.properties");
try {
properties.load(inputStream);
try {
druidDataSource=(DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection connection=THREAD_LOCAL.get();
if (connection==null){
try {
connection=druidDataSource.getConnection();
THREAD_LOCAL.set(connection);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return connection;
}
public static void begin(){
Connection connection=null;
try {
connection=getConnection();
connection.setAutoCommit(false);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
public static void commit(){
Connection connection=null;
try {
connection=getConnection();
connection.commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
closeAll(connection,null,null);
}
}
public static void rollback(){
Connection connection=null;
try {
connection=getConnection();
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
closeAll(connection,null,null);
}
}
public static void closeAll(Connection connection, Statement statement, ResultSet resultSet){
try {
if(connection!=null){
connection.close();
}
if (statement!=null){
statement.close();
}
if(resultSet!=null){
resultSet.close();
}
THREAD_LOCAL.remove();
}catch (Exception e){
e.printStackTrace();
}
}
}
- login,html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/servletLearn/login" method="post">
用户名:<input type="text" name="stuname">
密码:<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
7、问题
上面的案例,调用业务逻辑和显示结果在一个servlet里,会产生设计问题
不符合单一职能原则,各司其职的思想,不利于后期维护
转发和重定向
转发:作用在服务器端,将请求发送给服务器上的其他资源,以共同完成一次请求的处理
1、页面跳转
在调用业务逻辑的Servlet中
request.getRequestDispatcher("/目标url").forword(request,response);
这种跳转,是在服务器内部跳转,地址栏不发生变化,属于同一次请求。
2、数据传递
forward表示一次请求,实在服务器内部跳转,可以共享同一次request作用域中的数据
- request作用域
用于存储数据的空间,作用范围是一次请求有效(一次请求可以经过多次转发)- 可以将数据存入request后,在一次请求过程中的任何位置进行获取
- 可传递任何数据(基本数据类型、对象、数组、集合等)
3、转发的特点
- 转发是服务器行为
- 转发是浏览器只做了一次访问请求
- 转发浏览器地址不变 转发两次跳转之间传输的信息不会丢失,所以可以通过reques进行数据的传递、
- 转发只能将请求转发给同一个Web应用中的组件
4、重定向
作用在客户端,客户端将请求发送给服务器后,服务器响应给客户端一个新的请求地址,客户端重新发送新请求。此种方式地址会变化,客户端发送了两次请求
页面跳转
response.sendRedirect("目标URI“);
数据传递
- response没有作用域,两次request请求中的数据无法共享传递数据
- 通过URI的拼接进行数据传递"webProject/b?username=tom";·
- 获取数据∶ request.getParameter("username);
特点
- 重定向是客户端行为。
- 重定向是浏览器做了至少两次的访问请求。
- 重定向浏览器地址改变。
- 重定向两次跳转之间传输的信息会丢失(rcquest范围)。
- 重定向可以指向任何的资源,包括当前应用程序中的其他资源、同一个站点上的其他应用程序中的资源、其他站点的资源。
Servlet生命周期
1、实例化
当用户第一次访问servlet时,由容器调用Servet的构造器创建具体的servlet对象。也可以在容器启动之后立刻创建实例。使用如下代码可以设置Servlet是否在服务器启动时就创建。
<load-on-startup>1</load-on-startup>
注意∶只执行一次
2、初始化
在初始化阶段,init方法会被调用。这个方法在javax.servlet.Servlet接口中定义。其中,方法以一个ServletConfig类型的对象作为参数。
只执行一次
3、服务
当客户端有一个请求时,容器就会将请求ServletReques与响应ServletResponse对象转给servlet,以参数的形式传给service方法
此方法会执行多次
4、销毁
当Servlet容器停止或者重新启动都会引起销毁Servlet对象并调用destroy方法。
只执行一次
Servlet特性
线程安全问题
Servlet在访问之后,会执行实例化操作,创建一个Servlet对象。而我们Tomcat容器可以同时多个线程并发访问同一个Servlet,如果在方法中对成员变量做修改操作,就会有线程安全的问题。
状态管理
- HTTP协议是无状态的,不能保存每次提交的信息
- 如果用户发来一个新的请求,服务器无法知道它是否与上次的请求有联系。
- 对于那些需要多次提交数据才能完成的Web操作,比如登录来说,就成问题了。
概念
将浏览器与web服务器之间多次交互当作一个整体来处理,并且将多次交互所涉及的数据(即状态)保存下来。
分类
客户端状态管理技术∶将状态保存在客户端。代表性的是Cookie技术。
服务器状态曾理技术∶将状态保存在服务器端。代表性的是session技术(服务器传递sessionID时需要使用Cookie的方式)
和application
Cookie
什么是Cookie
- Cookie是在浏览器访间web服务器的某个资源时,由Web服务器在HTTP响应消息头中附带传送给浏览器的一小段数据。
- 一旦web浏览器保存了某个Cookie,那么它在以后每次访间该web服务器时,都应在HTTP请求头中将这个cookie回传给web服务
- 一个Cookie主要由标识该信息的名称(name)和值(value)组成。
创建Cookie
Cookie ck=new Cookie("code",code);
ck.setPath("/项目名/路径名");
ck.setMaxAge(-1); 内存存储,取值3种,>0有效期,单位秒,=0 浏览器关闭 <0内存存储 默认-1
response.addCookie(ck):添加到response对象种,响应式发送给客户端
获取Cookie
Cookie[] cookies=req.getCookies();
for (Cookie cook:cookies
) {
if(cook.getName().equals("code")){
code=cook.getValue();
break;
}
}
修改Cookie
要求,cookie的name和路径必须一致,才可以修改。否则就是新建Cookie
编码和解码
创建带中文的Cookie
读取带中文的Cookie
Cookie的优点和缺点
优点
- 可配置到期规则。
- 简单性∶Cookie是一种基于文本的轻量结构,包含简单的键值对。
- 数据持久性∶Cookie默认在过期之前是可以一直存在客户端浏览器上的。
缺点
- 大小受到限割∶大多数浏览器对Cookie的大小有4K、8K字节的限制。
- 用户配置为禁用∶有些用户禁用了浏览器或客户端设备接收Cookie的能力,因此限制了这一功能。
- 潜在的安全风险∶Cookie可能会被暮改。会对安全性造成潜在风险或者导致依赖于Cookie的应用程序失败。
Sesion
- Sessio用于记录用户的状态。
- Sesion指的是在一段时间内,单个客户端与web服务器的一连串相关的交互过程
- 在一个Session中,客户可能会多次请求访问同一个资源,也有可能请求访问各种不同的服务器资源。
原理
服务器会为每一次会话分配一个Session对象
同一个浏览器发起的多次请求,同属于一次会话(Session)
首次使用到Session时,服务器会自动创建Session,并创建Cookie存储Sessionld发送回客户端
## 使用
sesion作用域∶
- 拥有存储数据的空间,作用范围是一次会话有效
- 一次会话是使用同一浏览器发送的多次请求。一旦浏览器关闭,则结束会话。
- 可以将数据存入Session中,在一次会话的任意位置进行获取。
- 可传递任何数据(基本数据类型、对象、集合、数组)
获取session
HttpSession session=request.getSession();
Session 保存数据
session.setAttribute("key",value);
Session 获取数据
session.getAttribute("key”);
Seesion移除数据
session,removeAttribute("key");
生命周期
- 开始:第一次使用到的Session的请求产生,创建Session
- 结束
- 浏览器关闭,则失效
- 超时,则失效
- sessopm.setMaxinactiveInterval(seconds);
-手工销毁失效:session.invalidate();
- sessopm.setMaxinactiveInterval(seconds);
ServletContext对象
ServletContext概述
全局对象,也拥有作用域,对应一个Tomcat中的Web应用
当Wweb服务器启动时,会为每一个Web应用程序创建一块共享的存储区域(ServletContext)。
ServletContext在web服务器启动时创建,服务器关闭时销毁。
## 获取方法
- GenericServlet提供了getServletContext()方法(推荐) this.getServletContext();
- HttpServletRequest提供了getServletContext()方法
- HttpSession提供了getServletContext()方法。
作用
获取项目真实路径
获取当前项目在服务器发布的真实路径
servletContext.getRealPath("/"); /代表当前项目
### 获取项目上下文路径
servletContext.getContextPath()
### 全局容器
存储方法和session一样,可以用作计数器
### 特点
唯一性:一个应用对应一个ServletContext
生命周期:只要容器不关闭或者应用不卸载,Servlet就一直存在
过滤器
是处于客户端与服务器目标资源之间的一道过滤技术
作用
执行地位在Servlet之前,客户端发送请求时,会先经过Fiter,再到达目标Servle中;
响应时,会根据执行流程再次反向执行Fiter
可以解决多个Servlet共性代码的冗余问题(例如∶乱码处理、登录验证)
编写过滤器
Servlet API中提供了一个Filter接口,开发人员编写一个Java类实现了这个接口即可,这个Java类称之为过滤器(Filter)
实现过程
- 编写Java类实现Fiter接口
- 在dofFlter方法中编写拦截逻辑
- 设置拦我路径
- 依赖FilterChain
过滤器配置
在自定义Filter类上使用注解@WebFilter(value=“要拦截的路径”)
xml:
<filter>
<filter-name>xml</filter-name>
<filter-class>com.lang.servlet.filter.ServletFileter</filter-class>
</filter>
<filter-mapping>
<filter-name>xml</filter-name>
<url-pattern>/test</url-pattern>
<!--填写要拦截的路径-->
</filter-mapping>
过滤器优先级
过滤器应用
过滤乱码
package com.lang.servlet.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/*")
public class ServletFileter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("fileter");
servletRequest.setCharacterEncoding("UTF-8 ");
servletResponse.setContentType("text/html;charset=UTF-8");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
登陆权限
package com.lang.servlet.filter;
import com.lang.servlet.entity.Student;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/*")
public class ServletFileter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("fileter");
servletRequest.setCharacterEncoding("UTF-8 ");
HttpServletRequest httpServletRequest=(HttpServletRequest)servletRequest;
HttpServletResponse httpServletResponse=(HttpServletResponse) servletResponse;
HttpSession httpSession=httpServletRequest.getSession();
Student student=(Student) httpSession.getAttribute("stu")
if (student!= null) {
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
else {
httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/路径");
}
}
@Override
public void destroy() {
}
}