JavaWeb学习笔记
内容
01.web基本概念
技术栈:Servlet/Jsp,Asp,PHPN
动态web
02.web服务器
2.1 web 语言
ASP:
-
-
微软:国内最早流行的就是ASP
-
在HTML中嵌入VB脚本,ASP+COM
-
在ASP开发中,基本一个页面都有几千行的业务代码,极其混乱。
-
维护成本高。
<% system.out.pringln("hello")%>
PHP:
-
PHP开发速度很快,功能很强大,跨平台,代码很简单(中国70%网站都是中小型都用PHP)。
-
无法承载流量大的情况。
JSP/servlet:
-
sun公司主推的B/S架构
-
基于JAVA语言(所有大公司,或者一些开源组件,都是java写的)
-
可以承载三高的问题
2.2 web服务器
IIS :
-
ASP,windows自带
Tomcat:
-
Apache基金会核心项目之一。
-
完美支持JSP/servlet标准。
-
支持技术先进,性能稳定,免费。
-
开源,轻量级,适合中小企业和并发不高的场合。
-
处理静态html不如Apache。
03 HTTP
http1.0:客户端和服务器连接后,只能获取一个web资源,断开连接。
http1.1:客户端和服务器连接后,可以获得多个web资源。
04 Maven
javaweb开发中,需要使用大量的jar包,我们手动去导入。
Maven的核心思想:约定大于配置!
4.1. IDEA中配置Maven
-
配置一个包含基本包的项目(不勾选就是最干净的Maven项目)
-
自动下载源码
-
配置Tomcat
4.2 pom文件
Tomcat服务器的web目录
pom.xml是Maven的核心配置文件
maven仓库
maven会导入这个JAR包所依赖的其他JAR包;
maven由于约定大于配置,之后例如mybatis里可能会遇到我们写的配置文件,无法被导出或者生效的问题,解决方案:
//在build中配置resources <!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
日志排错
启动项目后不能打开Tomcat原来的examples/ROOT等项目。解决
05 Servlet
5.1 导HttpServlet包思路
-
快捷键Alt+Ent Maven导入本地没有
-
Maven仓库也没有
-
最后联想到Tomcat对servlet有很强的支持。所以去Tomcat的lib目录下找,找到。(Servlet-api,返回Maven仓库去找,使用人数最多的那个。)
5.2 未走数据库
需要两个东西,Class类+类的web.xml配置映射
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); PrintWriter writer = resp.getWriter(); writer.println("<html>"); writer.println("hello,servlet!"); writer.println("</html>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }
<!--web.xml中是配置web的核心应用--> <web-app> <display-name>Archetype Created Web Application</display-name> <!--注册servlet--> <servlet> <servlet-name>helloServlet</servlet-name> <servlet-class>HelloServlet</servlet-class> </servlet> <!--一个servlet有一个mapping--> <servlet-mapping> <servlet-name>helloServlet</servlet-name> <!--请求路径--> <url-pattern>/UrltohelloServlet</url-pattern> </servlet-mapping> </web-app>
访问localhost/项目名/Urltohelloservlet
5.3 servlet原理
建立子项目,删掉项目里的src目录,再在这个项目里建立Module。
5.4 mapping问题
-
一个servlet可以指定一个映射路径
-
一个servlet可以指定多个映射路径
-
一个servlet可以指定通用映射路径(*通配符)
-
指定一些前缀或者后缀等...
<servlet-mapping> <servlet-name>helloServlet</servlet-name> <url-pattern>/secendurl</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>helloServlet</servlet-name> <url-pattern>*must</url-pattern> /*/xxx/*must不行*/ </servlet-mapping>
-
优先级
指定了固定的映射路径 > 通配
5.5 getServletContext()
-
servlet里的一些方法
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // this.getInitParameter() 初始化参数 对应web.xml里<init-param> // this.getServletConfig() Servlet配置 对应web.xml全部配置 // this.getServletContext() Servlet上下文 重点 ServletContext servletContext = this.getServletContext(); } }
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象。它代表了当前的web应用。
应用
1.共享数据
在一个Servlet中保存数据(任何数据都可以保存),可以在另一个Servlet中拿到。
#HelloServlet.class中保存
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // this.getInitParameter() 初始化参数 对应web.xml里<init-param> // this.getServletConfig() Servlet配置 对应web.xml全部配置 // this.getServletContext() Servlet上下文 重点 ServletContext servletContext = this.getServletContext(); servletContext.setAttribute("username", "xiaoyang"); } }
#GetServlet.class中拿到,并在网页打印
public class GetServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); Object username = servletContext.getAttribute("username"); resp.setContentType("text/html;utf-8"); resp.getWriter().print(username.toString()); } }
#别忘了web.xml文件
<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>hello</servlet-name> <servlet-class>HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet> <servlet-name>getc</servlet-name> <servlet-class>GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>getc</servlet-name> <url-pattern>/getc</url-pattern> </servlet-mapping> </web-app>
访问一次localhost/hello数据存储,访问一次localhost/getc拿到数据
2.获得初始化参数
#testgetServletContext.class
public class TestgetServletContext extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); String jdbcUrl = servletContext.getInitParameter("jdbcUrl"); resp.getWriter().print(jdbcUrl); } }
#web.xm
<context-param> <param-name>jdbcUrl</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param> <servlet> <servlet-name>gp</servlet-name> <servlet-class>TestgetServletContext</servlet-class> </servlet> <servlet-mapping> <servlet-name>gp</servlet-name> <url-pattern>/gp</url-pattern> </servlet-mapping>
访问localhost/gp
3.请求转发
#testDispatch.class
public class TestDispatch extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); resp.getWriter().print("hello<br>"); servletContext.getRequestDispatcher("/gp").forward(req, resp); } }
#web.xml
<servlet> <servlet-name>td</servlet-name> <servlet-class>TestDispatch</servlet-class> </servlet> <servlet-mapping> <servlet-name>td</servlet-name> <url-pattern>/td</url-pattern> </servlet-mapping>
访问/td,得到gp的资源。并且打开F12,状态码200说明,转发 ≠ 重定向。
4.读取资源文件(项目中大量运用) -> 失败
Properties
-
在java目录下新建properties
-
在resources目录下新建properties。发现都被打包到了同一个路径下。
#db.properties
username = xiaoyang
password = 1234
新建后RUN一下,在target/项目/WEB_INFF/classes下生成properties文件【如果没有生成,则考虑04Maven里"约定大于配置",在pom文件里配置一下<build>】
#TestgetResource.class
public class TestgetResource extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //得到一个文件流 InputStream is = this.getServletContext().getResourceAsStream("/WEB_INF/classes/db.properties"); Properties prop = new Properties(); //加载这个流 prop.load(is); resp.getWriter().print(prop.getProperty("username").toString()); resp.getWriter().print(prop.getProperty("password").toString()); } }
#web.xml
<servlet> <servlet-name>tr</servlet-name> <servlet-class>TestgetResource</servlet-class> </servlet> <servlet-mapping> <servlet-name>tr</servlet-name> <url-pattern>/tr</url-pattern> </servlet-mapping>
5.6 HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpservletRequest对象,代表响应的一个HttpservletResponse;
-
如果要获取客户端请求过来的参数:找HttpservletRequest
-
如果要给客户端响应一些信息:找HttpServletResponse
1. 方法简单分类
负责向浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException; PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头
void setCharacterEncoding(String var1); void setContentLength(int var1); void setContentLengthLong(long var1); void setContentType(String var1); void setDateHeader(String var1, long var2); void addDateHeader(String var1, long var2); void setHeader(String var1, String var2); void addHeader(String var1, String var2); void setIntHeader(String var1, int var2); void addIntHeader(String var1, int var2);
2.常见应用
-
向浏览器输出消息
上面演示很多
-
下载文件
过程:要获取下载文件的路径;文件名;想办法让浏览器能够支持下载;获取下载文件的输入流;创建缓冲区;获取 OutputStream对象;将FileOUtputStream流写入到buffer缓冲区;使用OutputStream将缓冲区中的数据输出到客户 端。
#FileServlet.class 太老了,现在一般不用
public class FileServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //要获取下载文件的路径; String realPath = this.getServletContext().getRealPath("E:\\Java项目\\javaweb-servlet\\servlet-03-response\\target\\classes\\2.png"); System.out.println("real-path"+realPath); // 文件名; String filename = realPath.substring(realPath.lastIndexOf("\\") + 1); // 想办法让浏览器能够支持下载; resp.setHeader("Content-Disposition", "attachment;filename="+filename); // 获取下载文件的输入流; FileInputStream in = new FileInputStream(realPath); // 创建缓冲区; int len = 0; byte[] buffer = new byte[1024]; // 获取OutputStream对象; ServletOutputStream out = resp.getOutputStream(); // 将FileOUtputStream流写入到buffer缓冲区;使用OutputStream将缓冲区中的数据输出到客户端。 while ((len=in.read(buffer)) != -1) { out.write(buffer, 0, len); } in.close(); out.flush(); out.close(); } }
-
验证码功能
前端实现
后端实现,需要i用到Java的图片类,生产一个图片。
#ImageServlet
public class ImageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //自动3秒刷新 resp.setHeader("refresh", "3"); //在内存中创建一个图片 BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); //得到图片 Graphics2D g = (Graphics2D) image.getGraphics();//笔 //设置图片的背景颜色 g.setColor(Color.WHITE); g.fillRect(0, 0, 80, 20); //给图片写数据 g.setColor(Color.BLUE); g.setFont(new Font(null, Font.BOLD, 20)); g.drawString(makeNum(), 0, 20); //告诉浏览器,这个请求用图片的方式打开 resp.setContentType("image/png"); resp.setDateHeader("expires", -1); resp.setHeader("Cache-Control", "no-cache"); resp.setHeader("Pragma", "no-chche"); //把图片写给浏览器 ImageIO.write( image, "jpg", resp.getOutputStream()); } //生成随机数 private String makeNum(){ Random random = new Random(); String num = random.nextInt(9999999) + ""; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 7 - num.length(); i++) { stringBuffer.append("0"); } num = stringBuffer.toString() + num; return num; } }
<servlet> <servlet-name>imageservlet</servlet-name> <servlet-class>ImageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>imageservlet</servlet-name> <url-pattern>/image</url-pattern> </servlet-mapping>
4 实现重定向
B一个web资源收到客户端A请求后,B通知A去访问另一个web资源C,这个过程叫重定向。
方式1 response.setStatus(302); response.setHeader("location", "/s3/image"); 方式2 response.sendRedirect("/s3/image");
5.7 HttpServletRequest
HTTP请求中的所有信息会被封装到HTtpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息。
用到jsp,则需要在父项目pom文件导入jsp包。
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.2.1</version> <scope>provided</scope> </dependency>
1.获取前端传递的参数+请求转发
主要用的方法
#index.jsp
<form action="/s4/login" method="post"> 用户名:<input type="text" name="username" required> <br> 密码:<input type="password" name="passwd" required> <br> 爱好: <input type="checkbox" name="hobbys" value="女孩">女孩 <input type="checkbox" name="hobbys" value="代码">代码 <input type="checkbox" name="hobbys" value="唱歌">唱歌 <input type="checkbox" name="hobbys" value="电影">电影 <br> <input type="submit"> </form>
#success.jsp 验证请求转发所用
登录成功!
#LoginServlet.java
public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); String username = req.getParameter("username"); String passwd = req.getParameter("passwd"); String[] hobbys = req.getParameterValues("hobbys"); System.out.println(username); System.out.println(passwd); System.out.println(hobbys.toString()); System.out.println(req.getContextPath()); //通过请求转发 req.getRequestDispatcher("/success.jsp").forward(req, resp); } }
#web.xml
<servlet> <servlet-name>loginservlet</servlet-name> <servlet-class>LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginservlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
登录index.jsp提交表单
5.8 Cookie
cookie:
-
一般会保存在本地的用户目录下appData下。
-
由浏览器保存,不同的浏览器具有不兼容性。
-
由浏览器主动向服务器提供,服务端只需getCookies就行。
细节:
-
一个cookie只能保存一个信息
-
一个web站点最多存放20个cookie
-
cookie大小限制4kb
-
300个cookie浏览器上限
-
不设置有效期,关闭浏览器自动失效。
-
控制台输入javascript:alert (document. cookie)可查看本网站下cookie
服务器相应给客户端cookie:
Cookie[] cookies = req.getCookies();//获得cookie
cookie.getName();//获得cookie中的name
cookie.getValue();//获得cookie中的value
new Cookie("lastLoginTime", System.currentTimeMillis()+"");//新建一个cookie
cookie.setMaxAge(0);//设置cookie有效期。为0即删掉
resp.addCookie(cookie);//响应给客户端一个cookie
#CookieDemo01.java
//保存用户上一次访问的时间 public class CookieDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); PrintWriter writer = resp.getWriter(); //cookie是从客户端获取 Cookie[] cookies = req.getCookies(); if (cookies != null){ writer.write("您上一次访问的时间:"); for (int i = 0; i < cookies.length; i++) { //获取cookie名字 Cookie cookie = cookies[i]; if(cookie.getName().equals("lastLoginTime")){ //获取cookie的值 long ltime = Long.parseLong(cookie.getValue()); Date ddate = new Date(ltime); writer.write(ddate.toLocaleString()); } } }else{ writer.write("这是您第一次访问本站!"); } //服务给客户端响应一个cookie Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+""); //cookie有效期 cookie.setMaxAge(24*60*60); resp.addCookie(cookie); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
#web.xml
<servlet> <servlet-name>cookieDemo01</servlet-name> <servlet-class>CookieDemo01</servlet-class> </servlet> <servlet-mapping> <servlet-name>cookieDemo01</servlet-name> <url-pattern>/lastlogintime</url-pattern> </servlet-mapping>
5.9 Session(重点)
-
服务器会给每一个用户(浏览器)创建一个Session对象;
-
一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
-
用户登录之后,整个网站都可以访问。
场景:
-
保存一个登录用户的信息
-
购物车信息
-
整个网站中经常会使用的数据
-
1. 设置一个session
-
set后session机制自动返回给用户一个cookie:sessionID
-
session还可保存对象等数据
#SessionDemo01.java
public class SessionDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); //得到Session HttpSession session = req.getSession(); //给Session存东西 session.setAttribute("name", "xiaoyang"); //获取Session的ID String id = session.getId(); //判断Session是不是新创建 if(session.isNew()){ resp.getWriter().write("session第一次创建成功 " + id); }else{ resp.getWriter().write("session已经在服务器中存在 "+id); } //Session创建的时候做了什么事情 /* Cookie jsessionid = new Cookie("JSESSIONID", id); resp.addCookie(jsessionid); */ } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
<servlet-mapping>/setsession</servlet-mapping>
2. 同一站点共享session
#GetSession.java
public class GetSession extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html;chareset=utf-8"); HttpSession session = req.getSession(); String name = (String) session.getAttribute("name"); System.out.println(name); } }
<servlet-mapping>/getsession</servlet-mapping>
#Servlet里getServletContext对象也可存储共享的数据。但一般用session。参见5.5
3. 注销session
1.关闭浏览器
2.手动注销
HttpSession session = req.getSession();
session.removeAttribute("name");
session.invalidate();
3.设置注销时间(web.xml里)
<session-config> <session-timeout>1</session-timeout>//一分钟后注销 </session-config>
4.补充两个会话机制
场景:客户端A和客户端B想公用一个servlet资源(之前说的是一个客户端共享多个servlet资源),就需要引进不同作用域的会话机制。ServletContext/applicationContext。
06 JSP
JSP页面可以嵌入java代码,为用户提供动态数据;
6.1 原理:
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问servlet。JSP本质上就是一个Servlet。
JSP转变成servlet。路径:
C:\Users\kenshin.IntelliJIdea2018.2\system\tomcat\Unnamed_javaweb-servlet\work\Catalina\localhost\s5\org\apache\jsp
分析原理
【RUN】后生成工作空间,访问index.jsp和hello.jsp
打开hello_jsp.java
HttpJspBase继承HttpServlet HttpServlet继承Servlet,所以最后JSP.java实现的是Servlet。
初始化/注销
熟悉不?Servlet里写入html元素。
以下这些对象都可以在JSP页面中直接使用。
大致原理图
6.2 JSP基础语法
九大内置对象
out out对象的API
request HttpServletRequest(作用域对象1)
response HttpServletResponse
config Servletconfig
session HttpSession(作用域对象2)
//在page指令中配置如下信息,session将不可用 <%@page session="false" %>
application ServletContext (作用域对象3)
page HttpJasPage相当于普通java类的this(作用域对象4)
exception Java.lang.Exception类的对象
//设置错误页面 index.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %> error.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="index.jsp" isErrorPage="true" %>
pageContext 代表JSP编译后的内容(也就是JSP页面的运行环境)。理所应当可以获得其他八个内置对象.(作用域对象5,算是吧)
四(五)个作用域对象都有方法:
setAttribute(string name,Object o)
getAttribute(string name)
removeAttribute(string name)
着重讲下pageContext
1 范围参数
pageContext多了一个指定范围的参数,不指定则默认page范围;范围参数有.APPLICATION_SCOPE,SESSION_SCOPE,REQUEST_SCOPE,PAGE_SCOPE。
#page1
#page2
2 findAttribute
pageContext多了一个方法:findAttribute(string name)。该方法会从小到大访问各个域page->application,拿到name的值。
3 引入和跳转
pageContext.forward(String url) 替代RequestDispatch.forward
pageContext.include(String url) 替代include
四种属性范围
上面已经提到
范围
场景
-
request:如果客户向服务器发请求,产生的数据,用户看完就没用了,像这样的数据就存在request域,像新闻数据,属于用户看完就没用的
-
session:如果客户向服务器发请求,产生的数据,用户用完了等一会儿还有用,像这样的数据就存在session域中,像购物数据,用户需要看到自己购物信息,并且等一会儿,还要用这个购物数据结帐
-
servletContext:如果客户向服务器发请求,产生的数据,用户用完了,还要给其它用户用,像这样的数据就存在servletContext域中,像聊天数据
6.3 JavaBean
1. 什么是JavaBean
JavaBean说白了就是一个普通的Java类,也称之为简单java对象——POJO(Plain ordinary Java Object),是Java设计中的一种设计模式,是一种基于Java平台的软件组件思想。
类名+setter/getter/tostring
2. 为什么要使用JavaBean
封装,重用,可读。
一般用来和数据库的字段做映射 ORM(对象关系映射 表->类 字段->属性 行记录->对象)。
3. JSP行为--JavaBean
JSP技术提供了三个关于JavaBean组件的动作元素,即JSP行为(标签),它们分别为:
jsp:useBean【在JSP页面中查找javaBean对象或者实例化javaBean对象】
jsp:setProperty【设置javaBean的属性】
jsp:getProperty【获取javaBean的属性】
<jsp:useBean id="person" class="pojo.Person" scope="page"/> <jsp:setProperty name="person" property="name" value="xiaoyang" /> <jsp:setProperty name="person" property="id" value="1"/> <jsp:setProperty name="person" property="age" value="18"/> 姓名:<jsp:getProperty name="person" property="name"/> id: <jsp:getProperty name="person" property="id"/> 年龄:<jsp:getProperty name="person" property="age"/>
<%--获取从另一个表单传递过来的值--%> <jsp:useBean id="person" class="pojo.Person" scope="page"/> <jsp:setProperty name="person" property="age">//根据JavaBean属性名(or表单属性名)获取值 <%--(注意JavaBean属性名要和表单属性名一致)--%> <jsp:setProperty name="person" property="*">//按照类型自动匹配
6.4 EL表达式
1. 什么是EL表达式
EL(Expression Language,表达式语言),EL表达式是用${}括起来的脚本,用来更方便读取对象中的属性,提交的参数,JavaBean,甚至集合!。
EL表达式如果找不到相应的对象属性,返回空白字符串“”而不是null,这是EL表达式最大的特点。
EL依赖
<!-- https://mvnrepository.com/artifact/javax.el/javax.el-api --> <dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>3.0.0</version> </dependency>
2. EL表达式的作用
获取各类数据
1.获取域对象的数据
//1.jsp <% application.setAttribute("name", "xiaoyang");//其他域也一样。 %>
//2.jsp
${name}
之前讲到ServletContext时讲到findAttribute(String name),EL表达式在执行时会调用该方法。这也解释了为什么EL表达式仅仅通过标识符就可以获取到域对象的数据。
查找顺序:从小到大:page > request > session > application。
2.获取JavaBean的属性
${person.age}//等同于person.getAge()
内部通过反射机制完成!
3.获取集合的数据
集合在开发中广泛被使用,EL很好支持了集合操作。可以非常方便地读取Collection和Map集合地内容
如果有一个List,元素是Person //不使用EL <% List<Person> list = (list) session.getAttribute("list"); out.write(list.get(0).getUsername()+"<br>"); ... out.write(list.get(n).getUsername()); %> //使用EL %{list[0].username} ... %{list[n].username}
如果有一个Map,关键字是数字{1,2,..},元素是Person
//使用EL
${map["1"].username}
...
${map["n"].username}
*如果关键字是字符串{"aa","bb",...}
则应使用这种方式:${map.aa.username}
EL表达式配合JSTL标签可以很方便的迭代集合!
3. EL运算符
EL支持简单的运算:加减乘除取模,关系运算符,逻辑运算符,empty运算符,三母运算符。
关系运算符 逻辑运算符 == 或 eq && 或 and != ne || or < lt ! not > gt <= le >= ge
三元运算符
${list==null ? "list为空" : "list不为空"}
4. EL表达式11个内置对象
EL可以输出的东西都在11个内置对象中,11个内置对象,其中10个是Map,只有pageContext不是Map,它就是PageContext类型,EL所有内置对象如下:
-
pageScope page域中的属性,相当于pageContext.getAttribute("name")
-
requestScope request域中的属性
-
sessionScope session域中的属性
-
applicationScope application域中的属性
-
param 所有请求参数 ,适用于单值的参数,相当于request.getParameter("name")
-
paramValues 所有请求参数 ,适用于多值的参数。
-
header 所有http请求头字段,相当于request.getHeader("xxx")
-
headerValues 同上,返回String[]
-
cookie cookie,例如${cookie.JSESSIONID.value}就是获取sessionId
-
initParam 获取web.xml中<context-param>内的参数
-
pageContext 可以获取JSP九大内置对象。例如pageContext.getRequest()可以写为${pageContext.request}
-
注意事项:
-
headerValues,如果头里有"-",例如Accept-Encoding,则要headerValues["Accept-Encoding"];
-
cookie,${cookie.key}取的是cookie对象,访问cookie名称和值${cookie.key.name}和${cookie.key.value}
5.回显数据
概念:
在数据提交出现错误的时候,已填写的信息仍在文本框中,比如用户登录,当用户输入错误的密码后,用户名仍在文本框,只是密码框清空。
意义:
对于一些要填写很多信息的表单,如果因为一些错误导致已经填写的整个表单信息重新填写,这对用户非常不友好。
EL表达式最大的特点就是:如果获取到的数据为null,输出空字符串"",利用这个特点我们可以实现数据回显:
//注册失败后,返回注册页面,之前填写的内容回显的功能!
<% Person person = new Person(); person.setName("xiaoyang"); request.setAttribute("name", person); %> <input type="radio" name="name" value="xiaoyang" ${person.name=='xiaoyang'?'checked':''}>xiaoyang <input type="radio" name="name" value="xiaosun" ${person.name=='xiaosun'?'checked':''}>xiaosun
6.EL自定义方法
用于拓展EL表达式的功能,可以让EL表达式完成普通Java程序代码能完成的功能。
比如:开发HTML转义的EL方法—》有时候想在JSP页面中输出JSP代码,但JSP引擎会自动把HTML代码解析输出给浏览器。
步骤1:创建自定义EL方法的实现类
(方法必须是静态的,因为EL表达式只能调用静态方法)
package impl; public class ShowJSP { public static String filter(String message){ if(message == null) return null; //转储message char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuilder result = new StringBuilder(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]){ case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return result.toString(); } }
步骤2:注册自定义EL函数
1.在WEB-INF目录下创建自定义tld文件:例如myelfuns.tld
2.绑定约束文件
<?xml version="1.0" encoding="ISO-8859-1"?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <!--标签库的版本号--> <tlib-version>1.0</tlib-version> <!--taglib指令中prefix需要指定的部分,任意,建议和文件名一致,方便管理。--> <short-name>myshortname</short-name> <!--taglib指令中URI需要指定的部分,任意。即便和JSP里要求的uri不一致也不影响--> <uri>http://mycompany.com</uri> <!--↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 主要配置 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓--> <function> <!--函数名字--> <name>filter</name> <!--函数位置--> <function-class>impl.ShowJSP</function-class> <!--函数的方法声明--> <function-signature>java.lang.String filter(java.lang.String)</function-signature> </function> </taglib>
步骤3:JSP里导入和使用
EL自定义函数前缀一般为“fn”,uri是“/WEB-INF/tld文件名称” —》myelfuns.tld
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib prefix="fn" uri="/WEB-INF/myelfuns.tld" %> <html> <head> <title></title> </head> <body> <%--完成了HTML转义功能--%> ${fn:filter("<a href='#'>点我,大概没用</a>")} </body> </html>
7. EL函数库(fn方法库)
在JSP页面中显示数据时,经常需要对字符串进行处理,于是SUN公司定义了一套EL函数库。可以认为fn方法库就是String的方法。
EL函数库(fn方法库),是JSTL标签库中的一个库,但是该库长得不像标签,所以称之fn方法库。
导入jstl和standard包。
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl --> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/taglibs/standard --> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency>
在JSP页面中指明使用标签库
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
fn:contains(string, substring) 如果参数string中包含参数substring,返回true
fn:containsIgnoreCase(string, substring) 如果参数string中包含参数substring(忽略大小写),返回true
fn:endsWith(string, suffix) 如果参数 string 以参数suffix结尾,返回true
fn:escapeXml(string) 将有特殊意义的XML (和HTML)转换为对应的XML character entity code,并返回
fn:indexOf(string, substring) 返回参数substring在参数string中第一次出现的位置
fn:join(array, separator) 将一个给定的数组array用给定的间隔符separator串在一起,组成一个新的字符串并返回。
fn:length(item) 返回参数item中包含元素的数量。参数Item类型是数组、collection或者String。如果是String类型,返回值是String中的字符数。
fn:replace(string, before, after) 返回一个String对象。用参数after字符串替换参数string中所有出现参数before字符串的地方,并返回替换后的结果
fn:split(string, separator) 返回一个数组,以参数separator 为分割符分割参数string,分割后的每一部分就是数组的一个元素
fn:startsWith(string, prefix) 如果参数string以参数prefix开头,返回true
fn:substring(string, begin, end) 返回参数string部分字符串, 从参数begin开始到参数end位置,包括end位置的字符
fn:substringAfter(string, substring) 返回参数substring在参数string中后面的那一部分字符串
fn:substringBefore(string, substring) 返回参数substring在参数string中前面的那一部分字符串
fn:toLowerCase(string) 将参数string所有的字符变为小写,并将其返回
fn:toUpperCase(string) 将参数string所有的字符变为大写,并将其返回
fn:trim(string) 去除参数string 首尾的空格,并将其返回
8. 注意:
要使用EL则必须在JSP里添加<%@page isELIgnored="false" %>
因为默认为true。
6.5 JSTL
1.什么是JSTL
JSTL(JSP Standard Tag Library,JSP标准标签库)
实现了基本的功能:集合的遍历,数据的输出,字符串的处理,数据的格式化,逻辑控制等等。
2.core标签库
core标签库是JSTL的核心标签库,实现了最基本的功能:流程控制,迭代输出等操作!
core标签库的前缀一般是c
在JSP中指明使用core标签库
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
1 c:cout
<c:out value="<string>" default="<string>" escapeXml="<true|false>"/> value是必要的。escapeXml默认为true
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <title>c:out 标签实例</title> </head> <body> <h1><c:out> 实例</h1> <% session.setAttribute("name", "<c:out> 实例"); %> <c:out value="${name}" escapeXml="true" default="默认值"></c:out><br/> <c:out value="${name}" escapeXml="false" default="默认值"></c:out><br/> <c:out value="${name}" escapeXml="false">使用的表达式结果为true,则输出该值</c:out><br/> <c:out value="${null}" escapeXml="false">使用的表达式结果为null,则输出默认值</c:out><br/>
2 c:set
一般用于设置 JavaBean 对象或 java.util.Map 对象的值。
<c:set var="<string>" value="<string>" target="<string>" property="<string>" scope="<string>"/> 都不是必要,scope默认值为page。
<c:set var="salary" scope="session" value="${2000*2}"/> <c:out value="${salary}"/>
3 c:remove
移除一个变量,可以指定这个变量的作用域,若未指定,则默认为变量第一次出现的作用域。
<c:remove var="<string>" scope="<string>"/> var必要,scope默认所有作用域。
<c:set var="salary" scope="session" value="${2000*2}"/> <p>salary 变量值: <c:out value="${salary}"/></p> <c:remove var="salary"/> <p>删除 salary 变量后的值: <c:out value="${salary}"/></p>
4 c:catch
处理产生的异常情况,并且将错误信息储存起来。
<c:catch var="<string>"> ... </c:catch> 都不必要,默认值None。
<c:catch var ="catchException"> <% int x = 5/0;%> </c:catch> <c:if test = "${catchException != null}"> <p>异常为 : ${catchException} <br /> 发生了异常: ${catchException.message}</p> </c:if>
报错500
添加包依赖
jstl-api jstl-impl
5 c:ifx
<c:if test="<boolean>" var="<string>" scope="<string>"> ... </c:if> test必要,scope默认值为page。
<c:set var="salary" scope="session" value="${2000*2}"/> <c:if test="${salary > 2000}"> <p>我的工资为: <c:out value="${salary}"/><p> </c:if>
6 c:choose/c:when/c:otherwise
switch语句中有case,而c:choose标签中对应有c:when,switch语句中有default,而c:choose标签中有c:otherwise。
<c:choose> <c:when test="<boolean>"> ... </c:when> <c:when test="<boolean>"> ... </c:when> ... ... <c:otherwise> ... </c:otherwise> </c:choose> c:choose和c:otherwise没有属性。 c:when:test必要。
<c:set var="salary" scope="session" value="${2000*2}"/> <p>你的工资为 : <c:out value="${salary}"/></p> <c:choose> <c:when test="${salary <= 0}"> 太惨了。 </c:when> <c:when test="${salary > 1000}"> 不错的薪水,还能生活。 </c:when> <c:otherwise> 什么都没有。 </c:otherwise> </c:choose>
7 c:import
提供了所有jsp:include行为标签所具有的功能,同时也允许包含绝对URL。
举例来说,使用c:import标签可以包含一个FTP服务器中不同的网页内容。
<c:import url="<string>" var="<string>" scope="<string>" varReader="<string>" context="<string>" charEncoding="<string>"/> url必要,context默认值为当前应用程序,charEncoding默认值为ISO--8869-1,scope默认值为page。
url:: 待导入资源的URL,可以是相对路径和绝对路径,并且可以导入其他主机资源 context: 当使用相对路径访问外部context资源时,context指定了这个资源的名字。 charEncoding: 所引入的数据的字符编码集 var: 用于存储所引入的文本的变量 scope: var属性的作用域 varReader: 可选的用于提供java.io.Reader对象的变量
<c:import var="data" url="http://www.runoob.com"/> <c:out value="${data}"/>
以上程序将会打印http://www.runoob.com"页面的源代码。
8 c:forEach
迭代一个集合中的对象。
<c:forEach items="<object>" begin="<int>" end="<int>" step="<int>" var="<string>" varStatus="<string>"> ... 都没必要,begin默认值0,end默认值最后一个元素,step默认1。
<c:forEach var="i" begin="1" end="5"> Item <c:out value="${i}"/> </c:forEach>
9 c:forTokens
指定分隔符,将字符串分隔成一个数组然后迭代。
<c:forTokens items="<string>" delims="<string>" begin="<int>" end="<int>" step="<int>" var="<string>" varStatus="<string>"> delims必要。begin默认值0,end默认值最后一个元素,step默认值1。
<c:forTokens items="google,runoob,taobao" delims="," var="name"> <c:out value="${name}"/><br/> </c:forTokens>
10 c:param
用于在c:url标签中指定参数,而且与URL编码相关。
<c:param name="<string>" value="<string>"/> name必要。value默认值Body。
<c:url var="myURL" value="index.jsp"> <c:param name="name" value="xiaoyang"/> <c:param name="password" value="123"/> </c:url> <c:out value="${myURL}"/>
11 c:redirect
重定向至一个新的URL,它提供内容相关的URL,并且支持c:param标签。
<c:redirect url="<string>" context="<string>"/> url必要,context默认值当前应用程序。
<c:redirect url="http://www.runoob.com"/>
6.6 传统自定义标签
6.7 简单自定义标签
6.8 JSP面试题
07 MVC三层架构
7.1 早些年
servlet--CRUD-->数据库
弊端:程序十分臃肿,不利于维护
servlet的代码中:处理请求,响应,视图跳转,处理JDBC,处理业务代码,处理逻辑代码
架构:没有什么是加一层解决不了的!
程序员调用
|
JDBC
|
Mysql Oracle sqlServer
7.2 MVC三层架构
Model
-
业务处理:业务逻辑(Service)
-
数据持久层:CRUD(Dao)
View
-
展示数据
-
提供链接发起Servlet请求(a,form,img...)
Controller
-
接收用户的请求:(req:请求参数,Session信息...)
-
交给业务层处理对应的代码
-
控制视图的跳转
-
登录-->请求用户的登录请求-->处理用户的请求(获取用户登录的参数)-->交给业务层处理登录业务(判断用户名密码是否正确:事务)-->Dao层查询用户名和密码是否正确 -->数据库 再一层层返回
08 过滤器Filter(重)
8.1 什么是Filter
在web开发过程中,为了实现某些特殊功能,经常需要对请求和响应消息进行处理。例如,记录用户访问信息,统计页面访问次数,验证用户身份等。Filter作为Servlet2.3中新增的技术,可以实现用户在访问某个目标资源之前,对访问的请求和响应进行相关处理;Filter被称作为过滤器或者拦截器,其基本功能就是对Servlet容器调用Servlet的过程进行拦截,从而在Servlet进行响应处理前后实现一些特殊功能。这就好比现实中的污水净化设备,它可以看作一个过滤器,专门用于过滤污水杂志。
导入mysql包
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.39</version> </dependency>
8.1 Filter开发步骤
1.编写Filter的实现类
*注意存在很多Filter,导包正确->servlet-api
#CharacterEncodingFilter.java 过滤乱码
public class CharacterEncodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { //web服务器启动就已经初始化了,随时等待过滤对象出现! } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding("utf-8"); servletResponse.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/html;charset=UTF-8"); System.out.println("CharacterEncodingFilter执行前...."); //让我们的请求继续走,如果不写,程序到此截止。 filterChain.doFilter(servletRequest, servletResponse); System.out.println("CharacterEncodingFilter执行后..."); } @Override public void destroy() { } }
2.编写测试类
public class ShowServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //如果没有Filter,一定会出现乱码 resp.getWriter().write("您好呀,world!"); } }
3.在web.xml中配置Filter
<servlet> <servlet-name>showservlet</servlet-name> <servlet-class>ShowServlet</servlet-class> </servlet> <!--这样就没过滤器的支持--> <servlet-mapping> <servlet-name>showservlet</servlet-name> <url-pattern>/showservlet</url-pattern> </servlet-mapping> <!--这样就有过滤器的支持--> <servlet-mapping> <servlet-name>showservlet</servlet-name> <url-pattern>/show/showservlet</url-pattern> </servlet-mapping> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> 只要是/show/路径下的任何请求,都会经过这个过滤器 <url-pattern>/show/*</url-pattern> </filter-mapping>
没走过滤器(/showservlet)
走过滤器(/show/showservlet)
09 监听器listener
9.1 什么是监听器
GUI图形化编程里经常用。
9.2 步骤
以统计网站在线人数为例,通过监听统计session可做到。
1.编写Listenner监听类
#OnlineCountListener.java
//统计网站在线人数:统计session public class OnlineCountListener implements HttpSessionListener { //创建session监听 //一旦创建session就会触发一次这个事件 @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { ServletContext ctx = httpSessionEvent.getSession().getServletContext(); Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount"); if(onlineCount == null){ onlineCount = new Integer(1); }else{ int count = onlineCount.intValue(); onlineCount = new Integer(count + 1); } ctx.setAttribute("OnlineCount", onlineCount); System.out.println("sessionCreated233333333"); } //销毁session监听 //一旦销毁session就会触发一次这个事件 //销毁session的方式:参见5 -》 5.9 -》 3 主动退出浏览器并不会注销session。考虑其他两种方式,手动注销。或者设置session过期时间。 @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { ServletContext ctx = httpSessionEvent.getSession().getServletContext(); Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount"); if(onlineCount == null){ onlineCount = new Integer(0); }else{ int count = onlineCount.intValue(); onlineCount = new Integer(count - 1); } ctx.setAttribute("OnlineCount", onlineCount); System.out.println("sessionDestroyed2333333333"); } }
2.在web.xml中配置listner
<listener> <listener-class>OnlineCountListener</listener-class> </listener>
3.编写测试页面
# index.jsp 统计页面 <%@page isELIgnored="false" %> 当前 ${applicationScope.OnlineCount} 人在线 # destroy.jsp 主动销毁session <% session.invalidate(); %>
4.测试
[redeploy] 登录0次
[restart] 登录2次
[另一个浏览器页面打开+1次]
补充:
10 JDBC及事务
JDBC(Java Database Connection,Java 连接数据库)
-
注意Mysql版本8必须导入响应的版本驱动
1. 简单增删改查
#数据库
create database jdbc; use jdbc; create table users( id int primary key , `name` varchar(40), `password` varchar(40), email varchar(60), birthday date ); insert into users(users.id,users.`name`,users.`password`,users.email,users.birthday) values (1,'张三','123456','zs@qq.com','2000-01-01'); insert into users(users.id,users.`name`,users.`password`,users.email,users.birthday) values (2,'李四','123456','ls@qq.com','2000-01-01'); insert into users(users.id,users.`name`,users.`password`,users.email,users.birthday) values (3,'王五','123456','ww@qq.com','2000-01-01');
驱动:com.mysql.cj.jdbc.Driver
URL:jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
查
import java.sql.*; public class TestJDBC { public static void main(String[] args) throws ClassNotFoundException, SQLException { String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8"; String username = "root"; String password = "root"; //1.加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2.连接数据库 Connection connection = DriverManager.getConnection(url, username, password); //3.向数据库发送SQL的对象statement:CRUD Statement statement = connection.createStatement(); //4.编写Sql String sql = "select * from users"; //5.执行sql。返回一个结果集 ResultSet resultSet = statement.executeQuery(sql); while(resultSet.next()){ System.out.println(resultSet.getObject("id")); System.out.println(resultSet.getObject("name")); System.out.println(resultSet.getObject("password")); System.out.println(resultSet.getObject("email")); System.out.println(resultSet.getObject("birthday")); } //6.关闭连接,先开的后关 resultSet.close(); statement.close(); connection.close(); } }
增(采用预编译的方式)
public class TestJDBC { public static void main(String[] args) throws ClassNotFoundException, SQLException { String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8"; String username = "root"; String password = "root"; //1.加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //2.连接数据库 Connection connection = DriverManager.getConnection(url, username, password); //3.编写Sql String sql = "insert into users(id,name,password,email,birthday) value(?,?,?,?,?);"; //4.预编译 PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1, 4); preparedStatement.setString(2, "xiaoyang"); preparedStatement.setString(3, "123456"); preparedStatement.setString(4, "xy@qq.com"); preparedStatement.setDate(5, new Date(new java.util.Date().getTime())); //5.执行sql int i = preparedStatement.executeUpdate(); if(i > 0){ System.out.println("插入成功【" + i + "】条数据"); } //6.关闭连接 preparedStatement.close(); connection.close(); } }
2.事务
要么都成功,要么都失败。
ACID原则:保证数据安全。
题外话:junit.jar:@Test注解保证不需要main方法,在哪都可以测试方法。
开启事务 事务提交 commit() 事务回滚 rollback() 关闭事务 转账: A:1000 B:1000 A(900) --100--> B(1100)