Hello World

JavaWeb笔记

2025/03/02
loading

JavaWeb

HTML + CSS

CSS选择器

  • 元素选择器:标签,例如h1

  • id选择器:给标签指定id属性,用#选择,注:id不会重复

  • 类选择器:用class注明类,用.选择

优先级:id选择器 > 类选择器

盒子模型:由内向外,content,内边距padding,边框border,外边距margin

div 标签:一行只显示一个,宽度默认是父元素的宽度

span 标签:一行可以显示多个

表单form标签:主要负责数据采集功能,例如登录,填写信息等

JavaScript

运算符:==会进行类型转换,===不会进行类型转换。

类型转换:parseInt,将字符串转换为数字,从第一个字符开始转换,一直到非数字字符。转换不了的话返回NaN

对象:

  1. 基础对象:

    1. Array:定义:new Array() 或 直接[]。数组长度可变,例如一个有三个元素的数组可以给第十个元素赋值,中间的元素为undefined。数组可以存放不同类型的数据。
      for循环遍历数组中所有元素,forEach遍历有值的元素

      1
      2
      3
      4
      5
      6
      7
      var arr = [1, 2, 3, 4];
      arr.forEach(function (e) {
      console.log(e);
      });
      arr.forEach((e) => {
      console.log(e);
      });
    2. 自定义对象

      1
      2
      3
      4
      var 对象名 = {
      属性名: 属性值;
      函数名() {};
      };
  1. BOM 浏览器对象模型

  2. DOM 文档对象模型

Vue

基于MVVM模型(Model - View - ViewModel)实现数据的双向绑定,将编程的关注点放在数据上

Ajax

异步的javascript和xml

通过ajax可以给服务器发送请求,并获取服务器响应的数据

异步交互:不重新加载整个页面的情况下,与服务器交换数据并更新部分网页。

axios是对ajax的封装

Maven

用于管理和构建java项目。mac中用brew安装maven,安装目录在/usr/local/Cellar/maven下,里面有配置文件,其中指定了依赖仓库的位置在/.m2目录下,还可以指定镜像仓库。

Maven坐标由groupId(项目隶属组织,域名反写),artifactId(项目名称或模块名称)和version组成。通过坐标可以唯一定位资源。

依赖可以传递,如果不想要传递的依赖,就在dependency里面加入exclusions标签来排除依赖。

可以用scope标签指定依赖的作用范围,四种取值:compile,test,provided,runtime。

Spring Boot

快速构建程序。

  • 接收参数:

    • 简单参数:
      直接在形参定义,形参名字要和参数一样,localhost:8080/hello?a=tom&b=1
      如果想让形参名字和请求参数不一样,要加入RequestParam注解

      1
      2
      3
      4
      5
      @RequestMapping("/hello")
      public String hello(@RequestParam(name="name") String a, Integer b) {
      System.out.println(a + b);
      return "ok";
      }
    • 实体参数:

      简单实体,定义POJO,参数名和对象内的字段名一样

      如果对象有嵌套的话,用符号“点”

    • 数组参数:直接定义一个数组作为形参

    • 集合参数:定义同名集合,在前面加上RequestParam注解

    • 日期参数:用DateTimeFormat注解来指定日期格式

    • JSON参数:定义POJO接收参数,需要加上RequestBody注解

    • 路径参数:

      1
      2
      3
      4
      5
      @RequestMapping("/hi/{name}")
      public String who(@PathVariable String name) {
      System.out.println(name);
      return "ok";
      }

RequestMapping注解:value属性为请求路径,method属性为请求方法(get、post等)。简单的注解为GetMapping注解,就不用指定method了。
controller里面的方法上的注解里面的路径可能会有公共的开头,可以在controller类上加上RequestMapping注解,里面指定一个路径为公共开头。这样最后完整的请求路径就是类上的RequestMapping的value属性加上方法上的RequestMapping的value属性

没有接收到参数,指定默认值:

1
2
@GetMapping
public Result func(@RequestParam(defaultValue="1") Integer value) {}

响应:
使用ResponseBody注解,将方法返回值直接响应,如果是对象或者集合,将其转换为JSON后响应。
RestController = Controller + ResponseBody

三层架构:
Controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据。
Service:业务逻辑层,处理具体业务逻辑
dao:数据访问层(持久层),负责数据访问操作

