整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          mybatis攔截器實現主鍵自動生成

          mybatis攔截器實現主鍵自動生成

          前陣子和朋友聊天,他說他們項目有個需求,要實現主鍵自動生成,不想每次新增的時候,都手動設置主鍵。于是我就問他,那你們數據庫表設置主鍵自動遞增不就得了。他的回答是他們項目目前的id都是采用雪花算法來生成,因此為了項目穩定性,不會切換id的生成方式。

          朋友問我有沒有什么實現思路,他們公司的orm框架是mybatis,我就建議他說,不然讓你老大把mybatis切換成mybatis-plus。mybatis-plus就支持注解式的id自動生成,而且mybatis-plus只是對mybatis進行增強不做改變。朋友還是那句話,說為了項目穩定,之前項目組沒有使用mybatis-plus的經驗,貿然切換不知道會不會有什么坑。后面沒招了,我就跟他說不然你用mybatis的攔截器實現一個吧。于是又有一篇吹水的創作題材出現。

          前置知識

          在介紹如何通過mybatis攔截器實現主鍵自動生成之前,我們先來梳理一些知識點

          1、mybatis攔截器的作用

          mybatis攔截器設計的初衷就是為了供用戶在某些時候可以實現自己的邏輯而不必去動mybatis固有的邏輯

          2、Interceptor攔截器

          每個自定義攔截器都要實現

          org.apache.ibatis.plugin.Interceptor
          

          這個接口,并且自定義攔截器類上添加@Intercepts注解

          3、攔截器能攔截哪些類型

          Executor:攔截執行器的方法。
          ParameterHandler:攔截參數的處理。
          ResultHandler:攔截結果集的處理。
          StatementHandler:攔截Sql語法構建的處理。

          4、攔截的順序

          a、不同類型攔截器的執行順序

          Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler
          

          b、多個攔截器攔截同種類型同一個目標方法,執行順序是后配置的攔截器先執行

          比如在mybatis配置如下

          	<plugins>
          		<plugin interceptor="com.lybgeek.InterceptorA" />
          		<plugin interceptor="com.lybgeek.InterceptorB" />
          	</plugins>
          

          則InterceptorB先執行。

          如果是和spring做了集成,先注入spring ioc容器的攔截器,則后執行。比如有個mybatisConfig,里面有如下攔截器bean配置

           @Bean
              public InterceptorA interceptorA(){
                  return new InterceptorA();
              }
          
              @Bean
              public InterceptorB interceptorB(){
                  return new InterceptorB();
              }
          

          則InterceptorB先執行。當然如果你是直接用@Component注解這形式,則可以配合@Order注解來控制加載順序

          5、攔截器注解介紹

          @Intercepts:標識該類是一個攔截器

          @Signature:指明自定義攔截器需要攔截哪一個類型,哪一個方法。
          @Signature注解屬性中的type表示對應可以攔截四種類型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的一種;method表示對應類型(Executor、ParameterHandler、ResultHandler、StatementHandler)中的哪類方法;args表示對應method中的參數類型

          6、攔截器方法介紹

          a、 intercept方法

          public Object intercept(Invocation invocation) throws Throwable
          

          這個方法就是我們來執行我們自己想實現的業務邏輯,比如我們的主鍵自動生成邏輯就是在這邊實現。

          Invocation這個類中的成員屬性target就是@Signature中的type;method就是@Signature中的method;args就是@Signature中的args參數類型的具體實例對象

          b、 plugin方法

          public Object plugin(Object target)
          

          這個是用返回代理對象或者是原生代理對象,如果你要返回代理對象,則返回值可以設置為

          Plugin.wrap(target, this);
          this為攔截器
          

          如果返回是代理對象,則會執行攔截器的業務邏輯,如果直接返回target,就是沒有攔截器的業務邏輯。說白了就是告訴mybatis是不是要進行攔截,如果要攔截,就生成代理對象,不攔截是生成原生對象

          c、 setProperties方法

          public void setProperties(Properties properties)
          

          用于在Mybatis配置文件中指定一些屬性

          主鍵自動生成思路

          1、定義一個攔截器

          主要攔截

           `Executor#update(MappedStatement ms, Object parameter)`} 
          

          這個方法。mybatis的insert、update、delete都是通過這個方法,因此我們通過攔截這個這方法,來實現主鍵自動生成。其代碼塊如下

          @Intercepts(value={@Signature(type=Executor.class,method="update",args={MappedStatement.class,Object.class})})
          public class AutoIdInterceptor implements Interceptor {}
          

          2、判斷sql操作類型

          Executor 提供的方法中,update 包含了 新增,修改和刪除類型,無法直接區分,需要借助 MappedStatement 類的屬性 SqlCommandType 來進行判斷,該類包含了所有的操作類型

          public enum SqlCommandType {
            UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
          }
          
          

          當SqlCommandType類型是insert我們才進行主鍵自增操作

          3、填充主鍵值

          3.1、編寫自動生成id注解

          Target(ElementType.FIELD)
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface AutoId {
              /**
               * 主鍵名
               * @return
               */
              String primaryKey();
          
              /**
               * 支持的主鍵算法類型
               * @return
               */
              IdType type() default IdType.SNOWFLAKE;
          
              enum IdType{
                  SNOWFLAKE
              }
          }
          

          3.2、 雪花算法實現

          我們可以直接拿hutool這個工具包提供的idUtil來直接實現算法。

          引入

           <dependency>
                      <groupId>cn.hutool</groupId>
                      <artifactId>hutool-all</artifactId>
                  </dependency>
          
          Snowflake snowflake=IdUtil.createSnowflake(0,0);
          long value=snowflake.nextId();
          

          3.3、填充主鍵值

          其實現核心是利用反射。其核心代碼片段如下

             ReflectionUtils.doWithFields(entity.getClass(), field->{
                              ReflectionUtils.makeAccessible(field);
                              AutoId autoId=field.getAnnotation(AutoId.class);
                              if(!ObjectUtils.isEmpty(autoId) && (field.getType().isAssignableFrom(Long.class))){
                                  switch (autoId.type()){
                                      case SNOWFLAKE:
                                          SnowFlakeAutoIdProcess snowFlakeAutoIdProcess=new SnowFlakeAutoIdProcess(field);
                                          snowFlakeAutoIdProcess.setPrimaryKey(autoId.primaryKey());
                                          finalIdProcesses.add(snowFlakeAutoIdProcess);
                                          break;
                                  }
                              }
                          });
          
          public class SnowFlakeAutoIdProcess extends BaseAutoIdProcess {
          
              private static Snowflake snowflake=IdUtil.createSnowflake(0,0);
          
          
              public SnowFlakeAutoIdProcess(Field field) {
                  super(field);
              }
          
              @Override
              void setFieldValue(Object entity) throws Exception{
                  long value=snowflake.nextId();
                  field.set(entity,value);
              }
          }
          

          如果項目中的mapper.xml已經的insert語句已經含有id,比如

          insert into sys_test( `id`,`type`, `url`,`menu_type`,`gmt_create`)values( #{id},#{type}, #{url},#{menuType},#{gmtCreate})
          

          則只需到填充id值這一步。攔截器的任務就完成。如果mapper.xml的insert不含id,形如

          insert into sys_test( `type`, `url`,`menu_type`,`gmt_create`)values( #{type}, #{url},#{menuType},#{gmtCreate})
          

          則還需重寫insert語句以及新增id參數

          4、重寫insert語句以及新增id參數(可選)

          4.1 重寫insert語句

          方法一:
          從 MappedStatement 對象中獲取 SqlSource 對象,再從從 SqlSource 對象中獲取獲取 BoundSql 對象,通過 BoundSql#getSql 方法獲取原始的sql,最后在原始sql的基礎上追加id

          方法二:

          引入

          <dependency>
          			<groupId>com.alibaba</groupId>
          			<artifactId>druid</artifactId>
          			<version>${druid.version}</version>
          		</dependency>
          

          通過

          com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser
          

          獲取相應的表名、需要insert的字段名。然后重新拼湊出新的insert語句

          4.2 把新的sql重置給Invocation

          其核心實現思路是創建一個新的MappedStatement,新的MappedStatement綁定新sql,再把新的MappedStatement賦值給Invocation的args[0],代碼片段如下

           private void resetSql2Invocation(Invocation invocation, BoundSqlHelper boundSqlHelper,Object entity) throws SQLException {
                  final Object[] args=invocation.getArgs();
                  MappedStatement statement=(MappedStatement) args[0];
                  MappedStatement newStatement=newMappedStatement(statement, new BoundSqlSqlSource(boundSqlHelper));
                  MetaObject msObject=MetaObject.forObject(newStatement, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(),new DefaultReflectorFactory());
                  msObject.setValue("sqlSource.boundSqlHelper.boundSql.sql", boundSqlHelper.getSql());
          
                      args[0]=newStatement;
          
              }
          

          4.3 新增id參數

          其核心是利用

          org.apache.ibatis.mapping.ParameterMapping
          

          核心代碼片段如下

            private void setPrimaryKeyParaterMapping(String primaryKey) {
                     ParameterMapping parameterMapping=new ParameterMapping.Builder(boundSqlHelper.getConfiguration(),primaryKey,boundSqlHelper.getTypeHandler()).build();
                     boundSqlHelper.getBoundSql().getParameterMappings().add(parameterMapping);
                 }
          

          5、將mybatis攔截器注入到spring容器

          可以直接在攔截器上加

          @org.springframework.stereotype.Component
          

          注解。也可以通過

           @Bean
              public AutoIdInterceptor autoIdInterceptor(){
                  return new AutoIdInterceptor();
              }
          

          6、在需要實現自增主鍵的實體字段上加如下注解

          @AutoId(primaryKey="id")
          	private Long id;
          

          測試

          1、對應的測試實體以及單元測試代碼如下

          @Data
          public class TestDO implements Serializable {
          	private static final long serialVersionUID=1L;
          
          	@AutoId(primaryKey="id")
          	private Long id;
          	private Integer type;
          	private String url;
          	private Date gmtCreate;
          	private String menuType;
          }
          
          @Autowired
              private TestService testService;
          
              @Test
              public void testAdd(){
                  TestDO testDO=new TestDO();
                  testDO.setType(1);
                  testDO.setMenuType("1");
                  testDO.setUrl("www.test.com");
                  testDO.setGmtCreate(new Date());
                  testService.save(testDO);
                  testService.get(110L);
              }
          
              @Test
              public void testBatch(){
                  List<TestDO> testDOList=new ArrayList<>();
                  for (int i=0; i < 3; i++) {
                      TestDO testDO=new TestDO();
                      testDO.setType(i);
                      testDO.setMenuType(i+"");
                      testDO.setUrl("www.test"+i+".com");
                      testDO.setGmtCreate(new Date());
                      testDOList.add(testDO);
                  }
          
                  testService.saveBatch(testDOList);
              }
          

          2、當mapper的insert語句中含有id,形如下

          <insert id="save" parameterType="com.lybgeek.TestDO" useGeneratedKeys="true" keyProperty="id">
          		insert into sys_test(`id`,`type`, `url`,`menu_type`,`gmt_create`)
          		values( #{id},#{type}, #{url},#{menuType},#{gmtCreate})
          	</insert>
          

          以及批量插入sql

          <insert id="saveBatch"  parameterType="java.util.List" useGeneratedKeys="false">
          		insert into sys_test( `id`,`gmt_create`,`type`,`url`,`menu_type`)
          		values
          		<foreach collection="list" item="test" index="index" separator=",">
          			( #{test.id},#{test.gmtCreate},#{test.type}, #{test.url},
          			#{test.menuType})
          		</foreach>
          	</insert>
          

          查看控制臺sql打印語句

          15:52:04 [main] DEBUG com.lybgeek.dao.TestDao.save -==>  Preparing: insert into sys_test(`id`,`type`, `url`,`menu_type`,`gmt_create`) values( ?,?, ?,?,? ) 
          15:52:04 [main] DEBUG com.lybgeek.dao.TestDao.save -==> Parameters: 356829258376544258(Long), 1(Integer), www.test.com(String), 1(String), 2020-09-11 15:52:04.738(Timestamp)
          15:52:04 [main] DEBUG com.nlybgeek.dao.TestDao.save - <==Updates: 1
          
          15:52:04 [main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch -==>  Preparing: insert into sys_test( `id`,`gmt_create`,`type`,`url`,`menu_type`) values ( ?,?,?, ?, ?) , ( ?,?,?, ?, ?) , ( ?,?,?, ?, ?) 
          15:52:04 [main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch -==> Parameters: 356829258896637961(Long), 2020-09-11 15:52:04.847(Timestamp), 0(Integer), www.test0.com(String), 0(String), 356829258896637960(Long), 2020-09-11 15:52:04.847(Timestamp), 1(Integer), www.test1.com(String), 1(String), 356829258896637962(Long), 2020-09-11 15:52:04.847(Timestamp), 2(Integer), www.test2.com(String), 2(String)
          15:52:04 [main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch - <==Updates: 3
          

          查看數據庫

          3、當mapper的insert語句中不含id,形如下

          <insert id="save" parameterType="com.lybgeek.TestDO" useGeneratedKeys="true" keyProperty="id">
          		insert into sys_test(`type`, `url`,`menu_type`,`gmt_create`)
          		values(#{type}, #{url},#{menuType},#{gmtCreate})
          	</insert>
          

          以及批量插入sql

          <insert id="saveBatch"  parameterType="java.util.List" useGeneratedKeys="false">
          		insert into sys_test(`gmt_create`,`type`,`url`,`menu_type`)
          		values
          		<foreach collection="list" item="test" index="index" separator=",">
          			(#{test.gmtCreate},#{test.type}, #{test.url},
          			#{test.menuType})
          		</foreach>
          	</insert>
          
          

          查看控制臺sql打印語句

          15:59:46 [main] DEBUG com.lybgeek.dao.TestDao.save -==>  Preparing: insert into sys_test(`type`,`url`,`menu_type`,`gmt_create`,id) values (?,?,?,?,?) 
          15:59:46 [main] DEBUG com.lybgeek.dao.TestDao.save -==> Parameters: 1(Integer), www.test.com(String), 1(String), 2020-09-11 15:59:46.741(Timestamp), 356831196144992264(Long)
          15:59:46 [main] DEBUG com.lybgeek.dao.TestDao.save - <==Updates: 1
          
          15:59:46 [main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch -==>  Preparing: insert into sys_test(`gmt_create`,`type`,`url`,`menu_type`,id) values (?,?,?,?,?),(?,?,?,?,?),(?,?,?,?,?) 
          15:59:46 [main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch -==> Parameters: 2020-09-11 15:59:46.845(Timestamp), 0(Integer), www.test0.com(String), 0(String), 356831196635725829(Long), 2020-09-11 15:59:46.845(Timestamp), 1(Integer), www.test1.com(String), 1(String), 356831196635725828(Long), 2020-09-11 15:59:46.845(Timestamp), 2(Integer), www.test2.com(String), 2(String), 356831196635725830(Long)
          15:59:46 [main] DEBUG c.n.lybgeek.dao.TestDao.saveBatch - <==Updates: 3
          

          從控制臺我們可以看出,當mapper.xml沒有配置id字段時,則攔截器會自動幫我們追加id字段

          查看數據庫

          總結

          本文雖然是介紹mybatis攔截器實現主鍵自動生成,但文中更多講解如何實現一個攔截器以及主鍵生成思路,并沒把intercept實現主鍵方法貼出來。其原因主要是主鍵自動生成在mybatis-plus里面就有實現,其次是有思路后,大家就可以自己實現了。最后對具體實現感興趣的朋友,可以查看文末中demo鏈接

          參考文檔

          https://www.cnblogs.com/chenchen127/p/12111159.html
          https://blog.csdn.net/hncaoyuqi/article/details/103187983
          https://blog.csdn.net/zsj777/article/details/81986096

          demo鏈接

          https://github.com/lyb-geek/springboot-learning/tree/master/springboot-mybatis-autoId

          . 登錄頁面的jsp

          login.jsp

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
          <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <%
              String path=request.getContextPath();
              String basePath=request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
          %>
          <!DOCTYPE html PUBLIC "-//W3C//D 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>登錄頁面</title>
              <base href="<%=basePath %>">
              <link rel="stylesheet" type="text/css" href="js/jquery-easyui-1.4.1/themes/default/easyui.css">
              <link rel="stylesheet" type="text/css" href="js/jquery-easyui-1.4.1/themes/icon.css">
              <link rel="stylesheet" type="text/css" href="css/common.css">
              <script type="text/javascript" src="js/jquery3.4.1/jquery3.4.1.min.js"></script>
              <script type="text/javascript" src="js/jquery-easyui-1.4.1/jquery.min.js"></script>
              <script type="text/javascript" src="js/jquery-easyui-1.4.1/jquery.easyui.min.js"></script>
              <script type="text/javascript" src="commons/validate.js"></script>
              <script type="text/javascript" src="js/jquery-easyui-1.4.1/locale/easyui-lang-zh_CN.js"></script>
              <script type="text/javascript" src="js/common.js"></script>
          </head>
          
          <body>
          <div id="login_frame">
              <img src="images/logo.png" class="logo">
              <form method="post" action="/login/login" onsubmit="return check()">
                  <p><label class="label_input">用戶名</label><input type="text" id="username" name="username" class="text_field"/>
                  </p>
                  <p><label class="label_input">密碼</label><input type="password" id="password" name="password"
                                                                 class="text_field"/></p>
          
                  <div id="login_control">
                      <input type="submit" id="btn_login" value="登錄"/>
                      <%--  <a id="forget_pwd" href="forget_pwd.html">忘記密碼?</a>--%>
                  </div>
              </form>
          </div>
          
          </body>
          </html>
          <script>
              function check() {
                  var username=$("#username").val();
                  var password=$("#password").val();
                  if (username==="" || username===null) {
                      alert("請輸入用戶名");
                      return false;
                  } else if (password==="" || password===null) {
                      alert("請輸入密碼");
                      return false;
                  } else {
                      return true;
                  }
              }
          </script>
          <style>
          
              body {
                  background-size: 100%;
                  background-repeat: no-repeat;
              }
          
              #login_frame {
                  width: 400px;
                  height: 260px;
                  padding: 13px;
          
                  position: absolute;
                  left: 50%;
                  top: 50%;
                  margin-left: -200px;
                  margin-top: -200px;
          
                  background-color: #bed2c7;
          
                  border-radius: 10px;
                  text-align: center;
              }
          
              form p > * {
                  display: inline-block;
                  vertical-align: middle;
              }
          
              #image_logo {
                  margin-top: 22px;
              }
          
              .label_input {
                  font-size: 14px;
                  font-family: 宋體;
          
                  width: 65px;
                  height: 28px;
                  line-height: 28px;
                  text-align: center;
          
                  color: white;
                  background-color: #00303E;
                  border-top-left-radius: 5px;
                  border-bottom-left-radius: 5px;
              }
          
              .text_field {
                  width: 278px;
                  height: 28px;
                  border-top-right-radius: 5px;
                  border-bottom-right-radius: 5px;
                  border: 0;
              }
          
              #btn_login {
                  font-size: 14px;
                  font-family: 宋體;
          
                  width: 120px;
                  height: 28px;
                  line-height: 28px;
                  text-align: center;
          
                  color: white;
                  background-color: #00303E;
                  border-radius: 6px;
                  border: 0;
          
                  float: left;
              }
          
              #forget_pwd {
                  font-size: 12px;
                  color: white;
                  text-decoration: none;
                  position: relative;
                  float: right;
                  top: 5px;
          
              }
          
              #forget_pwd:hover {
                  color: blue;
                  text-decoration: underline;
              }
          
              #login_control {
                  padding: 0 28px;
              }
          
              .logo {
                  width: 40px;
                  height: 35px;
                  margin-top: -10px;
              }
          </style>

          2. 登錄的攔截器

          public class LoginInterceptor implements HandlerInterceptor {
          
              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
                  // 獲取請求的uri
                  String uri=request.getRequestURI();
                  // 除了login.jsp是可以公開訪問的,其它的URL都沒攔截
                  if (uri.indexOf("/login") >=0) {
                      return true;
                  } else {
                      // 獲取session
                      HttpSession session=request.getSession();
                      UserPojo user=(UserPojo) session.getAttribute("USER_SESSION");
                      // 判斷session中是否有用戶數據,如果有數據,則返回true。否則重定向到登錄頁面
                      if (user !=null) {
                          return true;
                      } else {
                          response.sendRedirect("/login/login");
                          return false;
                      }
                  }
              }
          
              @Override
              public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,
                                     ModelAndView modelAndView) throws Exception {
          
              }
          
              @Override
              public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                          Object o, Exception e) throws Exception {
          
              }
          }

          3. 登錄的 Controller

          小伙伴使用spring boot開發多年,但是對于過濾器和攔截器的主要區別依然傻傻分不清。今天就對這兩個概念做一個全面的盤點。

          定義與作用范圍

          • 過濾器(Filter):過濾器是一種可以動態地攔截、處理和響應HTTP請求和響應的對象。它基于Servlet規范,可以攔截所有到達Web應用的請求,包括靜態資源(如HTML、CSS、JavaScript文件等)和動態資源(如Servlet、JSP頁面等)。過濾器主要用于請求的預處理和響應的后處理,如字符編碼處理、URL訪問記錄等。
          • 攔截器(Interceptor):攔截器是Spring MVC框架中的一部分,它基于Spring框架的AOP(面向切面編程)思想。它主要攔截Controller層的方法調用,不會攔截對靜態資源的請求。攔截器主要用于在請求處理的不同階段(如請求進入控制器方法之前或之后)執行額外的邏輯操作,如權限檢查、接口日志記錄、異常處理等。

          過濾器實例

          定義過濾器:實現javax.servlet.Filter接口,并重寫doFilter方法。

          注冊過濾器:通過@WebFilter注解自動注冊,或者使用FilterRegistrationBean在Spring配置類中注冊。

          import org.springframework.stereotype.Component;
           
          import javax.servlet.*;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;
           
          @Component
          public class SimpleCORSFilter implements Filter {
           
              @Override
              public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
                  HttpServletResponse response=(HttpServletResponse) res;
                  HttpServletRequest request=(HttpServletRequest) req;
           
                  response.setHeader("Access-Control-Allow-Origin", "*");
                  response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
                  response.setHeader("Access-Control-Max-Age", "3600");
                  response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");
           
                  if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
                      response.setStatus(HttpServletResponse.SC_OK);
                  } else {
                      chain.doFilter(req, res);
                  }
              }
           
              @Override
              public void init(FilterConfig filterConfig) {}
           
              @Override
              public void destroy() {}
          }

          上面代碼定義了一的跨域資源共享(CORS)過濾器,用于處理跨域請求。它設置了允許的源、方法和頭部,并處理預檢請求,這個在開發中經常用到。

          攔截器實例

          定義攔截器:實現HandlerInterceptor接口,并重寫preHandle方法。

          @Component  
          public class AuthInterceptor implements HandlerInterceptor {  
              @Override  
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
                      throws Exception {  
                  // 獲取用戶信息,進行身份驗證  
                  // ...  
                    
                  // 如果用戶未登錄或權限不足,返回false并設置響應狀態  
                  if (!isUserAuthenticated(request)) {  
                      response.setStatus(HttpServletResponse.FORBIDDEN);  
                      return false;  
                  }  
                    
                  // 用戶已登錄且權限足夠,放行請求  
                  return true;  
              }  
          }

          注冊攔截器:在Spring配置類中注冊攔截器,并指定攔截的路徑和順序。

          @Configuration  
          public class WebConfig implements WebMvcConfigurer {  
              @Autowired  
              private AuthInterceptor authInterceptor;  
                
              @Override  
              public void addInterceptors(InterceptorRegistry registry) {  
                  registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns("/login");  
              }  
          }

          上面代碼主要實現一個權限檢查攔截器,用于在請求進入控制器方法之前進行身份驗證。

          總結

          作用范圍

          • 過濾器攔截所有到達Web應用的請求(包括靜態資源和動態資源)
          • 攔截器主要攔截Controller層的方法調用,不攔截靜態資源

          操作對象

          • 過濾器只能對請求(request)和響應(response)進行操作
          • 攔截器可以對請求(request)、響應(response)、處理器(handler)、模型視圖(modelAndView)、異常(exception)等進行


          每天一個小知識,每天進步一點點?。?![加油][加油][加油]

          喜歡這類文章,請關注、點贊、收藏、轉發,謝謝?。。?/p>


          主站蜘蛛池模板: 欧美激情国产精品视频一区二区| 色一情一乱一伦一区二区三区 | 福利电影一区二区| 精品欧洲av无码一区二区14| 久久久久99人妻一区二区三区| 一区二区在线视频观看| 一区二区三区美女视频| 国产成人精品一区二区三区| 久久一区二区三区99| 亚洲一区视频在线播放| 国产一区二区精品久久岳| 中文字幕精品一区二区精品| 国产AV一区二区三区传媒| 久久久久人妻一区精品色| 精品成人乱色一区二区| 欧美一区内射最近更新| 韩国一区二区三区视频| 一区二区免费电影| 国产精品亚洲一区二区三区在线| 少妇人妻精品一区二区三区| 中文字幕人妻AV一区二区| 国产精品成人一区二区三区| 久久精品综合一区二区三区| 亚洲AV无码一区东京热| 亚洲丶国产丶欧美一区二区三区| 亚洲丰满熟女一区二区v| 精品无码人妻一区二区三区品 | 亚洲sm另类一区二区三区| 亚洲中文字幕乱码一区| 波多野结衣在线观看一区二区三区 | 午夜性色一区二区三区不卡视频| 国产一区在线播放| 成人无码AV一区二区| 亚洲综合在线成人一区| 国产三级一区二区三区| 看电影来5566一区.二区| 国产手机精品一区二区| 亚洲免费一区二区| 亚洲综合色一区二区三区小说| 国产一区二区三区福利| 无码国产精成人午夜视频一区二区|