SpringBoot

1.SpringBoot概述

  • Spring Boot是Spring提供的一个子项目,用于快速构建Spring应用程序

1.1 SpringBoot特性

1.1.1 起步依赖

1
本质上就是一个Maven坐标,整合了完成一个功能需要的所有坐标

image-20231205203333300

1.1.2 自动配置

1
遵循约定大约配置的原则,在boot程序启动后,一些bean对象会自动注入到ioc容器,不需要手动声明,简化开发

image-20231205203452281

1.1.3 其它特性

1
2
3
1.内嵌的Tomcat、Jetty(无需部署WAR文件,只需要jar包即可)
2.外部化配置
3.不需要XML配置(使用两种配置文件:properties/yml)

2. SpringBoot快速配置启动

  • 需求:

image-20231204192633172

  • 使用步骤:

image-20231204192703435

2.1 创建springboot项目(需要联网)

基于Spring官方骨架,创建SpringBoot工程

image-20231222161742596

基本信息描述完毕之后,勾选web开发相关依赖

image-20231222161807562

点击Finish之后,就会联网创建这个SpringBoot工程,创建好之后,结构如下:

image-20231222161832561

2.2 定义请求处理类

运行SpringBoot自动生成的引导类

image-20231222161927734

2.3 运行启动类

运行SpringBoot自动生成的引导类

image-20231222161954197

打开浏览器,输入 http://localhost:8080/hello

image-20231222162007824

2.4 Web分析

image-20231222162146209

3. 配置文件(两种)

3.1 application.properties配置文件

image-20231205194847853

3.2 application.yml配置文件

image-20231205194904840

3.3 两者对比

image-20231205201627980

3.4 配置文件书写和获取(yml为例)

1.第三方技术配置信息

image-20231205202058345

2.自定义配置信息

image-20231205202150707

  1. 书写:
    image-20231205202329674

  2. 获取:

    1
    2
    3
    @Value("${键名}")

    @ConfigurationProperties(prefix="共同上级/前缀")
    • 1:Value注解:

    image-20231205202739537

    • 2.ConfigurationProperties注解(设定共同前缀):

image-20231205203010224

4. 整合Mybatis

  • 整体图

    image-20231206143956810

4.1 添加依赖

image-20231206144112908

4.2 添加配置

image-20231206144155200

4.3 书写业务逻辑

image-20231206154000861

1.User类

  • 实体类主要负责编写数据库的实体信息,和数据库的表要对应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.itheima.springbootmybatis.pojo;
public class User {
private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;
public User() {
}
public User(Integer id, String name, Short age, Short gender, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
public Short getGender() {
return gender;
}
public void setGender(Short gender) {
this.gender = gender;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
", phone='" + phone + '\'' +
'}';
}
}

2.UserMapper类

  • 主要编写sql语句 ** **–主要用到Mapper和Select注解
1
2
3
4
5
6
7
8
9
10
package com.itheima.springbootmybatis.mapper;
import com.itheima.springbootmybatis.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {
@Select("select * from user where id=#{id}")
public User findById(Integer id);
}

3.UserService类

  • 主要编写所有需求方法
1
2
3
4
5
6
package com.itheima.springbootmybatis.Service;
import com.itheima.springbootmybatis.pojo.User;

public interface UserService {
public User findById(Integer id);
}
  • 主要实现接口方法,调用Mapper类对象实现 –主要用到Service和Autowired注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.itheima.springbootmybatis.Service.impl;
import com.itheima.springbootmybatis.Service.UserService;
import com.itheima.springbootmybatis.mapper.UserMapper;
import com.itheima.springbootmybatis.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findById(Integer id) {
return userMapper.findById(id);
}
}

4.UserController类

  • 主要调用Service接口对象实现 –主要用到RestController和Autowired和RequestMapping注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.itheima.springbootmybatis.controller;
import com.itheima.springbootmybatis.Service.UserService;
import com.itheima.springbootmybatis.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findById")
public User findById(Integer id){
User byId = userService.findById(id);
return byId;
}
}

5.最终结果

image-20231206145717660

5.Bean扫描(自动注解)

  • Springboot启动类的注解可以自动扫描,并且默认扫描启动类所在包和子包
image-20231206155042959

6.来自第三方的Bean对象

6.1 怎么引入

步骤一: maven下载

image-20231214121545801

步骤二:pom.xml导入jar依赖

image-20231214121831143

步骤三:刷新maven之后在外部库查看

image-20231214122259994

6.2 怎么注解

如果要注册的bean对象来自于第三方(不是自定义的),是无法用 @Component 及衍生注解声明bean的只能使用以下两种方式去解决bean注解

