导航
[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署
[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend
[源码-vue06] Vue.nextTick 和 vm.$nextTick
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI
[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例
[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
[前端学java03-SpringBoot实战] lombok,日志,部署
[前端学java04-SpringBoot实战] 静态资源 + 拦截器 + 前后端文件上传
[前端学java05-SpringBoot实战] 常用注解 + redis实现统计功能
(一) 前置知识
(1) 一些单词
security 安全 保护
core 核心
archetype 原型 // create from archetype 从原型创建
conditional 条件的
final 最终 决赛 // 在java中是常量的关键字
framework 框架 架构 // spring framework => spring框架
external 外部的 // external libraries 扩展类库
(2) SpringBoot的默认错误解析
- 在
src/main/resources/templates/error/4xx.html或5xx.html会被自动解析
- 1.可以在
resources/templates
文件下 - 2.也可以在静态资源文件夹下
resources/static|public|resources|META-INF.resources 等
- 1.可以在
-
当访问不存在的页面时,会返回templates/error/4xx|5xx页面
(3) java基本数据类型
- java提供了8中基本类型
四个整数型
两个浮点型
字符型
布尔型
-
四个整数型
-
byte
8位 -
short
16位 -
int
32位 => 4个Byte -
long
64位 => 8个Byte
-
-
两个浮点型
float
double
-
字符型
char
-
布尔型
boolean
- 属性:
SIZE
TYPE
MIN_VALUE
MAX_VALUE
(4) java的引用类型
- 所有引用类型的默认值都是 null
- 一个引用变量可以用来引用任何与之兼容的类型
(5) 常量 final
- 常量通常大写
final double PI = 3.1415927;
(6) 自动类型转换
int i = 128;
byte b = (byte)i;
(7) List 和 ArrayList 的区别
List<SomeObject> myList = new ArrayList<SomeObject>();
ArrayList<SomeObject> myList = new ArrayList<SomeObject>();
区别:
(1) List是一个接口,它没有实现任何属性和方法,在List上如果调用方法,实际上是调用的 ArrayList 的方法
(2) List是一个接口,需要使用实现类,比如ArrayList
(3) 使用方式不同:Array数组使用下标获取元素,List是get
(4) 初始化:Array数组必须指定大小,不灵活。List 可以自己扩充大小,方便
(8) IOC AOP
-
IOC
控制反转/依赖注入
=> Bean对象的实例化 Bean对象的创建 -
AOP
面向切面编程
=> 动态代理 -
Spring JDBC + 事务
(二) SpringBoot常用注解
(1) @RestController
@RestController = @Controller + @ResponseBody
- 特点
- 添加该注解后,Controller类中的方法
无法返回页面
, - 相当于在方法上自动加了
@ResponseBody
注解,所以没办法跳转并传入数据到另一个页面
- 返会的内容就是
return
的内容
- 添加该注解后,Controller类中的方法
(2) @GetMapping @PostMapping @PutMappiing @DeleteMapping
(3) @PathVariable @CookieValue @RequestHeader @ReqeustAttribute @RequestParam @RequestBody @MatrixVariable
- @PathVariable 在 RESTful 风格下url中获取路径变量
// 测试 request
// 测试 @PathVariable
// 测试 @CookieValue
// 测试 @RequestHeader => 对比 @RequestParam @RequestBody @RequestPart
// 测试 @RequestAttribute
// 测试 @RequestParam
// 测试 @RequestBody
// 测试URL:http://localhost:7777/car/1/owner/woow_wu7?age=20&city=chongqing
@GetMapping("/car/{id}/owner/{username}")
public Void getPath(
HttpServletRequest request,
@PathVariable("id") int id,
@PathVariable("username") String username,
@PathVariable Map<String, String> pathVariable,
// @CookieValue("name") String name,
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String, String> headers,
@RequestParam("age") int age,
@RequestParam Map<String, String> params
// @RequestBody String body
// @RequestAttribute("message") String message
) {
log.info("@PathVariable('id') => id:{}, username: {}", id, username);
log.info("@PathVariable Map<String, String> => 可以用一个map对象,接收所有的path变量 => pv: {}", pathVariable);
String tempId = pathVariable.get("id"); // Map实例有 ( map.get ) ( map.put ) 等方法
System.out.println(tempId);
log.info("@RequestHeader('User-Agent') => User-Agent: {}", userAgent);
log.info("@RequestHeader Map<String, String> => headers: {}", headers);
log.info("@RequestParam Map<String, String> => params: {}", params);
// log.info("@CookieValue('name') => name: {}", name);
// log.info("@RequestBody String body => 可以获取post请求的body,也可以是一个Map实例,比如 @RequestBody Map<String, Object> body ====> body{}", body);
request.setAttribute("message", "success");
log.info("request: {}", request.getAttribute("message"));
return null;
}
(4) @Configuration配置类的注解 + @Bean向容器中注册组件
-
@Configuration => 可以理解为xml中的 beans 标签
- 作用:是告诉springboot标注的类是一个
配置类
- 注意点:@Configuration 标注的
配置类本身也是组件
- 参数:
@Configuration(proxyBeanMethods = true)
- 作用:是告诉springboot标注的类是一个
-
@Bean => 可以理解为xml中的 bean 标签
- 作用:
向容器中添加组件,并且添加的组件是 ( 单实例 )
- 组件ID:是
方法名
- 返回类型:就是
组件的类型
- 返回值:就是
组件在容器中的实例
@Bean(组件名) => @Bean()的参数可以重命名组件名,而不是用方法名
- 作用:
(1)
/src/main/java/com.example.demo/config/PetConfig.java
-------
// 1. 对比参考 UserConfig 类
// 2. @Configuration 标注的类是配置类,配置类本身也是组件
// 3. 外界无论对配置类中的这个注册方法调用多少次获取的都是之前容器中的单实例,前提是 @Configuration(proxyBeanMethods = true
// 4. 如果 @Configuration(proxyBeanMethods = false) 外界调用拿到的就不是 ( 代理对象 ),就 ( 不是单实例 ) 的了
@Configuration // 配置类 => 相当于以前的xml配置文件,xml中有 beans bean 标签
public class PetConfig {
@Bean // 向容器中注册组件 => @Bean(pet02)这样写可以把注册到容器中的组件重新命名为pet02,而不是用方法名pet01
public PetBean pet01() {
return new PetBean("dog", "white");
}
}
(2)
问题:如何验证组件已经注册到容器中呢?
回答:在主类中可以查看,因为主类中通过 IOC 可以通过 run.getBeanDefinitionNames() 获取到组件名数组
问题:如果获取到容器中的组件呢?
回答:run.getBean 来获取
具体代码:
// 主程序类,主配置类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 1. 返回 IOC 容器
// 2. IOC的作用是:控制反转 和 依赖注入
ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
// 3. 查看容器里的组件
String[] beanDefinitionNames = run.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
// 3. 从容器中获取组件 userX
UserBean userX1 = run.getBean("userX", UserBean.class);
UserBean userX2 = run.getBean("userX", UserBean.class);
System.out.println("组件:" + (userX1 == userX2));
System.out.println("上面是true,因为注册的组件默认就是单实例的,因为@Bean给容器注册的组件是单实例的");
// 3. 从容器中获取组件 pet01
PetBean pet01 = run.getBean("pet01", PetBean.class);
System.out.println(pet01.getName()); // 获取pet01对象中的name属性
}
}
(5) @ConditionalOnBean 条件装配注解
- @ConditionalOnBean(name) 表示IOC容器中存在 ( name组件 ) 时,才向容器中添加 ( @ConditionalOnBean ) 组件,不存在name就不添加
- @ConditionalOnBean(name) 如果标注在类上,则表示类里面所有@Bean生效的前提是:容器中有name组件
- 一般都场景启动器中就有非常多的各种条件注解,条件判断
- 在IOC中
- 1.优先解析
@Component,@Service,@Controller,@Mapper
等注解类 - 2.再解析配置类,即
@Configuration
标注的类 - 3.最后解析配置类中定义的
@Bean
- 1.优先解析
@Configuration
// @ConditionalOnBean(name = "com.example.demo.bean.ImportBean") 如果标注在类上,则表示里面所有@Bean生效的前提是容器中有com.example.demo.bean.ImportBean组件
public class TestConditionalOnBeanConfig {
// @ConditionalOnBean(name) 表示IOC容器中存在 ( name组件 ) 时,才向容器中添加 ( @ConditionalOnBean ) 组件,不存在name就不添加
// @ConditionalOnBean(name) 如果标注在类上,则表示类里面所有@Bean生效的前提是:容器中有name组件
// - 在IOC中
// - 1.优先解析 `@Component,@Service,@Controller,@Mapper`等注解类
// - 2.再解析配置类,即`@Configuration`标注的类
// - 3.最后解析配置类中定义的 `@Bean`
@ConditionalOnBean(name = "com.example.demo.bean.ImportBean")
@Bean("@ConditionalOnBean")
public TestConditionalOnBeanBean registerConditionalOnBean() {
return new TestConditionalOnBeanBean("@ConditionalOnBean");
}
}
(5) @Import 向容器中添加组件
- @Import 和 @Bean 的作用类似,都是向容器中添加组
- 语法:
@Import({ 类名.class, 类名.class... })
- 组件名:@Import导入容器中的组件名默认是 (
全类名
) - 注意点:
- @Import()必须用在组件类上,组件肯定是在springboot IOC容器中的
- @Import() 比如用在@Config @Controller @Service 等组件上都可以
- @Import()可以导入第三方包
src/main/java/com.example.demo/config/PetConfig.java
-------
// @Import
// 1. @Import 和 @Bean 的作用类似,都是向容器中添加组
// 2. @Import导入容器中的组件名默认是 ( `全类名` )
@Import({UserBean.class, PetBean.class})
@Configuration(proxyBeanMethods = true) // 配置类 => 相当于以前的xml配置文件,xml中有 beans bean 标签
public class PetConfig {
@Bean // 向容器中注册组件 => @Bean(pet02)这样写可以把注册到容器中的组件重新命名为pet02,而不是用方法名pet01
public PetBean pet01() {
return new PetBean("dog", "white");
}
}
// 以上打印的话
// @Import向容器添加的组件名是:com.example.demo.bean.PetBean
// @Bean向容器添加的组件名是:pet01
(6) @ImportResource 把传统的xml文件配置的组件添加到容器中
-
@ImportResource("classpath:beans/beans.xml")
- 参数:
classpath:beans/beans.xml
表示的是配置组件beans.xml的文件路径
- 具体:
beans.xml
被放在了src/main/resources/beans/beans.xml
中
- 参数:
(1)
src/main/java/com.example.demo/config/PetConig.java
-------
// @ImportResource
// 1. @ImportResource("classpath:beans/beans.xml")
// 2. 参数:`classpath:beans/beans.xml` 表示的是 `配置组件beans.xml的文件路径`
// 3. 具体:`beans.xml` 被放在了 `src/main/resources/beans/beans.xml` 中
@Import({UserBean.class, PetBean.class})
@Configuration(proxyBeanMethods = true) // 配置类 => 相当于以前的xml配置文件,xml中有 beans bean 标签
@ImportResource("classpath:beans/beans.xml")
public class PetConfig {
@Bean // 向容器中注册组件 => @Bean(pet02)这样写可以把注册到容器中的组件重新命名为pet02,而不是用方法名pet01
public PetBean pet01() {
return new PetBean("dog", "white");
}
}
(2)
src/main/resources/beans/beans.xml
-------
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--UserBean-->
<bean id="haha" class="com.example.demo.bean.UserBean">
<property name="name" value="zhangsan"></property>
<property name="age" value="20"></property>
</bean>
<!--PetBean-->
<bean id="hehe" class="com.example.demo.bean.PetBean">
<property name="name" value="dog"></property>
<property name="color" value="red"></property>
</bean>
</beans>
(7) @ConfigurationProperties + @Component 实现配置绑定
@ConfigurationProperties(prefix = "myapp")
-
@Compoennt
:的作用是将类组件添加到容器中
(1)
src/main/java/com.example.demo/bean/AppMessageBean.java
-------
// 1. 只有在容器中的组件才能获取 SpringBoot 的强大功能,也就是说要使用@ConfigurationProperties()必须用@Component将对象标记成容器组件
// 2. 该 bean 对象主要是测试 @ConfigurationProperties 和 @Component 两个注解
// 3. 如果类中的属性多于application.yml文件中的myapp的话,多的属性返回的是null
@Data
@Component
@ConfigurationProperties(prefix = "myapp") // prefix="myapp" 这个前缀的值是在 application.yml 文件中配置的
public class AppMessageBean {
private String name;
private String email;
private String author;
private String other;
}
(2)
src/main/resources/application.yml
-------
myapp:
# 自定义的配置参数,这里主要用来验证 @ConfigurationProperties 注解的使用
author: woow_wu7
name: react-admin-java
email: woow.wu7@gmail.com
(3)
src/main/java/com.example.demo/controller/TestController.java
-------
@Autowired
AppMessageBean appMessageBean;
// (1)
// 测试: @ConfigurationProperties 和 @Component 两个注解
// 教程: https://www.cnblogs.com/jimoer/p/11374229.html
@GetMapping("/@ConfigurationProperties")
public AppMessageBean getAuthorName() {
System.out.println(appMessageBean);
String author = appMessageBean.getAuthor();
System.out.println(author);
return appMessageBean;
}
(8) @SpringBootTest + @Test 单元测试
- 1.引入maven场景启动器
spring-boot-starter-test
<!-- spring-boot-starter-test -->
<!-- 单元测试场景启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- 2.编译一个测试类
src/test/java/com.example.demo/ApplicationTests.java
-------
@SpringBootTest
class ApplicationTests {
@Test
void contextLoads() { dosomething...}
}
(三) 修改maven依赖包的版本号的两种方法
以mysql为例
(1) 在 pom.xml 中通过 version 标签来指定 ( 原理是maven的就近依赖原则 )
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
<scope>runtime</scope>
</dependency>
(2) 在 pom.xml 中通过 properties 标签来修改 ( 原理是maven属性的就近优先原则 )
<properties>
<java.version>1.8</java.version>
<!-- 除了修改version还可以在properties中来修改依赖的版本号 -->
<!-- <mysql.version>8.0.21</mysql.version> -->
</properties>
(四) 数据访问
(1) mysql驱动 + JDBC数据库连接池 = springboot最基础的操作mysql的方式
在这基础上还可以添加 mybatis 来操作数据库
- 1.安装:mysql驱动 => mysql-connector-java
- 2.安装:jdbc数据库连接池 => spring-boot-starter-data-jdbc
( 区分:spring-boot-starter-jdbc )
- 3.配置项:spring.datasource...
- 4.数据源:
HikariDataSource
spring:
datasource:
# 1. 只要装了 ( mysql驱动 ) 和 ( jdbc数据库连接池 ),并且在这里配置好 ( 数据库连接池相关的配置项 ) 就能连接数据库
# 2. mysql驱动 => mysql-connector-java
# 3. jdbc连接池 => spring-boot-starter-jdbc
# 4. 更进一步:还可以使用 ( Druid数据源 + MyBatis )
url: jdbc:mysql://localhost:3306/7-react-admin-java?serverTimezone=GMT%2B8&useSSL=false
username: root
password: root
dirver-class-name: com.mysql.cj.jdbc.Driver
jdbc:
template:
query-timeout: 10 # 10s没查出来就超时
- 4.通过springBoot容器中的 JdbcTemplate 就行操作数据库 ( 单元测试 )
src/test/com.example.demo/ApplicationTests.java
-------
@SpringBootTest
@Slf4j
class ApplicationTests {
@Autowired
JdbcTemplate jdbcTemplate; // 自动注入容器中的 JdbcTemplate
@Test
void contextLoads() {
Long aLong = jdbcTemplate.queryForObject("select count(*) from music", Long.class); // 操作数据库
log.info("music总数据量:{}", aLong);
}
}
(2) HikariDataSource 和 Druid数据源 => 滤过Druid数据源
(五) Redis
Redis是一个开源的,( 内存中
) 的 ( 数据结构存储系统
),它可以用作 ( 数据库
) ( 缓存
) ( 消息中间件
)
(1) 导入maven的依赖包,即redis的场景启动器
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
(2) 查看springboot的autoconfigure中的redis的 ( 自动配置类 )
- 目录:
External Libraries/spring-boot-autoconfigure:2.4.2/data/redis/RedisAutoConfiguration.java
- external 外部的意思
- external libraries 扩展类库的意思
-
RedisAutoConfiguration.java (1)
- redis自动配置:
- @EnableConfigurationProperties(RedisProperties.class) (2)
- spring.redis.xxx (3)
RedisAutoConfiguration.java => @EnableConfigurationProperties(RedisProperties.class) => spring.redis.xxx是对redis的配置,也就是说可以在application.yml中去做redis相关的配置,比如 spring.redis.xxx
- 连接工厂是准备好的 ( 两种 )
- Lettuce => LettuceConnectionConfiguration
- Jedis => JedisConnectionConfiguration
- 操作redis
- 自动注入了下面两个组件类
-
RedisTemplate<Object, Object>
- key: value
- 对比 JdbcTemplate
-
StringRedisTemplate
- key 和 value 都是 String
- redis自动配置:
(3) 申请一个阿里云的 redis 服务器,并申请redis公网连接地址,和设置白名单,用户权限(读写)等
(4) 下载安装 redis 和 redis客户端
- 下载连接
- redis下载安装教程
- windows系统redis客户端下载连接
- redis常用命令
-
redis的使用
- 1.在windows系统上安装完成后,在解压的目录下双击
redis-cli.exe
运行 - 2.除了双击运行,也可以在该文件夹下打开cmd,输入
./redis-cli.exe
命令
- 1.在windows系统上安装完成后,在解压的目录下双击
-
redis常用命令
-
获取读写权限
auth username:password
auth password
-
是否禁用redis
config set disable [yes|no]
-
查询默认密码 ( 登陆后才可以操作 )
- config get requirepass
-
修改密码 ( 登陆后才可以操作 )
- config set requirepass 123456
-
登陆redis
- ./redis-cli -p 6379 -a 123456
-
启动服务
- ./redis-server --service-start
-
停止服务
- ./redis-server --service-stop
-
string
-
set key value
设置键值 -
get key
获取键值 -
del key
删除键值 -
mget key1
key2 同时获取 -
exists key
检查key是否存在
-
-
list => 是链表
-
lpush key value1 value2
在key所关联的list的头部 ( 插入 ) 所有value值 -
rpush key value1 value2
在key所关联的list的头部 ( 插入 ) 所有value值 -
lrange key start end
获取key链表中从start到end的元素的值,start、end可 为负数,若为-1则表示链表尾部的元素 -
lpushx key value
当key存在时才leftpush,如果不加x不存在时会新建 -
lpop key
返回并弹出指定的key关联的链表中的第一个元素,即头部元素
-
-
set => 是数组加链表
-
sadd key value1 value2
向set中添加数据,如果该key的值已有则不会 重复添加 l` -
smembers key
获取set中所有的成员 -
scard key
获取set中成员的数量 -
srem key member1、member2
删除set中指定的成员
-
-
zset
- zset中的每一个成员都会有一个分 数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序
-
zadd key score member score2 member2
将所有成员以及该成员的 分数存放到sorted-set中
-
hash
- Redis中的Hashes类型可以看成具有String Key和String Value的map容器,所以适合存储值是对象的信息,比如 username
-
hset key fild value
为指定的key设定field/value对(键值对) -
hgetall key
获取key中的所有filed-vaule
-
获取读写权限
(5) 在application.properties 或 applicatioin.yml中配置 redis
spring:
redis:
# 可以通过 (external libraries)/spring-boot-autoconfigure:2.4.2/data/redis/RedisAutoConfiguration/RedisProperties/url 来查看
# url = redis://user:password@example.com:6379
host: r-bp1z4zrytbuyv7mkuzpd.redis.rds.aliyuncs.com
port: 6379
password: woow_wu7:ALy123456789
(6) 在springboot的测试类中测试 读写redis
src/test/java/com.example.demo/ApplicationTests.java
-------
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RedisTemplate redisTemplate;
@Test
void testRedis() {
ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
stringStringValueOperations.set("redis", "ok"); // string set
String redis = stringStringValueOperations.get("redis"); // string get
log.info("redis: {}", redis);
}
(7) 从 Lettuce 切换到 Jedis
- springboot中默认使用的是 Lettuce,如何切换成 Jedis
(1) 导入依赖
<!-- jedis -->
<!-- SpringBoot默认使用的是Lettuce,要切换成jedis就必须安装该依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
(2) 修改配置
redis:
# 可以通过 (external libraries)/spring-boot-autoconfigure:2.4.2/data/redis/RedisAutoConfiguration/RedisProperties/url 来查看
# url = redis://user:password@example.com:6379
host: r-bp1z4zrytbuyv7mkuzpd.redis.rds.aliyuncs.com
port: 6379
password: woow_wu7:ALy123456789
client-type: jedis #两种,默认是 Lettuce
(3) 测试
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Test
void testRedis() {
ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
stringStringValueOperations.set("redis", "ok");
String redis = stringStringValueOperations.get("redis");
log.info("redis: {}", redis);
// 打印 redis 的连接工厂是用的 Lettuce 还是 jedis
log.info(String.valueOf(redisConnectionFactory.getClass()));
}
(8) 实现一个小功能 => 统计每个url访问的次数
- 在springboot连接好redis后
- 原理
- 1.通过 Interceptor
拦截
除去登陆,静态资源外的所有请求
- 2.在拦截器中实现 HandlerInterceptor 接口的前置钩子
preHandle
方法 - 3.在 preHandle 方法中通过获取自动注入的
StringRedisTemplate
的opsForValue()
的increment()
方法实现统计 - 4.redis => 中的
increment(url)方法,key=url, value=计数
- 5.拦截器通过
@Component
添加到容器中,在配置类中实现WebMvcConfigurer
接口的addInterceptors
方法实现拦截
- 1.通过 Interceptor
- 详细步骤
(1)
src/mian/java/com.example.demo/interceptor/RedisUrlCountInteceptor.java
-------
/**
* Interceptor 和 Filter 都具有相同的功能
* 区别:
* 1. Filter: 是Servlet定义的原生组件,好处是脱离spring也能使用
* 2. Interceptor: 是spring定义的接口,只能在spring中使用,可以使用Spring的 ( 自动装配 ) 等功能
*/
@Component // 将拦截器放到容器中
public class RedisUrlCountInterceptor implements HandlerInterceptor {
@Autowired
StringRedisTemplate stringRedisTemplate; // 自动注入操作redis的容器中的组件类
// preHandle 前置钩子
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
// stringRedisTemplate.opsForValue().increment(url) => 该方法每次访问该地址,url计数都会+1
stringRedisTemplate.opsForValue().increment(requestURI);
return true;
}
}
(2)
src/main/java/com.example.demo/config/AdminWebConfig.java
-------
/**
* 拦截器
* 1. 编写一个拦截器,实现 HandlerInterceptor 接口
* 2. 把拦截器注册到容器中 ( 实现 WebMvcConfigurer 接口的 addInterceptors 方法)
* 3. 指定拦截规则 【如果拦截所有,静态资源也会被拦截,可以用 excludePathPatterns 方法放行】
*/
// @Configuration 用于定义 ( 配置类 )
@Configuration
@EnableConfigurationProperties
public class AdminWebConfig implements WebMvcConfigurer {
// 这里之所以可以自动注入,是因为RedisUrlCountInterceptor类通过@Compoent注册到容器中了,非配置类
@Autowired
RedisUrlCountInterceptor redisUrlCountInterceptor;
// @Override表示被标注的方法是一个重写方法
// override 覆盖
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(redisUrlCountInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/", "/login", "css/**", "/fonts/**", "/images/**", "/js/**");
}
}