控制反转IOC:容器来控制对象的创建控制权
依赖注入DI:容器为程序提供运行时所依赖的资源
bean对象:IOC容器中创建、管理的对象。

Service层和Dao层的实现类交给IOC容器管理:类上面加上Component注解
为Controller层和Service层注入运行时所依赖的对象:变量上面加上AutoWired注解

要把某个对象交给IOC容器管理,在类上加注解:

注解 说明
@Component 声明bean的基础注解,不属于以下三类时用此注解
@Controller 标注控制器类Controller
@Service 标注业务类Service
@Respository 标注数据访问类,由于与mybatis整合,用得少

AutoWired自动装配,默认按照类型,如果有多个相同类型的bean,会报错。解决方法有:加上Primary注解,指定优先级;加上Qualifier注解,手动指定注入哪个bean;加上Resource注解,手动指定bean。

lombok

使用注解的形式来自动生成”构造器、getter/setter、equals、hashcode、toString”等方法,还可以自动化生成日志变量
![[截屏2025-02-02 08.02.47.png]]
引入依赖

1
2
3
4
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

Slf4j注解:替代private static Logger log = LoggerFactory.getLogger(DeptController.class),使用注解后,用log.info(""); 就能记录日志。

MyBatis

持久层框架,用于简化JDBC的开发。
引入依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>  
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.2.0</version>
</dependency>

在application.properties中配置MyBatis,配置数据库连接信息(四要素)

1
2
3
4
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver  
spring.datasource.url=jdbc:mysql://localhost:3306/employees
spring.datasource.username=root
spring.datasource.password=

编写SQL语句(注解/XML)
注解:在Mapper类前加Mapper注解,运行时会自动生成该接口的实现类对象(代理对象),并且将该对象交给IOC容器管理

1
2
3
4
5
@Mapper  
public interface EmployeeMapper {
@Select("select * from Employees")
List<Employee> list();
}

获取对象

1
2
@Autowired  
private EmployeeMapper employeeMapper;

数据库连接池

标准接口:DataSource:sun官方提供的数据库连接池接口,由第三方组织实现此接口,功能:获取连接,Connection getConnection() thows SQLException
springboot默认为HikariDataSource。想要切换为Druid数据库连接池需要修改依赖:

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.24</version>
</dependency>

切换druid可以在配置文件中datasource后面加上druid,这个是可选的。

MyBatis基本操作(注解)

删除数据

1
2
@Delete("delete from Employees where id = #{id}")
int delete(Integer id); // 返回值表示此操作影响的记录数量

注:如果mapper接口方法形参只有一个普通类型的参数,那么#{}里面的参数名可以随便写

查看日志

查看mybatis日志:在application.properties配置文件中打开mybatis日志并指定输出到控制台:mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

#占位符

sql语句里面会有一些?作为占位符,这样sql可以预编译,更加高效和安全
sql注入:' or '1'='1
参数占位符:可以用#{}${}#会将占位符替换为,生成预编译SQL,会自动设置参数值。$会将参数拼接在SQL里,有SQL注入问题。

插入数据

参数太多,可以插入一个对象

1
2
3
4
5
6
7
8
9
// EmployeeMapper.java
@Insert("insert into Employees(emp_no, birth_date, first_name, last_name, gender, hire_date) " +
"values(#{emp_no},#{birth_date},#{first_name},#{last_name},#{gender},#{hire_date})")
void insert(Employee employee);

// test.java
Employee employee = new Employee(2222222, LocalDate.of(2000, 7, 4),
"Hai", "Lu", 'M', LocalDate.of(2025,2,4));
employeeMapper.insert(employee);

(注:下面的结论需要测试,有可能是插入数据时,如果emp_no是自增主键的话,插入时不用注明,所以需要额外获取,我上面指定了emp_no值,所以可能不需要加这个注解来获取主键的值)
如果需要获取插入数据的主键,需要添加注解:@Options(useGeneratedKeys=true, keyProperty="emp_no"),useGeneratedKeys表示需要拿到生成的主键值,keyProperty表示获取到的主键值封装到employee对象的emp_no属性中。

更新数据

使用Update注解,类似于插入,可以用对象作为参数

查询数据

注意要设置返回值,如果返回值可能有多个,就返回列表

1
2
@Select("select * from Employees where last_name=#{last_name}")  
Employee findByLastname(String last_name);