方式一:Bean注解

  • 以下是两种方法的区别

  • 1.存放到启动类

image-20231214121924472

  • 2.存放到配置类(@Configuration)

    配置类是可以写很多bean对象,并且配置类必须存放在启动类的同级和下一级包位置,方便Bean扫描(如果放在外面就需要方法2import注解了)

image-20231214121937990

方式二:Import注解

  • 1.导入配置类

image-20231214173522240

  • 2.导入ImportSelector接口实现类

    先创建一个java类重写ImportSelector接口的方法,返回值就是一个数组(要放入的bean对象)

    image-20231214181412529

    然后在启动类中添加import注解就可以

image-20231214181440899

方式三: EnableXxxx注解(封装@Import注解)

image-20231227100935762

然后在启动类中添加import注解就可以

image-20231227101125400

6.3 注册条件(符合条件才可以注入ioc容器)

Springboot提供了Conditional注解解决注册时候对象赋值问题,但是过于复杂所以给出了常用的三个相关注解

image-20231215105437322

1. @ConditionalOnProperty(从配置文件判断)

从配置文件中的读取,读取成功注入,读取失败不注入

image-20231215110651901

2.@ConditionalOnMissingBean(从IOC容器判断)

因为Country需要从配置文件中读取注入,失败了所以没有注入,这样的话下面的province就会注入

image-20231215110456029

3.@ConditionalOnClass(从当前环境判断)

如果环境中有当前类就注入,没有就不注入

image-20231215111612870

7.自动配置原理

7.1 原理图

  • 其实就是将自动配置类写到一个.imports配置文件中
image-20231218140121744

上图描述:

image-20231218142400281
  • 第二遍的理解
image-20231227145334477

7.2 如何实现

  • 原来的情况: 导入jar包之后只有类,需要自己写配置类和启动类import导入

image-20231218141522259

  • 现在的情况: 导入jar包的时候里面不仅仅有类,还有配置类,imports配置文件等,不需要添加任何其他操作

    image-20231218142256913

8.自定义starter(公共组件)

  • 主要是自己写autoconfigure和starter两个组件

image-20231218152255793

8.1 提供核心组件1-autoconfigure模块

image-20231218152656226

8.2 提供核心组件2-starter模块

image-20231218152819225

8.3 使用starter模块

image-20231218152955444

9.Springboot父工程(为其他依赖提供兼容版本)

之前开发的SpringBoot入门案例中,我们通过maven引入的依赖,是没有指定具体的依赖版本号的。因为每一个SpringBoot工程,都有一个父工程。依赖的版本号,在父工程中统一管理
image-20231222172623765

10.内置tomcat

我们的SpringBoot中,引入了web运行环境(也就是引入spring-boot-starter-web起步依赖),其内部已经集成了内置的Tomcat服务器。

我们可以通过IDEA开发工具右侧的maven面板中,就可以看到当前工程引入的依赖。其中已经将Tomcat的相关依赖传递下来了,也就是说在SpringBoot中可以直接使用Tomcat服务器

image-20231222172735914

当我们运行SpringBoot的引导类时(运行main方法),就会看到命令行输出的日志,其中占用8080端口的就是Tomcat

image-20231222172803184

11. Web后端开发总结

web后端开发现在基本上都是基于标准的三层架构进行开发的,在三层架构当中,Controller控制器层负责接收请求响应数据,Service业务层负责具体的业务逻辑处理,而Dao数据访问层也叫持久层,就是用来处理数据访问操作的,来完成数据库当中数据的增删改查操作。

image-20231227104938801

在三层架构当中,前端发起请求首先会到达Controller(不进行逻辑处理),然后Controller会直接调用Service 进行逻辑处理, Service再调用Dao完成数据访问操作

如果我们在执行具体的业务处理之前,需要去做一些通用的业务处理,比如:我们要进行统一的登录校验,我们要进行统一的字符编码等这些操作时,我们就可以借助于Javaweb当中三大组件之一的过滤器Filter或者是Spring当中提供的拦截器Interceptor来实现

image-20231227105011355

而为了实现三层架构层与层之间的解耦,我们学习了Spring框架当中的第一大核心:IOC控制反转与DI依赖注入

所谓控制反转,指的是将对象创建的控制权由应用程序自身交给外部容器,这个容器就是我们常说的IOC容器或Spring容器。

而DI依赖注入指的是容器为程序提供运行时所需要的资源

