JavaWeb
1、编写HelloServlet程序
1.1、新建普通的Maven项目
在项目中,删掉里面的src目录,这个空项目就是Maven主工程
1.2、新建Mavenweb项目
在新建中的web项目的[子项目名→src→main]目录下新建java和resources
1.3、Maven环境优化
修改web.xml为最新(去tomcat文件夹下的webapps里面的ROOT→WEB-INF→web.xml复制)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
//版本用最新的4.0
</web-app>
1.4、依赖
在父项目的pom.xml中添加依赖<dependencies>其中写各个依赖</dependencies>
其中各个依赖的<scope>provided</scope>
可以先删除
<dependencies>
<!--添加Servlet和JSP依赖-->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
1.5、输出Hello
-
编写一个普通类
-
实现Servlet接口,这里我们直接继承HttpServlet(
HttpServlet实现了Servlet接口
) -
重写doGet和doPost方法
-
在web.xml中编写映射
<!--注册servlet--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.ljh.servlet.HelloServlet</servlet-class> </servlet> <!--servlet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/path</url-pattern> </servlet-mapping>
-
配置Tomcat:注意配置项目发布路径即可。
-
启动项目
2、HttpServlet知识点
2.1、Header信息头
MIME信息头比较常用的两个:
Content-Type:(常用,该实体头的作用是让服务器告诉浏览器它发送的数据属于什么文件类型)
Content-Disposition: (常用,当Content-Type的类型为要下载的类型时,这个信息头会告诉浏览器这个文件的名字和类型)
//头部信息设置如下
response.addHeader("content-type","image/png");
response.addHeader("Content-Disposition", "attachment;filename="+filename);
2.2、下载图片
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 要获取下载文件的路径
String filePath = "F:\IDEA\projects\study\javaweb-01-servlet\response\src\main\resources\2.jpg";
// 2. 下载的文件名
//下面代码的意思是从filePath最后的\的后一个位置开始截取,一直截取到最后,结果为2.jpg
String fileName = filePath.substring(filePath.lastIndexOf("\\") + 1);
// 3. 设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码
resp.setHeader("Content-Disposition","attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
// 4. 获取下载文件的输入流
FileInputStream fis = new FileInputStream(filePath);//将文件路径传入
// 5. 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
// 6. 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端!
while ((len=fis.read(buffer))>0){
out.write(buffer,0,len);
}
fis.close();
out.close();
}
2.3、生成随机数
//方法一
public static void main(String[] args) {
final int CON_LENGTH = 5;//设置随机数的个数
String str = "ABCDEFGHIGKLMNOPQRSTUVWXYZ";
str += str.toLowerCase();
str += "0123456789";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < CON_LENGTH; i++) {
Random ra = new Random();
int num = ra.nextInt(str.length());
char c = str.charAt(num);
sb.append(c);
}
System.out.println(sb);//这里的sb即为5位随机数,随机数包括大小写字母及数字
}
//方法二
private String makeNum(){
Random random = new Random();
String num = random.nextInt(9999999) + "";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 7-num.length() ; i++) {
sb.append("0");
}
num = sb.toString() + num;
return num;//返回7位随机数字
}
String、StringBuffer(更安全)和StringBuilder(性能高)的区别可见链接: https://blog.csdn.net/csxypr/article/details/92378336
2.4、重定向
面试题:请你聊聊重定向和转发的区别?
相同点
- 页面都会实现跳转
不同点
- 请求转发的时候,url不会产生变化 (307)
- 重定向时候,url地址栏会发生变化 (302)
resp.sendRedirect("/r0/success.jsp");//重定向
req.getRequestDispatcher("/success.jsp").forward(req,resp);//请求转发
典型的应用场景:
-
forward: 访问 Servlet 处理业务逻辑,然后 forward 到 jsp 显示处理结果,浏览器里 URL 不变
-
redirect: 提交表单,处理成功后 redirect 到另一个 jsp,防止表单重复提交,浏览器里 URL 变了
2.5、登录
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<h2>Hello World!</h2>
<%--${pageContext.request.contextPath}代表当前的项目--%>
<form action="${pageContext.request.contextPath}/login" method="get">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><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" value="提交">
</form>
</body>
</html>
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取name为username的值
//getParameter获取前端传递的单个值
//getParameterValues获取一个数组
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println(username+":"+password);
System.out.println(Arrays.toString(hobbys));
resp.sendRedirect("/r0/success.jsp");//重定向
//通过请求转发
//这里的 / 代表当前的web应用
//req.getRequestDispatcher("/success.jsp").forward(req,resp);
//重定向还需要加当前项目路径
}
3、Cookie
Cookie[] cookies = req.getCookies();//获取cookie数组,可以for遍历得到cookie
String name = cookie.getName();//单个cookie的名字(key)
String value = cookie.getValue();//单个cookie的值(value)
Cookie cookie = new Cookie("username", username);//创建cookie
cookie.setMaxAge(24*60*60)//设置cookie有效期
resp.addCookie(cookie);//响应给客户端一个cookie
//resp.getWriter().write返回前端中文乱码问题,在前面添加下面的代码
resp.setHeader("Content-type", "text/html;charset=UTF-8");
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
Cookie中编码、解码问题:
- URLEncoder.encode:编码
- URLDecoder.decode:解码
Cookie cookie = new Cookie("lastLoginDate", URLEncoder.encode(new Date().toLocaleString(),"utf-8"));//这里是编码
resp.getWriter().write(URLDecoder.decode("最后登录:" + cookie.getValue(),"utf-8"));//这里是解码
删除Cookie:
- 不设置有效期,关闭浏览器
- 设置setMaxAge为0
4、Session
HttpSession session = req.getSession();//获取session
session.getId()//这个是session的唯一ID
class Student{
String name;
int age;
String intro;
Student(String name, int age, String intro) {
this.name = name;
this.age = age;
this.intro = intro;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", intro='" + intro + '\'' +
'}';
}
}
//下面设置一个session,名字为name,值为一个Student对象
session.setAttribute("name",new Student("小明",25,"我是一个IT农民工"));
//下面是获取session中名字为name的值,这里的值是一个对象
Object obj = session.getAttribute("name");
session.invalidate();//注销session,再次访问会自动生成一个另外的SessionID
session还可以在web.xml中配置
<session-config>
<!--30分钟后Session自动失效,以分钟为单位-->
<session-timeout>30</session-timeout>
</session-config>
5、JSP
内置对象
- PageContext 存东西
- Request 存东西
- Response
- Session 存东西
- Application 【SerlvetContext】 存东西
- config 【SerlvetConfig】
- out
- page ,不用了解
- exception
pageContext.setAttribute("name1","长江1号"); //保存的数据只在一个页面中有效
request.setAttribute("name2","长江2号"); //保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","长江3号"); //保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","长江4号"); //保存的数据只在服务器中有效,从打开服务器到关闭服务器
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!
session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车;
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数据;
JSTL标签库
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入JSTL库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--EL表达式获取表单中的数据:${param.参数名}--%>
<%--不加上的话,${param.参数名}解析不出来--%>
<%@ page isELIgnored="false"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>测试jstl</h2>
<form action="JspDemo01.jsp" method="get">
用户名:
<label>
<input type="text" name="username"/>
</label><br>
<input type="submit" value="登录">
</form>
<%
ArrayList<String> obj = new ArrayList<>();
obj.add("小明");
obj.add("小华");
obj.add("小李");
obj.add("小孙");
obj.add("小浩");
request.setAttribute("stu",obj);
%>
c:foreach中的属性
var 每一次遍历出来的变量
items 要遍历的对象
begin 哪里开始(0=第一个元素,1=第二个元素)
end 到哪里(0=第一个元素,1=第二个元素)
step 每一次迭代的步长
<c:forEach var="per" items="${stu}">
<c:out value="${per}"/>
</c:forEach>
<%--判断${param.username == 'admin'},把结果放入isAdmin中--%>
<c:if test="${param.username == 'admin'}" var="isAdmin">
<c:out value="管理员接口测试完毕"/>
</c:if>
<c:out value="${isAdmin}"/>
</body>
</html>
6、Filter
-
导包
public class FilterFormatWebPage implements Filter
这里的Filter需要导的包是javax.servlet的Filter
-
重写Filter接口的方法:init、doFilter、destroy
其中,doFilter方法
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("Filter进行之前"); /*必须要写filterChain.doFilter,filterChain.doFilter将请求转发给过滤器链下一个filter, 如果没有filter那就是你请求的资源*/ filterChain.doFilter(servletRequest,servletResponse); System.out.println("Filter进行之后"); }
-
在web.xml中配置 Filter
<filter> <filter-name>FilterFormatWebPage</filter-name> <filter-class>com.ljh.filter.FilterFormatWebPage</filter-class> </filter> <filter-mapping> <filter-name>FilterFormatWebPage</filter-name> <url-pattern>/service/*</url-pattern> </filter-mapping>
7、JDBC
7.1、idea连接MySQL
-
首先,需要知道当前电脑所安装的MySQL版本,可以在navicat中看到
-
根据版本在pom.xml中导入相应的包
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.14</version> </dependency>
-
点击右侧的Database: +→Data Source→MySQL
-
配置Driver,由于本电脑使用的是MySQL8
- 这里要注意:一定要加时区
- ?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
-
点击Test Connection,测试是否能够连接
-
若连接报超时,可能是防火墙未关闭,可暂时关闭一下
7.2、JDBC查询语句
package com.ljh.jdbc;
import java.sql.*;
public class JdbcTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
/*配置信息*/
String url = "jdbc:mysql://localhost:3306/ob?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false";
String username = "root";
String password = "1234";
/*JDBC查询*/
//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 ob_rating";
/*若是增删改操作,则执行int i = statement.executeUpdate(sql);*/
/*其中返回的整形i是受影响的行数*/
//5.执行SQL语句,返回一个ResultSet(结果集)
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
System.out.println("id="+resultSet.getObject("id"));
System.out.println("user_id="+resultSet.getObject("user_id"));
System.out.println("user_name="+resultSet.getObject("user_name"));
System.out.println("book_id="+resultSet.getObject("book_id"));
System.out.println("book_name="+resultSet.getObject("book_name"));
System.out.println("book_rating="+resultSet.getObject("book_rating"));
System.out.println("rating_date="+resultSet.getObject("rating_date"));
}
//6.关闭数据库,先开后关
resultSet.close();
statement.close();
connection.close();
}
}
7.3、预编译插入语句
package com.ljh.jdbc;
import java.sql.*;
public class JdbcTest02 {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
/*配置信息*/
String url = "jdbc:mysql://localhost:3306/student?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false";
String username = "root";
String password = "1234";
/*JDBC查询*/
//1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.连接数据库
Connection connection = DriverManager.getConnection(url, username, password);
//3.编写SQL语句
String sql = "insert into users values (?,?,?,?);";//这里的?为占位符
//4.预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,1);//给第一个占位符?赋值为1
preparedStatement.setString(2,"小明");//给第二个占位符?赋值为"小明"
preparedStatement.setString(3,"123456");
preparedStatement.setInt(4,13);
//5.执行SQL语句,用executeUpdate
int i = preparedStatement.executeUpdate();
if(i>0) {
System.out.println("插入成功!");
}
//6.关闭数据库,先开后关
preparedStatement.close();
connection.close();
}
}
7.4、模拟事务回滚
package com.ljh.jdbc;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JdbcTest03 {
/*下面的@Test注解只有在方法上有效,只要加了这个注解的方法,就可以直接运行!*/
/*@Test是pom.xml中添加的junit单元测试依赖*/
@Test
public void test() {
/*配置信息*/
String url = "jdbc:mysql://localhost:3306/student?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false";
String username = "root";
String password = "1234";
Connection connection = null;
//1.加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
//2.连接数据库
connection = DriverManager.getConnection(url, username, password);
//3.通知数据库开启事务,false代表开启,一定要写,不写的话会造成数据库中数据错误
connection.setAutoCommit(false);
//4.编写SQL语句
String sql1 = "update users set usertype = usertype-100 where id = '3'";
//5.预编译,执行SQL语句
connection.prepareStatement(sql1).executeUpdate();
//制造错误,主要是在报错的时候测试是否第一条执行完了,第二条却没执行的情况
//此时,数据库应回滚,否则破坏事务的一致性
int i = 1/0;//若把此错误注释,会正常运行
String sql2 = "update users set usertype = usertype+100 where id = '4'";
connection.prepareStatement(sql2).executeUpdate();
//等待上面两条数据提交完毕,提交事务
connection.commit();
System.out.println("事务提交成功!");
} catch (Exception e) {
try {
//如果出现异常,就通知数据库回滚事务
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}