条件查询模糊匹配时,例如@Select("select * from Employees where last_name like '%${last_name}%'"),注意此处用的是${} 而不是 #{},因为问号不能出现在单引号内。一个解决方案是用concat函数来进行字符串拼接:@Select("select * from Employees where last_name like concat('%', #{last_name}, '%')")

数据封装

如果实体类属性名(java中定义的pojo类的字段名)和数据库返回的字段名一致,则mybatis会自动封装,如果名称不一致,则不能自动封装。
解决方案1:给返回的字段起别名,别名和实体类的属性名一致。
解决方案2:使用Results和Result注解手动映射封装

1
2
3
4
@Results([
@Result(column="数据库中的字段名", property="实体类属性名")
@Result(column="数据库中的字段名", property="实体类属性名")
])

解决方案3:mybatis驼峰命名自动映射,在application.properties配置文件中指定:mybatis.configuration.map-underscore-to-camel-case=true

XML映射文件

XML映射文件的名称与Mapper接口名称一致,文件位置放在resources中与Mapper文件相同包名内。XML中namespace属性与Mapper接口的全限定名一致。XML中SQL语句的id和Mapper接口中的方法名一致,返回类型一致。

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.springboot_test.Mapper.EmployeeMapper">
<select id="findByFirstname" resultType="com.example.springboot_test.pojo.Employee">
select * from Employees where first_name like concat('%', #{first_name}, '%')
</select>
</mapper>

resultType指的是单条记录所封装的类型。
XML适用于复杂的场景,而注解适用于简单场景。

动态SQL

<if>

使用test属性进行判断,如果为true,则拼接里面的SQL

1
2
3
4
5
6
7
8
9
select * from Employees
<where>
<if test="gender='F'">
emp_no > 1000
</if>
<if test="。。。">
and 。。。
</if>
</where>

注意使用的是where标签而不是正常sql语句的where,可以去除if标签里面开头可能有的and和or。所有if都是false的话,在sql语句中不会出现where关键字。
与where标签类似,mybatis也提供了set标签来代替update语句里的set关键字,类似于where,set里面的语句可能会出现不必要的逗号,set标签会去除这些逗号。

<foreach>

标签里的常用属性 描述
collection 遍历的集合
item 遍历出来的元素
separator 分隔符
open 遍历开始前拼接的sql片段
close 遍历结束后拼接的sql片段
例如按id批量删除
1
2
3
4
5
6
7
<delete id="deleteByMultiId">  
delete from Employees
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>

<sql> <include>

类似于将java代码中重复的代码抽取为一个方法,使用sql标签相当于定义一个方法,里面的id属性相当于方法名,include标签相当于使用该方法,refid属性指定方法名。

1
2
3
4
5
<sql id="selectAll">  
select * from Employees
</sql>

<include refid="selectAll"/> where first_name like concat('%', #{first_name}, '%')

案例

前端部署在Nginx中,后端部署在tomcat中。
rest风格:通过url定位资源,使用http动词描述操作,get是查询,post是新增,put是修改,delete是删除。

以查询部门为例,前端请求路径/depts,
DeptController接受请求,然后调用service查询部门信息,最后响应数据
DeptService调用mapper接口进行查询
DeptMapper使用SQL语句进行数据库查询

controller中调用service:面向接口

1
2
@AutoWired
private DeptService deptService;

分页查询可以使用PageHelper插件

1
2
3
4
5
6
7
8
9
10
// EmpMapper
@Select("select * from emp)
List<Emp> list();

// EmpServiceImpl
PageHelper.startPage(page, pageSize); // 设置分页参数
List<Emp> empList = empMapper.list(); // 查询
Page<Emp> p = (Page<Emp) empList;
PageBean pageBean = new PageBean(p.getTotel(), p.getResult()); // 封装对象

以新增员工为例:

查看接口文档:请求路径:“/emps”,请求方式POST,请求参数json格式
基本流程:

  1. 浏览器发送请求后,EmpController接收参数,调用service的方法保存数据,然后响应(需要加上PostMapping注解,接收json用实体类封装,参数前加上RequestBody注解)
  2. EmpService:在Service接口中添加方法,在service实现类中实现方法。1. 补充基础属性(创建时间、修改时间等)。2. 调用mapper接口保存数据
  3. EmpMapper:编写SQL语句:insert into。。。

文件上传

前端上传文件时,需要编写下面的表单代码。前端页面三要素:method为post,enctype为multipart/form-data,input标签的type为file:

1
2
3
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image">
</form>

在controller接受文件时,使用MultipartFile类型接受文件:

1
2
3
4
5
6
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(MultipartFile image) {
}
}

假如upload里面的参数名为file,与表单名image不一样,可以加入RequestParam注解:
public Result upload (@RequestParam("image") MultipartFile file)

存储文件

image.transferTo方法将接受到的文件转存到本地磁盘文件中
文件名不能重复,可以使用uuid(通用唯一识别码)
在spring boot中,文件上传默认最大大小为1M,可以在配置中修改:

1
2
3
# 上面的是配置单个文件,下面的是配置单个请求(可以有多个文件)
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB

可以将文件上传到云OSS对象存储服务。

配置文件

在application.properties中配置某些参数,例如:aliyun.oss.endpoint=。。。 ,在代码中使用Value注解注入外部配置,用法为:@Value("${配置文件里的key}")

1
2
@Value("${aliyun.oss.endpoint}")
private String endpoint;

也可以用ConfigurationProperties注解自动注入,需要指定前缀prefix

配置格式

springboot提供多种配置方式
application.properties

1
server.port=8080

application.yml (推荐) / application.yaml

1
2
server:
port:8080

登录认证

会话跟踪:维护浏览器状态,服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪方案:客户端会话跟踪:cookie,服务端会话跟踪:session,令牌技术

服务器响应时,使用set-cookie,浏览器保存,后面请求时用cookie携带

Session

基于cookie,

令牌技术

JWT令牌json web token,
组成:
第一部分:header头,记录令牌类型,签名算法等,例如:{"alg":"HS256","type":"JWT"}
第二部分:payload有效载荷,携带自定义信息,默认信息等
第三部分:signature签名,确保安全性,计算得出

登录成功后,服务器生成令牌。后续每个请求都携带JWT令牌,系统处理请求前,先校验令牌。
使用时,引入依赖:jjwt

1
2
3
4
5
String jwt = Jwts.builder()  
.signWith(SignatureAlgorithm.HS256, "secret")
.setClaims() // 载荷
.setExpiration() // 有效时间
.compact();

解析令牌,得到载荷:

1
2
3
4
Jwts.parser()  
.setSigningKey("secret")
.parseClaimsJws(jwt)
.getBody();

前两个部分是base64编码的,可以随意解码,第三个部分是加密的.
注:NoClassDefFoundError报错,解决方式,添加依赖:jaxb-api与jaxb-runtime

通过JWT跟踪会话:
流程:浏览器登陆成功后,服务器生成JWT令牌,直接返回给前端浏览器,前端将令牌存储起来,后续请求,每一次请求都要携带令牌,服务器收到令牌对其进行校验.
具体来说,服务器在Result中的data字段中包含JWT.浏览器在每一次请求的请求头中携带JWT,请求头的名称为token,值为JWT.

拦截请求:

CATALOG
  1. 1. JavaWeb
    1. 1.1. HTML + CSS
    2. 1.2. JavaScript
      1. 1.2.1. 对象:
    3. 1.3. Vue
    4. 1.4. Ajax
    5. 1.5. Maven
    6. 1.6. Spring Boot
    7. 1.7. lombok
    8. 1.8. MyBatis
      1. 1.8.1. 数据库连接池
      2. 1.8.2. MyBatis基本操作(注解)
        1. 1.8.2.1. 删除数据
        2. 1.8.2.2. 查看日志
        3. 1.8.2.3. #占位符
        4. 1.8.2.4. 插入数据
        5. 1.8.2.5. 更新数据
        6. 1.8.2.6. 查询数据
        7. 1.8.2.7. 数据封装
      3. 1.8.3. XML映射文件
      4. 1.8.4. 动态SQL
        1. 1.8.4.1. <if>
        2. 1.8.4.2. <foreach>
        3. 1.8.4.3. <sql> <include>
    9. 1.9. 案例
      1. 1.9.1. 以新增员工为例:
      2. 1.9.2. 文件上传
        1. 1.9.2.1. 存储文件
      3. 1.9.3. 配置文件
        1. 1.9.3.1. 配置格式
    10. 1.10. 登录认证
      1. 1.10.1. cookie
      2. 1.10.2. Session
      3. 1.10.3. 令牌技术