服务器对象Servlet
Servlet : server applet
-
概念︰运行在服务器端的小程序
servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。*将来我们自定义一个类,实现servlet接口,复写方法。 -
快速入门:
1.创建JavaEE项目
2.定义一个类,实现servlet接口
public class servletDemo1 implements servlet
3.实现接口中的抽象方法
4.配置servlet
在web.xml中配置︰<servlet>
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>com.xjbt.servlet.ServletGo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
-
原理:
1.当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的servlet的资源路径
2.查找web.xml文件,是否有对应的<url-pattern>标签体内容。
3.如果有,则在找到对应的<servlet-class>全类名
4.tomcat会将字节码文件加载进内存,并且创建其对象
5.调用其方法
- servlet中的生命周期方法︰
-
init(ServletConfig servletConfig)
被创建:
init方法,只执行一次
说明一个servlet在内存中只存在一个对象,servlet是单例的
多个用户同时访问时,可能存在线程安全问题。
解决∶尽量不要在servlet中定义成员变量。即使定义了成员变量,也不要修改值
servlet什么时候被创建?
在配置文件web.xml=》<Servlet></Servlet>标签体中配置
指定Servlet创建时机
指定servelet第一次访问时创建 默认
<load-on-startup>-1</load-on-startup>
指定servelet在服务器启动时创建
<load-on-startup>1</load-on-startup>
-
service(ServletRequest servletRequest, ServletResponse servletResponse)
提供服务:
执行service方法,执行多次
每次访问servlet时,service方法都会被调用一次。 -
destroy()
被销毁:
执行destroy方法,只执行一次
只有服务器正常关闭时,才会执行destroy方法。
destroy方法在servlet被销毁之前执行,一般用于释放资源
-
使用注解
servlet3.0 :支持注解配置。可以不需要web.xml了。
步骤︰
- 创建]avaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
- 定义一个类,实现servlet接口
- 复写方法
- 在类上使用@webservlet注解,进行配置
@webservlet(“Servlet资源路径")
告诉计算机(Tomcat服务器)程序的资源路径(“Servlet资源路径),程序全类名称不用写,注释在哪儿就是哪个类
-
Servlet体系结构
- Servlet --接口
|- Genericservlet(Servlet子) --抽象类
GenericServlet :将servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
将来定义servlet类时,可以继承Genericservlet,实现service()方法即问 如果想要其他方法复写即可
|- Httpservlet(servlet孙) --抽象类
定义类继承Httpservlet
复写doGet(get接收数据方式)/doPost(post接收数据方式)方法
- Httpservlet(servlet孙) --抽象类
- Genericservlet(Servlet子) --抽象类
- Servlet --接口
-
Servlet相关配置
urlpartten:servlet 访问路径
一个servlet可以定义多个访问路径︰ @webServlet({" /d4" , "/dd4" , " /ddd4"})
路径定义规则∶
1. /xxx
2. /xxx/xxx:多层路径,目录结构
3. *.do
HTTP
- 概念:Hyper Text Transfer Protocol超文本传输协议
- 传输协议:定义了,客户端和服务器端通信时,发送数据的格式
-
特点∶
- 基于TCP/IP的高级协议
- 默认端口号:80
- 基于请求/响应模型的:一次请求对应一次响应
- 无状态的:每次请求之间相互独立,不能交互数据
-
历史版本:
1.0:每一次请求响应都会建立新的连接
1.1:复用连接
- 请求消息数据格式
字符串格式∶
POST /login.html HTTP/1.1 -请求行
Host:localhost
User-Agent: Mozilla/5.0 (windows NT 6.1; win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0Accept: text/html,application/xhtml+xml, application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-Tw;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflate
connection: keep-alive
upgrade-Insecure-Requests: 1 -请求头
-请求空行
username=zhangsanl -请求体 POST方式才有请求体
-
请求行
请求方式【GET/POST】 请求url【/login.html】 请求协议/版本【HTTP/1.1】请求方式:HTTP协议有7中请求方式,常用的有2种
GET:
1. 请求参数在请求行中,在url后。
2. 请求的url长度有限制的
3. 不太安全
POST :
1.请求参数在请求体中
2.请求的url长度没有限制的
3.相对安全 -
请求头:客户端浏览器告诉服务器一些信息
请求头名称:请求头值常见的请求头:
User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
可以在服务器端获取该头的信息,解决浏览器的兼容性问题
Referer : http://localhost/login.html
*告诉服务器,我(当前请求)从哪里来?
作用:
1. 防盗链:防止其他网站的地址指向当前地址的资源页面
2. 统计工作: -
请求空行
空行 -
请求体(正文)
-
响应消息数据格式
响应消息∶服务器端发送给客户端的数据
HTTP/1.0 200 oK =>响应行
content-Type: text/html; =>响应头
charset=UTF-8content-Length: 101
Date: wed,06 Jun 2018 07:08:42GMT
=>响应空行
<html> =>响应体
<head>
<title>$Title$</title></ head>
<body>
hello , response< / body>
</html>
-
响应行
协议/版本【HTTP/1.0】 响应状态码【200】 状态码描述【oK】状态码都是3位数字
分类:
1xx:服务器接收客户端消息,但没有接收完成,等待一段时间后,发送1xx多状态码
2xx:成功。代表:200
3xx:重定向。代表:302(重定向),304(访问缓存,浏览器访问服务器图片,浏览器如果有该图片的缓存,服务器发送304,表示不用在请求这张图片)
4xx:客户端错误。
404:(请求路径没有对应的资源)
405:请求方式没有对应的doXxx方法
5xx:服务器端错误。代表:500(服务器内部出现异常) -
响应头
content-Type∶服务器告诉客尸端本次响应体数据格式以及编码格式
content-disposition:服务器告诉客户端以什么格式打开响应体数据
值:
in-line:默认值,在当前页面内打开
attachment;filename=xxx∶以附件形式打开响应体。文件下载 -
响应空行
-
响应体:传输的数据
-
Request对象和Response对象的原理
-
Request继承结构体系
- servletRequest --接口
l继承- HttpservletRequest --接
l实现
org.apache.catalina.connector.RequestFacade类(tomcat)
- HttpservletRequest --接
- servletRequest --接口
-
request功能∶
- 获取请求数据:
-
获取请求行数据
请求行:GET /xjbt/demo1?name=zhangsan HTTP/1.1
方法∶
string getMethod()获取请求方式:GET
*string getcontextPath()获取虚拟目录:/day14
string getservletPath()获取servlet路径:/demo1
string getQuerystring()获取get方式请求参数: name=zhangsan
/day14/demo1获取请求URI
*string getRequestURI() /day14/demo1
*stringBuffer getRequestURL() http://localhost/day14/demo16
string getProtocol()获取协议及版本:HTTP/1.1
string getRemoteAddr() 获取客户机的IP地址
-
获取请求头数据
方法∶
*string getHeader(string name):通过请求头的名称获取请求头的值
Enumeration<string> getHeaderNames():获取所有的请求头名称
示例:@WebServlet("/ServletText02") public class ServletText02 extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { 1,获取请求头 user-agent String agent = req.getHeader("user-agent"); if(agent.contains("Chrome")){ System.out.println("谷歌浏览器"); }else if(agent.contains("Firefox")){ System.out.println("火狐浏览器"); } System.out.println("----------------------------------------------------"); 2,所有请求头名子 Enumeration<String> headerNames = req.getHeaderNames(); while (headerNames.hasMoreElements()){ String headerName = headerNames.nextElement(); //根据名称获取请求头值 String header = req.getHeader(headerName); System.out.println(headerName+"---"+header); } System.out.println("====================================================="); 3,获取请求头 referer 是从哪里访问 String referer = req.getHeader("referer"); System.out.println(referer); System.out.println("-----------"); 3,防盗链 if(referer != null){ if( referer.contains ("newServlet")){ //正常访问 response.setContentType( "text/htm1;charset=utf-8" ); response.getWriter().write("播放电影...." ); }else{ //盗链 response.setContentType( "text/html;charset=utf-8"); response.getWriter().write("想看电影吗?来优酷吧..."); } } } }
-
获取请求体数据:
请求体∶只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
步骤∶- 获取流对象
BufferedReader getReader():获取字符输入流,只能操作字符数据
*servletInputstream getInputstream()∶获取字节输入流,可以操作所有类型数据【文件上传】
- 再从流对象中拿数据
@WebServlet("/RequestText02") public class RequestText02 extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { 1获取请求体 1)获取字符输入流 BufferedReader reader = req.getReader(); String line=null; 2)读取字符 while((line=reader.readLine())!=null){ =>按行读取数据 System.out.println(line); } } }
- 获取流对象
-
- 其他功能︰
-
获取请求参数通用方式
string getParameter(String name) :根据参数名称获取参数值username=zs&password=123
String[] getParametervalues(string name) :根据参数名称获取参数值的数组hobby=xx&hobby-game
Enumeration<string> getParameterNames():获取所有请求的参数名称
Map<String,string[]> getParameterMap():获取所有参数的map集合
@WebServlet("/RequestText03") public class RequestText03 extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { 获取请求参数通用方式 1)通过参数名称获取参数值对应到页面的name值 适用于单个键值对 String username = req.getParameter("username"); //System.out.println(username); 2)通过参数名获取参数值数据,复选框 数组 String[] hobbies = req.getParameterValues("hobby"); for (String hobby: hobbies) { //System.out.println(hobby); } 3)获取所有的参数名 Enumeration<String> parameterNames = req.getParameterNames(); while (parameterNames.hasMoreElements()){ System.out.println(req.getParameter(parameterNames.nextElement())); } 4)获取所有的参数名 map集合 Map<String, String[]> parameterMap = req.getParameterMap(); Set<String> keySet = parameterMap.keySet(); =>获取name【map的key[]】 for (String name:keySet){ System.out.println(name); String[] values = parameterMap.get(name); =>获取每个name对应的值【通过map的key获取map的值values[]】 for (String value:values){ System.out.println(value); } System.out.println("--------"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); =>复用用dopost方法 } }
中文乱码问题:
get方式:,tomcat 8 已经将get方式乱码问题解决了
post方式会乱码
解决∶在获取参数前,设置request的编码request.setcharacterEncoding("utf-8");
-
请求转发︰—种在服务器内部的资源跳转方式
步骤︰
1.通过request对象获取请求转发器对象︰RequestDispatcher getRequestDispatcher(String path)
2.使用RequestDispatcher对象来进行转发∶forward(ServletRequest request,ServletResponse response)
特点∶
1.浏览器地址栏路径不发生变化。
2.只能转发到当前服务器内部资源中。
3.转发是一次请求。 -
共享数据
域对象:一个有作用范围的对象,可以在范围内共享数据
reauest域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
方法:
void setAttribute(string name,object obj)
:存储数据
object getAttitude(string name)
:通过键获取值
void removeAttribute(string name)
:通过键移除键值对
-
需求:
1.编写login.html登录页面
2.username & password 两个输入框
2.使用Druid数据库连接池技术,操作mysql, day14数据库中user表
4.使用JdbcTemplate技术封装JDBC
5.登录成功跳转到successservlet展示:登录成功!用户名,欢迎您
6.登录失败跳转到Failservlet展示:登录失败,用户名或密码错误
-
开发步骤:
- 创建项目,导入html页面,配置文件,jar包
- 创建数据库环境
3.创建User实体类,属性是user表的字段CREATE DATABASE if not exists jdbc; USE jdbc; CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE NOT NULL, PASSWORD VARCHAR(32) NOT NULL );
- 编写基于druid数据库池的jdbcUtils
public class JDBCUtilsTest { private static DataSource ds;//数据库连接池对象 static{ try { Properties pro = new Properties(); //查找jdbc.properties数据库配置文件 InputStream is = JDBCUtilsTest.class.getClassLoader().getResourceAsStream("jdbc.properties"); pro.load(is); ds= DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取数据库连接池 * @return */ public static DataSource getDataSource(){ return ds; } /** * 获取数据库连接 * @return * @throws SQLException */ public static Connection getConnection() throws SQLException { return ds.getConnection(); } }
- 创建dao提供login方法
public class UserDao { //声明template公用里面的方法 private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDatasource()); /** * 用户登陆方法 填写表单 * @param loginUser 用户名密码 * @return 包含用户全部数据的user对象 */ public User login(User loginUser){ try { String sql = "select * from user where username=? and password=?"; User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginUser.getUsername(), loginUser.getPassword()); return user; } catch (DataAccessException e) { e.printStackTrace(); return null; } } }
- 测试下dao是否成功
public class UeserDaoTest { @Test public void testLogin(){ User user=new User(); user.setUsername("zhangsan"); user.setPassword("123"); UserDao userdao=new UserDao(); User loginuser = userdao.login(user); System.out.println(loginuser); } }
- 写ServletLogin类
@WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置编码 req.setCharacterEncoding("utf-8"); /* //获取传过来的值 String username=req.getParameter("username"); String password=req.getParameter("password"); //创建User对象 User loginUser=new User(); loginUser.setUsername(username); loginUser.setPassword(password);*/ Map<String, String[]> map = req.getParameterMap(); User loginUser=new User(); try { //使用beanutils BeanUtils.populate(loginUser,map); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } //创建dao方法 UserDao userdao=new UserDao(); User user = userdao.login(loginUser); if(user==null){ //登录失败 req.getRequestDispatcher("/Failservlet").forward(req,resp); }else{ //登录成功 req.setAttribute("username",user.getUsername()); req.setAttribute("password",user.getPassword()); req.getRequestDispatcher("/successServlet").forward(req,resp); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
- 编写successServlet
@WebServlet("/successServlet") public class successServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码 response.setContentType("text/html;charset=utf-8"); response.getWriter().write("欢迎"+request.getAttribute("username")); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
- 编写FailServlet
@WebServlet("/Failservlet") public class Failservlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码 response.setContentType("text/html;charset=utf-8"); response.getWriter().write("登录失败"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
- login.html中form表单的action路径的写法
虚拟目录+Servlet的资源路径 - BeanUtils工具类,简化数据封装
用于封装JavaBean【标准的Java类】- 要求:
1.类必须被public修饰
2.必须提供空参的构造器
3.成员变量必须使用private修饰
4.提供公共setter和getter方法 - 功能:封装数据
- 概念:
成员变量:
属性:setter和getter方法截取后的产物
例如:getUsername() --> Username--> username - 方法:
setProperty()
getProperty()
populate(Object obj , Map map):将map集合的键值对信息,封装到对应的JavaBean对象中
- 要求:
-
Response对象
-
设置响应行【:HTTP/1.1 200 ok】
设置状态码:setstatus(int sc)
-
设置响应头:
setHeader(string name,string value)
-
设置响应体:
使用步骤︰- 获取输出流
字符输出流:Printwriter getwriter()
字节输出流:servletoutputstream getoutputstream()
- 使用输出流,将数据输出到客户端浏览器
- 获取输出流
-
案例
- 重定向
//重定向 response.setstatus(302);//设置重定向状态码 response.setHeader ("location" , " /newServlet/ServletResponseTest02");//设置响应头*/ //动态获取虚拟目录 string contextPath = request.getcontextPath(); //重定向2只有重定向的资源路径发生了改变 response.sendRedirect(s: contextPath+" /servletResponseTesto2"");
- 重定向的特点
1.地址栏发生变化
2.重定向可以访问其他站点(服务器)的资源
3.重定向是两次请求 - 转发的特点︰
1.转发地址栏路径不变
2.转发只能访问当前服务器下的资源
3.转发是一次请求
- 路径:
-
相对路径∶通过相对路径不可以确定唯一资源
规则:找到当前资源和目标资源之间的相对位置关系 -
绝对路径∶通过绝对路径可以确定唯一资源
给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
建议虚拟目录动态获取:request.getcontextPath()
<a> , <form>重定向【sendRedirect(s: contextPath+"/servletResponseTesto2");
】...
给服务器使用:不需要加虚拟目录
转发路径【getRequestDispatcher("/successServlet").forward(req,resp)
】
-
2.服务器输出字符数据到浏览器
步骡︰
1.获取字符输出流
2.输出数据乱码问题∶
获取流对象之前,设置流的默认编码: ISO-8859-1设置为:GBKresponse.setCharacterEncoding( "utf-8");
告诉浏览器,服务器发送的消息体数据的编码。建议浏览器使用该编码解码
response.setHeader( "content-type","text/htm1;charset=utf-8");
简单的形式,设置编码,是在获取流之前设置
response.setcontentType("text/html;charset=utf-8");
1.Printwriter pw = response.getwriter();获取的流的默认编码是ISO-8859-1
2.设置该流的默认编码
-
3. 验证码:
```
int width=100;
int height=50;
//1.创建—对象,在内存中图片(验证码图片对象)
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2.美化图片
Graphics graphics = image.getGraphics();//画笔对象
//填充粉红色
graphics.setColor(Color.pink);//设置画笔颜色
graphics.fillRect(0,0,width,height);//填充
//蓝色边框
graphics.setColor(Color.blue);
graphics.drawRect(0,0,width-1,height-1);
//写数字
String str="ABCDEFGHIJKLMNOPQISCUVWSYZabcdefghijklmnopqiscuvwsyz0123456789";
//生成随机角标
Random rand = new Random();
for (int i = 1; i <=4 ; i++) {
int index = rand.nextInt(str.length());
//获取随机字符
char c = str.charAt(index);//随机字符
graphics.drawString(c+"",width/5*i,height/2);
}
//4画干扰线
graphics.setColor(Color.green);
for (int i = 1; i <=10; i++) {
int x1=rand.nextInt(width);
int y1=rand.nextInt(height);
int x2=rand.nextInt(width);
int y2=rand.nextInt(height);
graphics.drawLine(x1,y1,x2,y2);
}
//3.将图片输出到页面展示
ImageIO.write(image,"jpg",response.getOutputStream());
```
```
var randimg = document.getElementById("randImg");
randimg.onclick=function(){
var date=new Date().getTime();//时间戳,永远不会重复
randimg.src="/newServlet/ServletResponseTest03?"+date
}
```
-
ServletContext对象:
- 概念:代表整个web应用,可以和程序的容器(服务器)来通信
- 获取:
- 通过request对象获取:
request.getservletcontext();
- 通过Httpservlet获取:
this.getservletcontext();
- 通过request对象获取:
- 功能:
-
获取MIME类型
MIME类型:在互联网通信过程中定义的一种文件数据类型
格式:大类型/小类型【text/ html】【image/jpeg】
获取:string getMimeType( string file)
-
域对象:共享数据
setAttribute(string name,object value)
getAttribute(string name)
removeAttribute(string name)
ServletContext对象范围:所有用户所有请求的数据 -
获取文件的真实(服务器)路径
String realPath = context.getRealPath("/b.txt" ) ; //web目录下资源访问
String c = context.getRealPath( s: "/WEB-INF/c.txt"); //WEB-INF目录下的资源访问
String a = context.getRealPath( s: "/WEB-INF/c1asses/a.txt");//src目录
-
- 下载:
//1.获取请求参数
String filename = request.getParameter("filename");
//2.使用字节输入流加载文件进内存
//找到文件服务器路径
ServletContext servletContext = this.getServletContext();
//获取文件真实路径
String realPath = servletContext.getRealPath("/img/" + filename);
//使用字节流关联
FileInputStream fils = new FileInputStream(realPath);
//3.设置response的响应头
//3.1设置响应头类型:content-type
String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
response.setHeader("content-type" ,mimeType);
//解决中文文件名问题
//1.获取user-agent请求头、
String agent = request.getHeader( "user-agent");
//2.使用工具类方法编码文件名即可
filename = DownLoadUtils.getFileName(agent,filename);
//3.2设置响应头打开方式: content-disposition
response.setHeader("content-disposition","attachment;filename="+filename);
//4.将输入流的数据写出到输出流中
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[ 1024* 8];//缓冲区
int len = 0;//读到的个数
while( ( len = fils.read(buff))!=-1){
sos.write( buff,0,len);
}
fils.close();