除了IOC与DI我们还讲到了AOP面向切面编程,还有Spring中的事务管理、全局异常处理器,以及传递会话技术Cookie、Session以及新的会话跟踪解决方案JWT令牌,阿里云OSS对象存储服务,以及通过Mybatis持久层架构操作数据库等技术

image-20231227105053208

我们在学习这些web后端开发技术的时候,我们都是基于主流的SpringBoot进行整合使用的。而SpringBoot又是用来简化开发,提高开发效率的。像过滤器、拦截器、IOC、DI、AOP、事务管理等这些技术到底是哪个框架提供的核心功能

image-20231227105131085

Filter过滤器、Cookie、 Session这些都是传统的JavaWeb提供的技术。

JWT令牌、阿里云OSS对象存储服务,是现在企业项目中常见的一些解决方案。

IOC控制反转、DI依赖注入、AOP面向切面编程、事务管理、全局异常处理、拦截器等,这些技术都是 Spring Framework框架当中提供的核心功能。

Mybatis就是一个持久层的框架,是用来操作数据库的。

在Spring框架的生态中,对web程序开发提供了很好的支持,如:全局异常处理器、拦截器这些都是Spring框架中web开发模块所提供的功能,而Spring框架的web开发模块,我们也称为:SpringMVC

image-20231227105333268

SpringMVC不是一个单独的框架,它是Spring框架的一部分,是Spring框架中的web开发模块,是用来简化原始的Servlet程序开发的

外界俗称的SSM,就是由:SpringMVC、Spring Framework、Mybatis三块组成。

基于传统的SSM框架进行整合开发项目会比较繁琐,而且效率也比较低,所以在现在的企业项目开发当中,基本上都是直接基于SpringBoot整合SSM进行项目开发的

链表

单链表

单链表图示:


单链表的基础操作

具体实现功能:

1. 添加节点(将指针不停指向下一个 判空) 
2. 查询下一个节点(返回this.next) 
3. 获取当前节点数据(返回this.data) 
4. 判断是不是最后一个节点(只需要返回next是不是为空)

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Node {
//节点内容
int data;

//下一个节点
Node next;

public Node(int data){
this.data=data;
}

//追加节点
public Node append(Node node){
//当前节点
Node currentNode =this;
while(true){
//取出下一个节点赋给上一个节点
Node nextNode=currentNode.next;
if(nextNode==null)
{
break; //下一个为空就跳出
}
currentNode=nextNode; //将当前指针挪到下一个
}
//把想要追加的追加上去(上面循环已经肯定是到最后一个节点了)
currentNode.next=node;

return this; //可不停地追加 n1.append(n2).append(3)...
}

//获取下一个节点
public Node next(){
return this.next; //返回next
}

//获取数据
public int getData(){
return this.data; //获取数据
}

//判断是否是最后一个
public boolean isLast(){
return next==null;
}
}

测试Test类

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args){
//创建节点
Node n1=new Node(1);
Node n2=new Node(2);
Node n3=new Node(3);
n1.append(n2).append(n3).append(new Node(4)); // 1 --> 2 --> 3 --> 4

System.out.println(n1.next().next().next().getData()); //数据是4的节点
System.out.println(n1.next().next().next().isLast()); //判断数据是4的节点是不是最后一个
}
}

测试结果:


单链表删除节点(让下下一个节点连到本节点)

1
2
3
4
5
6
7
//删除节点
public void removeNext(){
//获取下下一个节点
Node newNext= next.next;
//下下一个节点直接给这个节点(直接把下一个节点删除)
this.next=newNext;
}

单链表插入节点(先管卸下去的节点然后再连接)

方法实现解释:

1
2
3
4
5
假设现在是 1 --> 2 --> 3 --> 4  我们想把5插入到2后面
具体实现:
1. 将原来的下一个节点3先存到一个变量节点
2. 将5连接到2后面 1 --> 2 --> 5 || 3 --> 4
3. 现在5已经替换了3的位置 把存3的那个变量节点连到5后面 1 --> 2 --> 5 --> 3 --> 4

方式实现

1
2
3
4
5
6
7
8
9
10

//插入节点(先将旧的插到新的上面 新的在插到要插的位置)
public void after(Node node){
//获取下一个节点为下下一个节点
Node nextNext=next; //现在的这个下一个节点存到一个叫nextNext的节点
//新加入的节点成为这个节点的下一个节点
this.next=node;
//插入的节点后面把存的那个节点接上去
node.next=nextNext;
}

测试Test类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args){
//创建节点
Node n1=new Node(1);
Node n2=new Node(2);
Node n3=new Node(3);
n1.append(n2).append(n3).append(new Node(4)); // 1 --> 2 --> 3 --> 4

Node n5=new Node(5); //要插入的n5节点
n1.next().after(n5); //插入到n2的后面 n1.next()就是n2
n1.show(); //展示所有节点

}
}

