此文章书写于 刚学习并练习过拦截器 及时进行记录,知识点并不充足,如有错误,还请见谅,请指出
我们想要自定义拦截对应的请求,放开对应的请求,或者说对一些请求进行加签,因此我们可以创建一个自定义的拦截器
什么?你不知道拦截器是什么想要赶紧去搜一下拦截器是什么?NO NO NO,我们先来使用拦截器做些什么再去对其进行更深入的了解。
我们先建一个 SpringBoot 项目
选择自己所需的 依赖(不选也可以,等下我直接把我的pom文件粘贴上来)
因为我已经试过一遍了,所以我就不创建了
各位根据这个层级目录进行创建
下面是一些代码
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--springboot+mybatis的依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--MySQL数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid数据库连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<!--Lombok依赖(可以配置也可以不用配置具体看自己)-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
application.yml
server:
#设置端口号
port: 8081 #默认端口是8080
spring:
datasource:
#数据库用户名
username: root
#数据库用户密码
password: 123456
#serverTimezone=UTC 解决市区的报错 一般mysql是8.0以上的是必须配置这个
#userUnicode=true&characterEncoding=utf-8 指定字符编码、解码格式
url: jdbc:mysql://localhost:3306/sauro?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8
#设置驱动类
driver-class-name: com.mysql.cj.jdbc.Driver
#设置数据源
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 配置mybatis
mybatis:
#指定pojo扫描包位置让mybatis自动扫描到指定义的pojo包下
type-aliases-package: com.me.test.pojo
#指定位置扫描Mapper接口对应的XML文件 classpath:xml文件位置
mapper-locations: classpath:mapper/*.xml
model(pojo)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
private Integer id;
private String username;
private String password;
private String authority;
}
service
public interface UserInfoService {
/**
* 增加一条数据
* @param userInfo 数据
*/
void add(UserInfo userInfo);
/**
* 删除一条数据
* @param id 被删除数据的id
*/
void delete(Integer id);
/**
* 修改一条数据
* @param userInfo 修改的数据
*/
void update(UserInfo userInfo);
/**
* 根据id去查询一条数据
* @param id 查询的id
*/
UserInfo queryById(Integer id);
/**
* 查询全部数据
* @return
*/
List<UserInfo> queryAll();
}
serviceImpl
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Autowired
private UserInfoMapper userInfoMapper;
@Override
public void add(UserInfo userInfo) {
userInfoMapper.add(userInfo);
}
@Override
public void delete(Integer id) {
userInfoMapper.delete(id);
}
@Override
public void update(UserInfo userInfo) {
userInfoMapper.update(userInfo);
}
@Override
public UserInfo queryById(Integer id) {
return userInfoMapper.queryById(id);
}
@Override
public List<UserInfo> queryAll() {
return userInfoMapper.queryAll();
}
}
mapper
@Repository
@Mapper
public interface UserInfoMapper {
/**
* 增加一条数据
* @param userInfo 数据
*/
void add(UserInfo userInfo);
/**
* 删除一条数据
* @param id 被删除数据的id
*/
void delete(Integer id);
/**
* 修改一条数据
* @param userInfo 修改的数据
*/
void update(UserInfo userInfo);
/**
* 根据id去查询一条数据
* @param id 查询的id
*/
UserInfo queryById(Integer id);
/**
* 查询全部数据
* @return
*/
List<UserInfo> queryAll();
}
mapper.xml
<?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.sauro.study.mapper.UserInfoMapper">
<insert id="add" parameterType="com.sauro.study.model.UserInfo">
insert into userinfo (username, password, authority)
values (#{username},#{password},#{authority});
</insert>
<delete id="delete" parameterType="Integer">
delete from userinfo where id = #{id};
</delete>
<update id="update" parameterType="com.sauro.study.model.UserInfo">
update userinfo set username=#{username},password=#{password},authority=#{authority}
where id=#{id};
</update>
<select id="queryById" parameterType="Integer" resultType="com.sauro.study.model.UserInfo">
select * from userinfo where id=#{id};
</select>
<select id="queryAll" resultType="com.sauro.study.model.UserInfo">
select * from userinfo;
</select>
</mapper>
controller
@Controller
@RequestMapping(value = "/test")
public class UserInfoController {
@Autowired
private UserInfoService userInfoService;
@GetMapping
@ResponseBody
public String queryAll() {
List<UserInfo> userInfoList = userInfoService.queryAll();
return JSON.toJSONString(userInfoList);
}
@GetMapping(" /{id}")
@ResponseBody
public String query(@PathVariable(value = "id") Integer id) {
UserInfo userInfo = userInfoService.queryById(id);
List<UserInfo> userInfoList = new ArrayList<>();
userInfoList.add(userInfo);
return JSON.toJSONString(userInfoList);
}
// 这个如果按照RESTful的风格创建不应该加上这个请求路径的,但是为了后面的学习,我们还是给他加上
@PostMapping("/post/add")
@ResponseBody
public String add(@RequestBody UserInfo userInfo) {
System.out.println("此请求没有经过拦截器");
userInfoService.add(userInfo);
return "添加OK";
}
@DeleteMapping(value = "/delete/{id}")
@ResponseBody
public String delete(@PathVariable("id") Integer id) {
userInfoService.delete(id);
return "删除成功";
}
@PutMapping("/update/{id}")
@ResponseBody
public String update(@PathVariable("id") Integer id, @RequestBody UserInfo userInfo) {
userInfo.setId(id);
userInfoService.update(userInfo);
return "修改成功";
}
}
实操
项目创建完成,我们正式开始我们的拦截器学习
新建一个拦截器类在对应的包下(可以创建一个拦截器的包)
自定义拦截器我们需要实现 HandlerInterceptor 接口 ,或者继承 HandlerInterceptorAdapter 类
然后我们对 相关接口或者类中的方法进行 实现或是重写
基于接口的
public class ProjectRequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
基于类的
public class ProjectRequestInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}
}
这里我采用类的方式对其进行学习
再每个方法里面输出对应的语句,便于我们运行的时候进行区分
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
super.afterCompletion(request, response, handler, ex);
}
这里只是编写了一个拦截器,具体的拦截器使用我们在一个新的配置文件中进行配置
新建一个配置类在 config包下,这个配置类需要实现 webMvcConfigurer 接口
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {
// 在配置类里我们把刚刚写的拦截器 放入Spring容器当中
// ProjectRequestInterceptor 是我刚刚创建的拦截器的文件名
@Bean
public ProjectRequestInterceptor projectRequestInterceptor() {
return new ProjectRequestInterceptor();
}
// 在 webMvcConfigurer接口中存在很多个方法,我们目前仅仅 实现 addInterceptors 方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 参数为我们刚刚创建的拦截器对象
registry.addInterceptor(projectRequestInterceptor())
// 这行代码代表 所有的请求,都需要进入 对应的拦截器当中执行对应的逻辑
.addPathPatterns("/**")
// 这行代码代表 这个请求不经过拦截器
.excludePathPatterns("/test/post/add");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
下面我们在刚刚创建的拦截器中打上断点,并在对应请求上加上一个断点
发送请求
我们发现,这个请求首先走到了 preHandle 这个方法
放开第一个断点,才走到了我们请求对应的controller上。继续放开断点
走到了 postHandle ,再次放开断点,走到了 afterCompletion
控制台的输出顺序也是
并且postMan也是可以获得数据的。
因此我们可以的出 ,在拦截器当中这几个方法的执行顺序是 preHandle --> postHandle --> afterCompletion
并且我们注意到 preHandle 这个方法是有返回值的,我们可以尝试下在这个方法中直接返回 false 是什么情况
重启项目,再次发送请求
我们发现,这个请求仍然是首先执行 preHandle 接口,我们放开这个断点,发现请求停止了。且postMan并没有返回相关数据。
我们刚才还特意配置了一个post/add 接口,来让我们看下有什么样的效果(记得在对应的controller上打上断点)
发送请求,我们发现他并没有走到 拦截器中的 preHandle 方法中
因此我们可以得出,preHandle这段代码决定了 配置了被拦截的路径请求 是否继续向下运行。
那么我们可以使用拦截器做些什么呢?
下面我们改造一下 preHandle中的代码。
再次发送get请求,发现不能成功走到正确的 controller中
让我们修改一下 相关的请求
再次发送请求发现可以正确的发送请求
由此我们可以对一些接口进行加签的设置,例如访问一些接口需要一些特定的参数,以此来防止被恶意访问或者说限制一些人对此接口进行调用。