JavaWeb学习笔记(二)
6、Servlet
6.1、简介
- servlet是sun公司用来开发动态web的工具
- sun在API中提供了一个接口叫做 servlet,开发一个servlet程序需要两个步骤
- 编写一个java类实现servlet接口
- 把编写好的java类部署到web服务器上
把实现了Servlet接口的Java程序叫做Servlet
6.2、HelloServlet
Servlet接口sun公司提供了两个默认的实现类: HttpServlet,GenericServlet
-
构建一个普通的Maven项目,删掉里面的src目录,在这个项目里面建立Moudel(模块),这个空的工程就是Maven主工程
-
在Maven父子工程中
父项目的pom.xml中自动生成
<!--读pom文件时,要去读modules下的子pom文件--> <modules> <module>HelloServlet</module> </modules>
子类项目的pom.xml自动生成
<!--使子项目继承父项目的设置,避免导入重复依赖 eg:son extends parent--> <parent> <artifactId>javaweb-02-servlet</artifactId> <groupId>com.servlet</groupId> <version>1.0-SNAPSHOT</version> </parent>
-
编写一个Servlet程序
-
编写一个普通类,实现Servlet接口,这里我们直接继承HttpServlet(
HttpServlet实现了Servlet接口
)public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter();//获取响应流 //响应流输出信息 writer.print("hello servlet"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
编写servlet映射
我们编写的java程序需要被浏览器访问,而浏览器需要访问web服务器,所以我们需要在web服务器中部署servlet,给他一个访问的具体路径。所以我们在web.xml中注册servlet和servlet-mapping
<!--注册servlet--> <servlet> <servlet-name>helloServlet</servlet-name> <servlet-class>com.thomas.HelloServlet</servlet-class> </servlet> <!--每一个servlet对应一个映射--> <servlet-mapping> <servlet-name>helloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
配置tomcat
-
启动项目
6.3、Servlet原理
- Web Client 向Servlet容器(Tomcat)发出请求
- Tomcat接收Web Client的请求
- Servlet容器创建一个HttpResquest对象,将请求的信息封装到对象中
- Servlet容器创建一个HttpResponse对象
- Servlet容器调用HttpServlet对象的service(),把httpResquest和httpResponse传给HttpServlet对象
- HttpServlet调用HttpRequest对象的有关方法,获取http请求的信息
- HttpServlet调用HttpResponse对象的有关方法,生成响应数据
- Servlet容器把HttpServlet的响应结果传给Web Client
6.4、Mapping问题
-
一个Servlet可以指定一个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
一个Servlet可以指定多个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello4</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello5</url-pattern> </servlet-mapping>
-
一个Servlet可以指定通用映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
-
默认请求路径
<!--默认请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
-
指定一些后缀或者前缀等等….
<!--可以自定义后缀实现请求映射 注意点,*前面不能加项目映射的路径 --> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.cxd</url-pattern> </servlet-mapping>
-
优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求
<!--404--> <servlet> <servlet-name>error</servlet-name> <servlet-class>com.thomas.servlet.ErrorServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
6.5、ServletContext对象
当web容器启动时,会创建一个servletContext对象,可以通过这个对象让servlet之间进行通信,代表当前的web应用
-
共享数据
我们在一个Servlet中通过servlerContext对象保存(set)的数据,可以在另一个servlet通过servlerContext对象get到
public class SetServletContext extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); String name = "thomas冯"; resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); servletContext.setAttribute("username",name); } }
public class GetServletContext extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); String username = (String) servletContext.getAttribute("username"); resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); resp.getWriter().print("username : " + username); } }
-
获取初始化参数
web.xml
<!--配置一些web应用初始化参数--> <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param>
获取
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); String url = context.getInitParameter("url"); resp.getWriter().print(url); }
-
请求转发
请求转发和重定向的区别
forward(转发):
是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,因为这个跳转过程实在服务器实现的,并不是在客户端实现的所以客户端并不知道这个跳转动作,所以它的地址栏还是原来的地址
redirect(重定向):
是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL
转发是服务器行为,重定向是客户端行为
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); System.out.println("进入了sd4"); RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");//转发的请求路径 requestDispatcher.forward(req,resp);//调用forward实现请求转发 //合并写 context.getRequestDispatcher("/gp").forward(req,resp); }
-
读取资源文件
##properties文件内容 username=root12312 password=zxczxczxc
public class ServletDemo05 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/kuang/servlet/aa.properties"); Properties prop = new Properties(); prop.load(is); String user = prop.getProperty("username"); String pwd = prop.getProperty("password"); resp.getWriter().print(user+":"+pwd); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
6.6、HttpServletResponse
web服务器接收了web client的请求,生成两个对象:HttpServletResponse和HttpServletRequest
- response负责把数据传给浏览器
- request负责读取client请求的信息
使用HttpServletResponse让浏览器下载文件
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取文件路径
String filePath = "D:\\ForStrong_java\\javaweb\\javaweb-02-servlet\\ServletHttpResponse\\target\\classes\\1.jpg";
//获取文件名
String fileName = filePath.substring(filePath.lastIndexOf("\\") + 1);
//获取文件输入流
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
//获取向浏览器的输出流
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(resp.getOutputStream());
//让浏览器知道我们要下载文件,需要设置头,让浏览器支持。中文文件名URLEncoder.encode编码,否则有可能乱码
resp.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName,"utf-8"));
//向浏览器输出数据
byte[] buffer = new byte[1024];
int len = 0;
while ((len = bufferedInputStream.read(buffer))>0){
bufferedOutputStream.write(buffer,0,len);
}
//关闭流
bufferedInputStream.close();
bufferedOutputStream.close();
}
response实现验证码,三秒刷新一次
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//写一张图片
BufferedImage bufferedImage = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);//需要一张画板
Graphics graphics = bufferedImage.getGraphics();//需要一只画笔
//设置背景
graphics.setColor(Color.white);//设置颜色
graphics.fillRect(0,0,80,20);
//设置内容
graphics.setColor(Color.blue);
graphics.setFont(new Font(null,Font.ITALIC,20));
graphics.drawString(Objects.requireNonNull(makeNum()),0,20);
//告诉浏览器,这个请求用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//给这个响应设置三秒刷新一次,且把缓存机制关掉
resp.setHeader("refresh","3");
//将这张图片写入浏览器
ImageIO.write(bufferedImage,"jpg",resp.getOutputStream());
}
response实现重定向
//参数为:项目名/地址名
resp.sendRedirect("/ServletHttpResponse/getImage");
<%--action:这里提交的路径,需要寻找到项目的路径--%>
<%--${pageContext.request.contextPath}代表当前的项目--%>
<form action="${pageContext.request.contextPath}/redirect" method="get">
username <input name="username" type="text"> <br>
password: <input name="password" type="password"> <br>
<input type="submit">
</form>
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username +": "+password);
//参数为:项目名/地址名
resp.sendRedirect("/ServletHttpResponse/success.jsp");
}
6.7、HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息
request获取请求的数据,并请求转发
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//获取表单数据
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobbies");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbies));
//请求转发
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
<form action="${pageContext.request.contextPath}/login" method="post">
username <input name="username" type="text"> <br>
password: <input name="password" type="password"> <br>
hobbies:
<input name="hobbies" type="checkbox" value="女孩">女孩
<input name="hobbies" type="checkbox" value="代码">代码
<input name="hobbies" type="checkbox" value="电影">电影
<input name="hobbies" type="checkbox" value="爬山">爬山
<input type="submit">
</form>
7、Cookie、Session
7.1、会话
- 会话:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话
- 有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学,曾经来过,称之为有状态会话
-
一个网站,怎么证明你来过?
- 客户端 服务端
- 服务端给客户端一个 信件,客户端下次访问服务端带上信件就可以了(cookie)
- 服务器登记你来过了,下次你来的时候我来匹配你(seesion)
- 客户端 服务端
7.2、保存会话的两种技术
cookie
- 客户端技术 (响应,请求)
session
- 服务器技术,利用这个技术,可以保存用户的会话信息? 我们可以把信息或者数据放在Session中
7.3、Cookie
-
从请求中拿到cookie信息
-
服务器响应给客户端cookie
Cookie[] cookies = req.getCookies(); //获得Cookie cookie.getName(); //获得cookie中的key cookie.getValue(); //获得cookie中的vlaue new Cookie("lastLoginTime", System.currentTimeMillis()+""); //新建一个cookie cookie.setMaxAge(24*60*60); //设置cookie的有效期 resp.addCookie(cookie); //响应给客户端一个cookie
cookie:一般会保存在本地的 用户目录下 appdata
一个网站cookie是否存在上限
- 一个Cookie只能保存一个信息
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
- Cookie大小有限制4kb
- 300个cookie浏览器上限
删除Cookie
- 不设置有效期,关闭浏览器,自动失效
- 设置有效期时间为 0
编码解码
URLEncoder.encode("测试","utf-8")
URLDecoder.decode(cookie.getValue(),"UTF-8")
7.4、Session
什么是Session:
- 服务器会给每一个用户(浏览器)创建一个Seesion对象;
- 一个Seesion独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
- 用户登录之后,整个网站它都可以访问!–> 保存用户的信息;保存购物车的信息……
使用Session
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//得到Session
HttpSession session = req.getSession();
//给Session中存东西
session.setAttribute("name",new Person("测试",1));
//获取Session的ID
String sessionId = session.getId();
//判断Session是不是新创建
if (session.isNew()){
resp.getWriter().write("session创建成功,ID:"+sessionId);
}else {
resp.getWriter().write("session以及在服务器中存在了,ID:"+sessionId);
}
//Session创建的时候做了什么事情;
// Cookie cookie = new Cookie("JSESSIONID",sessionId);
// resp.addCookie(cookie);
}
获得Session
HttpSession session = req.getSession();
Person person = (Person) session.getAttribute("name");
System.out.println(person.toString());
HttpSession session = req.getSession();
session.removeAttribute("name");
//手动注销Session
session.invalidate();
会话自动过期:web.xml配置
<!--设置Session默认的失效时间-->
<session-config>
<!--15分钟后Session自动失效,以分钟为单位-->
<session-timeout>15</session-timeout>
</session-config>
7.5 cookie和seesion的异同
- **相同点: **
- cookie和session都是为了解决http协议无状态的特征
-
区别:
- cookie数据是存放在客户端本地的,session数据是存放在服务器的,但是服务端的session的实现对客户端的cookie有依赖关系的
- cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session
- session会在一段时间内存放在服务器,如果session过多,会导致服务器压力过大,性能降低。如果考虑服务器性能方面应该使用cookie
- session会在一段时间内存放在服务器,如果session过多,会导致服务器压力过大,性能降低。如果考虑服务器性能方面应该使用cookie
- 一个用户在一个站点上可以有多个cookie,但是只有一个session