插入n5在n2后面:


循环链表

循环链表图示:

方法实现(下一个节点指向当前节点)

对比单链表: 最后一个的next要指向开头的节点

1
2
//下一个节点
LoopNode next=this;

具体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class LoopNode {
//节点内容
int data;

//下一个节点
LoopNode next=this;

public LoopNode(int data){
this.data=data;
}

//获取下一个节点
public LoopNode next(){
return this.next; //返回next
}

//获取数据
public int getData(){
return this.data; //获取数据
}

//判断是否是最后一个
public boolean isLast(){
return next==null;
}

//删除节点
public void removeNext(){
//获取下下一个节点
LoopNode newNext= next.next;
//下下一个节点直接给这个节点(直接把下一个节点删除)
this.next=newNext;
}


//插入节点(先将旧的插到新的上面 新的在插到要插的位置)
public void after(LoopNode node){
//获取下一个节点为下下一个节点
LoopNode nextNext=next; //下一个节点给下下一个节点
//新加入的节点成为这个节点的下一个节点
this.next=node;
//下下一个节点成为新节点的下一个节点
node.next=nextNext;
}


}

测试Test类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {
public static void main(String[] args){
//创建节点
LoopNode n1=new LoopNode(1);
LoopNode n2=new LoopNode(2);
LoopNode n3=new LoopNode(3);
LoopNode n4=new LoopNode(4);

//插入节点
n1.after(n2); //现在只有 1 --> 2
n2.after(n3); //现在是 1 --> 2 -->3
System.out.println(n1.next().getData()); // 1的下一个节点的内容应该输出2
System.out.println(n2.next().getData()); // 2的下一个节点的内容应该输出3
System.out.println(n3.next().getData()); // 3的下一个节点的内容应该输出1
}
}

具体实现结果:


双向循环链表

双向循环链表图示:

方式实现

图示解释:

具体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class DoubleNode {
//上一个节点
DoubleNode pre=this; //一开始这个节点的前一个和后一个都是它自己!!!
//下一个节点
DoubleNode next=this;
//节点数据
int data;

public DoubleNode(int data) {
this.data=data;
}

//增加节点
public void after(DoubleNode node){
//原来的下一个节点
DoubleNode nextNext=next;
//把新节点作为当前节点的下一个节点
this.next=node;
//把当前节点作为新节点的前一个节点
node.pre=this;
//让原来的下一个节点作为新节点的下一个节点
node.next=nextNext;
//让原来的下一个节点的上一个节点作为新节点
nextNext.pre=node;
}

//下一个节点
public DoubleNode next() {
return this.next;
}

//上一个节点
public DoubleNode pre(){
return this.pre;
}

//获取数据
public int getData(){
return this.data;
}

}

测试Test类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test {
public static void main(String[] args){
//创建节点
DoubleNode n1 = new DoubleNode(1);
DoubleNode n2 = new DoubleNode(2);
DoubleNode n3 = new DoubleNode(3);
//追加节点
n1.after(n2);
n2.after(n3); // 1 2 3 循环
//查看上一个,自己,下一个节点的内容
System.out.println(n2.pre().getData()); // n2的前一个节点的数据 1
System.out.println(n2.getData()); // n2的数据 2
System.out.println(n2.next().getData()); // n2的下一个节点的数据 3
System.out.println(n3.next().getData()); // n3的下一个节点的数据 1
System.out.println(n1.pre().getData()); // n1的前一个节点的数据 3
}
}

测试代码结果:


队列

队列(先进先出)

队列就跟排队一样


具体实现

具体方法:

1. 入队(新数组在旧数组的基础上加元素然后替换旧数组)
2. 出队(需要取出第一个并且返回 其余的错位传给新数据 用新数组替换旧数组即可)
3. 判空(判断数组长度是否为空)

实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class MyQueue {
//底层用数组来存储数据
int[] elements;

public MyQueue(){
elements=new int[0];
}

//入队(和栈一样的代码思路)
public void add(int element){
//创建一个新的数组
int[] newArr =new int[elements.length+1];
//原始数组中的元素赋值到新的数组
for(int i=0;i<elements.length;i++){
newArr[i]=elements[i];
}
//将添加的元素加入到新数据
newArr[elements.length]=element; //放到最后!!!
//新数组替换旧数组
elements=newArr;
}

//出队(需要取出第一个并且返回 其余的错位传给新数据 用新数组替换旧数组即可)
public int poll(){
//取出来第一个元素
int element=elements[0];

//新建数组
int[] newArr=new int[elements.length-1];
//旧数组除了第一个数存到新的数组
for(int i=0;i<newArr.length;i++){
newArr[i]=elements[i+1]; //从第二个开始错位传给新的参数
}
//替换数组
elements=newArr;
//返回队列第一个元素
return element;
}

//判断栈是否为空(只需要返回布尔值是不是为空)
public boolean isEmpty(){
return elements.length==0;
}

}

测试Test类

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args){
//创建一个队列
MyQueue m=new MyQueue();
m.add(9);
m.add(8);
m.add(7); // 从前往后: 9 8 7

System.out.println(m.poll()); //第一个为9
System.out.println(m.poll()); //第二个为8 因为9已经被踢出去了
}
}

具体结果:


数组模拟队列

思路

1. 阿斯达请问
2. 阿斯达阿斯达    

代码实现

1
2



栈(先进后出)

栈相当于手枪弹夹(不停地往下压子弹)


具体实现

具体方法

1. 压入元素(新数组在旧数组基础上加上压入的元素之后替换旧数组)
2. 取出栈顶(取出并返回栈顶 然后新数组将其他元素放入后替换旧数组)
3. 查看栈顶(只需要返回数组最后一个)
4. 判空(返回数组长度是否为空)

底层方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Main {
//底层用数组来存储数据
int[] elements;

public Main(){
elements=new int[0];
}

//压入元素(新数组在旧数组基础上加上压入的元素之后替换旧数组)
public void push(int element){
//创建一个新的数组
int[] newArr =new int[elements.length+1];
//原始数组中的元素赋值到新的数组
for(int i=0;i<elements.length;i++){
newArr[i]=elements[i];
}
//将添加的元素加入到新数据
newArr[elements.length]=element; //放到最后!!!
//新数组替换旧数组
elements=newArr;
}

//取出栈顶元素(取出并返回栈顶 然后新数组将其他元素放入后替换旧数组)
public int pop(){
if(elements.length==0){
System.out.println("有异常");
}
//取出数组最后一个数(栈顶)
int top= elements[elements.length-1];
//新建数组存放其他数据
int[] newArr=new int[elements.length-1];
//旧数组除了最后一个存到新的数组
for(int i=0;i<elements.length-1;i++){
newArr[i]=elements[i];
}
//替换数组
elements=newArr;

return top; //返回栈顶
}

//查看栈顶元素(只需要返回数组最后一个)
public int look(){
return elements[elements.length-1]; //只是查看栈顶
}

//判断栈是否为空(只需要返回是不是为空)
public boolean isEmpty(){
return elements.length==0;
}

}

测试Test类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args){
//创建一个栈
Main m=new Main();
//压入数组
m.push(9);
m.push(8);
m.push(7); // 从上到下: 7 8 9
//取数
System.out.println(m.pop()); // 7 因为目前最后压进去的是7
System.out.println(m.pop()); // 8 7已经弹出去了
System.out.println(m.look()); // 9 7.8都弹出去了
}
}

最终执行测试:


SpringMVC原理分析

运行原理

SpringMVC图示:

文字描述:

前提:
    web.xml文件中设置了DispatcherServlet的<url-pattern>为“/”
执行:
    1. 用于发起请求 --> 请求一个控制器
    2. 执行DispatcherServlet
    3. DispatcherServlet调用 HandlerMapping的DefaultAnnotatiionHandlerMapping解析URL
    4. 调用HandlerAdatper的AnnotationMethodHandlerAdapter
    5. 调用Controller中的HandlerMethod
    6. 当HandlerMehod执行完毕会返回view
    7. view会被viewResovler进行视图解析 --> 调用jsp对应的.class文件 --> 运行
    8. 将运行结果响应给客户端

SpringMVC实现自定义拦截器

拦截器

发送请求时被拦截器拦截,在控制器(注解类的@RequestMapping)前后添加额外功能

拦截器和AOP区别

1. AOP在特定方法前后进行扩充(对ServiceImpl实现类)
2. 拦截器对控制器方法(对Controller注解类)

SpringMVC拦截器和Filter过滤器区别

1. SpringMVC拦截器  拦截控制器Controller
2. Filter过滤器     拦截任何请求

拦截器实现(三个方法)

新建类实现HandlerInterceptor接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class DemoInterceptor implements HandlerInterceptor {

//1. 在进入控制器之前执行(false表示阻止进入控制器) --拦截器拦截哪个控制器
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("1.preHandle (控制器执行前)");
System.out.println("拦截器拦截的是:"+arg2); //arg2传的是哪个控制器
System.out.println("------------------------------------------------------");
return true; // false 表示 阻止进入控制器
}

//2. 控制器执行完成,进去jsp之前执行 -- 日志记录/敏感词语过滤
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)throws Exception {
System.out.println("3.postHandle (jsp之前)");
System.out.println("往"+ arg3.getViewName()+"跳转"); //arg3是跳转到哪个jsp页面
System.out.println("model的值 :"+arg3.getModel().get("model")); //获取model的值
String word = arg3.getModel().get("model").toString(); //将model的值tostring转为字符串
String neword = word.replace("祖国", "***"); //通过字符串替换 将祖国这个敏感词 换成 ***
System.out.println("拦截器执行之后model过滤后: "+neword); //输出之后的model结果
System.out.println("------------------------------------------------------");
}

//3. jsp执行完成后执行 -- 记录异常到日志里面
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)throws Exception {
System.out.println("5.afterCompletion jsp之后");
System.out.println("现在的异常是: "+arg3); //arg3获取异常是什么
System.out.println("------------------------------------------------------");
}

}

spingmvc配置拦截器(两种方法)

拦截所有的控制器

1
2
3
<mvc:interceptors>
<bean class="com.bjsxt.interceptor.DemoInterceptor"></bean>
</mvc:interceptors>

拦截特定的控制器

1
2
3
4
5
6
7
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/demo"/>
<mvc:mapping path="/demo01"/>
<bean class="com.bjsxt.interceptor.DemoInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>

控制器

1
2
3
4
5
6
7
8
9
10
11
@Controller
public class DemoController {

@RequestMapping("demo")
public String demo(Model model) {
System.out.println("2.控制器demo执行");
System.out.println("------------------------------------------------------");
model.addAttribute("model","我们都爱祖国"); // 设置model值为我们都爱祖国
return "index.jsp"; // 跳转到index.jsp页面
}
}

主页index.jsp页面##

1
2
3
4
5
6
7
8
<body>

<% System.out.println("4.index.jsp (jsp执行)");
System.out.println("---------------------------------------");
%>
jsp执行正常!

</body>

总结:

方法分析(3个):

1. preHandle :                  控制器前(可以获得拦截的是哪个控制器)
2. @RequestMapping的注解方法 :   控制器 (写跳转哪个jsp页面)
3. postHandle :                 控制器后 jsp前(日志记录/敏感词语过滤)
4. xxx.jsp                      jsp页面
5. afterCompletion :            jsp之后(记录异常/异常写入日志)

最终执行结果:


拦截器栈

多个拦截器同时生效 –> 拦截器栈

执行顺序(先进后出)

执行顺序和springmvc.xml里面配置的顺序有关

执行案例(两个拦截器)

配置顺序 : 拦截器A --> 拦截器B

执行顺序(圆环!!!) :  
            preHandle(A)  最外层
            preHandle(B)  次外层
            控制器方法     中心(上面都是A在前 下面都是B在前)
            postHandle(B) 
            postHandle(A)
            JSP页面
            afterCompletion(B)
            afterCompletion(A)

SpringMVC实现文件上传和下载

文件下载

1. 响应头没有设置:Content-Disposition(浏览器默认按照inline值处理)  
    1.1 inline 就是能显示就显示,不能显示就下载
2. 响应头设置:Content-Disposition="attachment;filename=文件名"
    2.1 attachment 以附加形式下载
    2.2 filename  就是下载时显示的下载文件名

导入jar包

准备下载的文件

更改springmvc的静态资源

1
2
3
//添加files文件的静态资源配置
<mvc:resources location="/files/" mapping="/files/**">
</mvc:resources>

index.jsp页面添加标签

1
<a href="download?fileName=a.txt">下载</a>

注解类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Controller
public class DemoController {

@RequestMapping("download")
public void download(String fileName,HttpServletResponse res,HttpServletRequest req) throws IOException
{
//设置响应流中文件进行下载
res.setHeader("Content-Disposition","attachment;filename="+fileName);

//把二进制流放入响应体中
ServletOutputStream os=res.getOutputStream();

//获取完整路径
String path=req.getServletContext().getRealPath("files"); //完整路径

//设置文件(路径,文件名)
File file=new File(path,fileName);
byte[] bytes=FileUtils.readFileToByteArray(file); //把一个文件导入转为二进制流
os.write(bytes); //流读入
os.flush();
os.close();

}

}

执行项目点击下载


文件上传(MultipartResovler)

文件上传具体分析

1. 基于apache的commons-fileupload.jar完成文件上传
2. MultipartResovler作用
    2.1 客户端上传的文件 -->MutipartFile封装类
    2.2 通过MutipartFile封装类获取到文件流

表单数据类型(enctype属性)

//enctype属性取值:
1. application/x-www-form-urlencoded(默认值) :普通表单数据(少量文字信息)
2. text/plain :大量文字信息(邮件/论文)
3. multipart/form-data : 表单中包含二进制文件内容

导入jar包

编写jsp页面

注解类传过去的类的对象和文件标签的name必须一致!!!

1
2
3
4
5
<form action="upload" enctype="multipart/form-data" method="post">  //enctype属性取值是表单中包含二进制文件的值
姓名:<input type="text" name="name"/><br/>
文件:<input type="file" name="file"/><br/> //一定要和注解类对象名一致!!!
<input type="submit" value="提交"/>
</form>

编写springmvc.xml文件

编写MultipartResovler解析器和异常解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- MultipartResovler解析器 -->	
<bean id="multipartResovler" class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver">
<property name="maxUploadSize" value="50"></property> //设置最大上传大小为50kb(超过我们就让弹出异常!!!)
</bean>

<!-- 异常解析器 -->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings"> //使用exceptionMappings属性
<props>
<prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">/error.jsp</prop> //设置最大上传异常的提示给error.jsp文件
</props>
</property>
</bean>

注解类

注解类MultipartFile类的对象和文件标签的name必须一致!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Controller
public class DemoController {

@RequestMapping()
public String upload(MultipartFile file,String name) throws IOException { // MultipartFile类的文件file名必须和input里面的name="file"一致

String fileName = file.getOriginalFilename(); //获取文件名
String suffix = fileName.substring(fileName.lastIndexOf(".")); //使用substring获取到.后面文件
if(suffix.equalsIgnoreCase(".png")) { //不区分大小判断
String uuid = UUID.randomUUID().toString(); //获取随机后数字
FileUtils.copyInputStreamToFile(file.getInputStream(),new File("E:/"+uuid+suffix)); //上传到E盘
return "index.jsp";
}
else{
return "error.jsp";
}

}

}

JSP九大内置对象和四大作用域

九大内置对象


四个作用域

1. page(当前页面)

2. request(一次请求中同一个对象)

3. session(一次会话)

3.1 只要客户端Cookie中传递的Jsessionid不变 --> session就不会重新实例化(不超过默认时间)
3.2 实际有效时间:
    3.2.1 浏览器关闭 : cookie失效
    3.2.2 默认时间 :tomcat的web.xml中配置

4 .application(关闭tomcat)


SpringMVC作用域传值方式(四种)

1. 使用原生Servlet(HandlerMethod参数中添加作用域对象)
2. 使用Map集合
3. 使用SpringMVC中Model接口
4. 使用SpringMVC中ModelAndView类

使用原生Servlet

在HandlerMethod参数中 + 作用域对象

注解类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Controller
public class DemoController {

@RequestMapping("demo01")
public String demo1(HttpServletRequest abc,HttpSession sessionParam) {

//request作用域
abc.setAttribute("req","req的值");

//session作用域
HttpSession session = abc.getSession();
session.setAttribute("session", "session的值");
sessionParam.setAttribute("sessionParam", "sessionParam的值");

//设置application作用域
ServletContext application=abc.getServletContext();
application.setAttribute("application","application的值");

return "index.jsp"; //返回给一个jsp页面
}
}

index.jsp页面(取出来):

1
2
3
4
request:${requestScope.req }</br>
session:${sessionScope.session }</br>
sessionParam:${sessionScope.sessionParam }</br>
application:${applicationScope.application }</br>

Map集合

map中的内容放入request作用域
spring会对map集合通过BindingAwareModelMap进行实例化

1
2
3
4
5
6
7
8
@RequestMapping("demo02")
public String demo2(Map<String,Object> map) {

System.out.println(map.getClass()); //输出map的结果
map.put("map","map的值"); //获取map值
return "index.jsp";

}

使用SpringMVC中Model接口

内容最终放入到request作用域

1
2
3
4
5
6
7
@RequestMapping("demo03")
public String demo3(Model model) {

model.addAttribute("model","model的值");
return "index.jsp";

}

使用SpringMVC中ModelAndView类

1
2
3
4
5
6
7
@RequestMapping("demo04")
public String demo4() {

ModelAndView m=new ModelAndView("index.jsp");
return "m";

}

SpringMVC实现菜单功能

前期准备

创建数据库

导入jar包

配置web.xml(四步)

1. spring的上下文参数(地址)
2. 监听器
3. springmvc的前端控制器(地址)
4. 字符编码过滤器(防止中文乱码)

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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-app_3_0.xsd">

<!-- spring的上下文参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- spring的监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- SpringMVC的前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器(防止中文乱码) -->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>

配置application.xml(七步)

1. 注解扫描
2. 加载属性文件(要写好db.properties文件)
3. 数据源
4. 扫描器
5. 事务管理器
6. 声明式事务
7. aop

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd" default-autowire="byName">

<!-- 注解扫描 -->
<context:component-scan base-package="com.bjsxt.service.impl"></context:component-scan>

<!-- 加载属性文件 -->
<context:property-placeholder location="classpath:db.properties"/>

<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

<!-- SqlSessionFactory -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bjsxt.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="factory"></property>
</bean>

<!-- 事务管理器 -->
<bean id="txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 声明式事务 -->
<tx:advice id="txAdvice" transaction-manager="txManage">
<tx:attributes>
<tx:method name="ins*"/>
<tx:method name="del*"/>
<tx:method name="upd*"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>

<!-- 配置aop -->
<aop:config>
<aop:pointcut expression="execution(* com.bjsxt.service.impl.*.*(..))" id="mypoint"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"/>
</aop:config>

</beans>

db.properties文件:

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm
jdbc.username = root
jdbc.password = njdxrjgc7777777.

配置springmvc.xml(三步)

1. 扫描注解
2. 注解驱动
3. 静态资源
4. 视图解析器    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 扫描注解 -->
<context:component-scan base-package="com.bjsxt.controller"></context:component-scan>

<!-- 注解驱动 相当于配置了两个组件 -->
<!-- org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping -->
<!-- org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter -->
<mvc:annotation-driven></mvc:annotation-driven>

<!-- 静态资源 -->
<mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
<mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
<mvc:resources location="/images/" mapping="/images/**"></mvc:resources>

<!-- 视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

</beans>

书写代码

创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Menu {
private int id;
private String name;
private int pid;
private List<Menu> children;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public List<Menu> getChildren() {
return children;
}
public void setChildren(List<Menu> children) {
this.children = children;
}

}

准备mapper下的接口和xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//准备接口写方法
public interface MenuMapper {
List<Menu> selByPid(int pid); //selByPid通过pid查询
}

//写配置文件
<?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.bjsxt.mapper.MenuMapper">

<resultMap type="menu" id="mymap"> //被调用之后
<id property="id" column="id"/> 通过id去查询
<collection property="children" select="com.bjsxt.mapper.MenuMapper.selByPid" column="id">
</collection>
</resultMap>

<select id="selByPid" parameterType="int" resultMap="mymap"> //因为list集合要通过resultMap查询
select * from menu where pid=#{0}
</select>
</mapper>

//application.xml文件 加别名
<!-- SqlSessionFactory -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="com.bjsxt.pojo"></property> //给pojo的实体类取别名
</bean>

准备service和impl实现层

准备接口展示结果:

1
2
3
4
//
public interface MenuService {
List<Menu> show(); // 展示方法
}

准备实现类

1
2
3
4
5
6
7
8
9
10
11
@Service
public class MenuServiceImpl implements MenuService {
@Resource
private MenuMapper menuMapper;

@Override
public List<Menu> show() {
return menuMapper.selByPid(0); //使用对象调方法
}

}

准备controller层(springmvc容器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller
public class MenuController {

//springmvc容器(当前类)调用spring容器中内容(impl实现类)

@Resource //通过byname查询
private MenuService menuServiceImpl;

@RequestMapping("show")
@ResponseBody
public List<Menu> show(){
return menuServiceImpl.show();
}

}

书写前端页面代码

导入js库

书写jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery-1.7.2.js"></script>
<script type="text/javascript">
$(function(){
$.post("show",function(data){
var result ="";
for(var i=0;i<data.length;i++){
result+="<dl>";
result+="<dt style='cursor:pointer'>"+data[i].name+"</dt>";
for(var j=0;j<data[i].children.length;j++){
result+="<dd>"+data[i].children[j].name+"</dd>";
}
result+="</dl>";
}
$("body").html(result);
});

//对所有父菜单添加点击事件
//live("事件名,多个事件使用空格分割",function(){})
$("dt").live("click",function(){
//slow normal fast 数值
$(this).siblings().slideToggle(1000); ; // slideToggle慢慢隐藏 dd和dt同级 (用siblings)
});

})
</script>
</head>
<body>
</body>
</html>

最终结果

直接运行界面

运行结果就会下载文件show.json

查看json文件内容


,