国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

利用Mybatis攔截器實現(xiàn)自定義的ID增長器

這篇具有很好參考價值的文章主要介紹了利用Mybatis攔截器實現(xiàn)自定義的ID增長器。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

  原生的Mybatis框架是沒有ID自增器,但例如國產(chǎn)的Mybatis Plus卻是支持,不過,Mybatis Plus卻是缺少了自定屬性的填充;例如:我們需要自定義填充一些屬性,updateDate、createDate等,這時Mybatis Plus自帶的ID自增器就無法滿足需求;這種時候我們就需要自定義的ID增加器,可以自定義ID增長策略同時還得支持更多的屬性自定義擴展,當然,最好能做成插件形式為其他項目或者模塊提供引入那就更新好了。

  利用Mybatis攔截器實現(xiàn)自定義的ID增長器

?

  在開始之前我們先確定和分析主要的需求,了解了需求才能更好的制作

  首先我們得確定主要的宗旨那就是實現(xiàn)ID自增長器,同時,還等保證該增長的靈活性和擴展性,讓其他項目引入之后可以很靈活的更改增長策略,隨意替換ID增長的實現(xiàn)。主要需求為下列幾點:

  1. 自定義的ID增長策略,同時保證該特性靈活性可以隨意替換
  2. 支持自定義的附帶屬性擴展增強
  3. 保證其他的項目引起使用時的絕對簡單,最好能像Spring Boot自動裝配模塊一樣保證簡單

  確定需求之后我們現(xiàn)在開始根據(jù)需求來功能了,我們由外到內(nèi)、由粗到細的去實現(xiàn)整個模塊。

  先確定該模塊的外體特征,先看第3點

保證其他的項目引起使用時的絕對簡單,最好能像Spring Boot自動裝配模塊一樣保證簡單

要保證該模塊的引用使用簡單那么就必須使用Spring Boot的特性->自動配置,實現(xiàn)自定義場景裝配器,利用該場景裝配才能保證模塊可以自動配置完成啟動之初的初始化。

我們先來新建Maven模塊

這里的模塊命名最好遵循Spring Boot官方的建議,第三方的模塊命名由模塊名稱+spring-boot;官方模塊由spring-boot+模塊名稱

利用Mybatis攔截器實現(xiàn)自定義的ID增長器

建好模塊之后我們來整理下目錄

這里我們把多余的目錄、文件刪除,這里使用了Maven的初始化模板,會自動生成一些模板文件;但是,該操作的主要目的只是獲得一個Maven結(jié)構(gòu)的項目

利用Mybatis攔截器實現(xiàn)自定義的ID增長器

接下來確定POM文件的引用

 1   <!-- springboot自動配置-->
 2         <dependency>
 3             <groupId>org.springframework.boot</groupId>
 4             <artifactId>spring-boot-autoconfigure</artifactId>
 5         </dependency>
 6 
 7         <!-- springboot自動配置處理器-->
 8         <dependency>
 9             <groupId>org.springframework.boot</groupId>
10             <artifactId>spring-boot-configuration-processor</artifactId>
11             <optional>true</optional>
12         </dependency>
13 
14         <!-- mybatis啟動器,本質(zhì)是在Mybatis的基礎(chǔ)上進行擴展的,必須引入Mybatis的支持-->
15         <dependency>
16             <groupId>org.mybatis.spring.boot</groupId>
17             <artifactId>mybatis-spring-boot-starter</artifactId>
18             <version>2.2.2</version>
19         </dependency>

到這里整個模塊的創(chuàng)建已經(jīng)完成了,接下來我們的仔細分析下該如何實現(xiàn)自定義的增長器

?插件要使用方便那么就意味著要拋棄繁雜的配置,同時對必要的信息注入配置時應(yīng)該采用注解的方式來保持簡潔;

既然是ID增長器那就必須的確定哪個屬性為ID,并且確定ID屬性的類的全限定名,在這里我們定義兩個注解

1、這里定義了@Key注解,該注解的主要目的是為了標識出ORM實體類映射數(shù)據(jù)庫表中的主鍵,插件最終的主要注入的屬性

 1 /**
 2  * 主鍵注解標記
 3  * @Author: Song L.Lu
 4  * @Since: 2024-01-18 11:20
 5  **/
 6 
 7 @Documented
 8 @Retention(RetentionPolicy.RUNTIME)
 9 @Target({ElementType.FIELD})
10 public @interface Key {
11     String value() default "";
12 }

2、@KeyGenerator 注解,用來標記當前的實體類所在的包路徑,該路徑為插件提供查找實體類的路徑;注意該注解標記于Spring Boot啟動主類上

格式@KeyGenerator ("xxx.xx.xx.entity")

 1 /**
 2  * 標記當前實體類所在的包路徑
 3  * @Author: Song L.Lu
 4  * @Since: 2024-01-18 11:19
 5  **/
 6 
 7 @Documented
 8 @Retention(RetentionPolicy.RUNTIME)
 9 @Target({ElementType.TYPE})
10 public @interface KeyGenerator {
11     String value();
12 }

3、創(chuàng)建一個Spring Boot 的AutoConfiguration用來自動完成插件的裝配,包括初始化上下文環(huán)境、掃描Entity實體類、注入Mybatis攔截器

GeneratorAutoConfiguration類由MATE-INFO/spring。factories注入,該配置文件在Spring Boot啟動時會自動掃描各模塊下的resource/MATE-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.ycg.tab.cloud.mybatis.generator.config.autoconfigure.GeneratorAutoConfiguration

?這里往Spring Context中注入了兩個實例對象

ParseMapperFactoryProcessor實現(xiàn)BeanFactoryPostProcessor.postProcessBeanFactory接口方法,其目的是在工廠創(chuàng)建并掃描完Beandefition后觸發(fā)實體類的掃描
GeneratorInterceptor實現(xiàn)了Mybatis的攔截器,其攔截活動為update/insert,在Mybatis觸發(fā)update/insert操作時進行ID增長和自定義的屬性添加效果
 1 /**
 2  * 自動裝配
 3  * @Author: Song L.Lu
 4  * @Since: 2024-01-18 11:19
 5  **/
 6 
 7 @ConditionalOnBean({MybatisAutoConfiguration.class})
 8 @AutoConfigureAfter({MybatisAutoConfiguration.class, SnowflakeIdGenerator.class})
 9 public class GeneratorAutoConfiguration {
10 
11     /**
12      * 注入實體類掃描處理器
13      * 主要在BeanFactoryPostProcessor.postProcessBeanFactory期間完成實體類的掃描
14      * @return
15      */
16     @Bean
17     public BeanDefinitionRegistryPostProcessor postProcessor() {
18         return new ParseMapperFactoryProcessor();
19     }
20 
21     /**
22      * 向Spring context注入Mybatis攔截器
23      * @param generator
24      * @param mapperRegisterStore
25      * @return
26      */
27     @Bean
28     public Interceptor interceptor(Generator<?> generator, MapperRegisterStore mapperRegisterStore) {
29         return new GeneratorInterceptor(generator, mapperRegisterStore);
30     }
31 }

4、ParseMapperFactoryProcessor類

  1 /**
  2  * 實體類掃描處理器
  3  * 主要工作完成@Key注解的掃描,確定標記的主鍵
  4  * @Author: Song L.Lu
  5  * @Since: 2024-01-18 11:17
  6  **/
  7 public class ParseMapperFactoryProcessorimplements BeanDefinitionRegistryPostProcessor {
  8     static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
  9 
 10     private final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
 11 
 12     private final String resourcePattern = "**/*.class";
 13 
 14     private Environment environment;
 15 
 16     private ResourcePatternResolver resourcePatternResolver;
 17 
 18     public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
 19         AnnotationMetadata metadata;
 20         String className = beanDef.getBeanClassName();
 21         if (className == null || beanDef.getFactoryMethodName() != null)
 22             return false;
 23         if (beanDef instanceof AnnotatedBeanDefinition && className
 24                 .equals(((AnnotatedBeanDefinition)beanDef).getMetadata().getClassName())) {
 25             metadata = ((AnnotatedBeanDefinition)beanDef).getMetadata();
 26         } else {
 27             try {
 28                 MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
 29                 metadata = metadataReader.getAnnotationMetadata();
 30             } catch (IOException ex) {
 31                 return false;
 32             }
 33         }
 34         return metadata.hasAnnotation("org.springframework.boot.autoconfigure.SpringBootApplication");
 35     }
 36 
 37     public final Environment getEnvironment() {
 38         if (this.environment == null)
 39             this.environment = new StandardEnvironment();
 40         return this.environment;
 41     }
 42 
 43     private ResourcePatternResolver getResourcePatternResolver() {
 44         if (this.resourcePatternResolver == null)
 45             this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
 46         return this.resourcePatternResolver;
 47     }
 48 
 49     protected String resolveBasePackage(String basePackage) {
 50         return ClassUtils.convertClassNameToResourcePath(getEnvironment().resolveRequiredPlaceholders(basePackage));
 51     }
 52 
 53     public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
 54         BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(MapperRegisterStore.class);
 55         beanDefinitionBuilder.setScope("singleton");
 56         AbstractBeanDefinition abstractBeanDefinition = beanDefinitionBuilder.getBeanDefinition();
 57         registry.registerBeanDefinition(Objects.requireNonNull(abstractBeanDefinition.getBeanClassName()), abstractBeanDefinition);
 58     }
 59 
 60     /**
 61      * 掃描被@Key注解標識的實體類
 62      * @param beanFactory
 63      * @return
 64      */
 65     public Map<Class<?>, Field> parseBeanDefinitionCandidate(ConfigurableListableBeanFactory beanFactory) {
 66         Map<Class<?>, Field> tableClassz = new HashMap<>();
 67         List<BeanDefinitionHolder> candidates = new ArrayList<>();
 68         String[] candidateNames = beanFactory.getBeanDefinitionNames(); // 從BeanFactory中取出已經(jīng)構(gòu)建的BeanDefinition
 69         for (String name : candidateNames) {
 70             BeanDefinition beanDef = beanFactory.getMergedBeanDefinition(name);
 71             if (checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) // 找出@SpringBoot注解的主類,主類上使用@Key@KeyGenerator("com.xxx.xx.xx.system.notice.domain.entity")
 72                 candidates.add(new BeanDefinitionHolder(beanDef, name));
 73         }
 74         String basePackage = parseCandidateBasePackages(candidates); // 獲取主類上的@KeyGenerator注解中的值,該值為實體類的包路徑
 75         Set<BeanDefinition> bfs = scanTableInfo(basePackage); // 根據(jù)找到的實體類的包路徑掃描實體類存放的地方,并且將下面所有的實體類掃描出來
 76         for (BeanDefinition bd : bfs) { // 遍歷所有實體類
 77             try {
 78                 Class<?> clz = Class.forName(bd.getBeanClassName());
 79                 ReflectionUtils.doWithFields(clz, ff -> {
 80                     Annotation[] annotations = ff.getAnnotations();
 81                     for (Annotation annotation : annotations) {
 82                         if (annotation instanceof Key) // 判斷實體類上的字段是否帶有@Key注解,如果帶有那么該字段便是由@Key注解標記的實體主鍵
 83                             tableClassz.put(clz, ff);
 84                     }
 85                 });
 86             } catch (Throwable e) {
 87                 throw new BeanDefinitionStoreException("Failed to parse entity class [" + bd
 88                         .getBeanClassName() + "]", e);
 89             }
 90         }
 91         return tableClassz;
 92     }
 93 
 94     private Set<BeanDefinition> scanTableInfo(String basePackage) {
 95         Set<BeanDefinition> candidates = new LinkedHashSet<>();
 96         try {
 97             String packageSearchPath = "classpath*:" + resolveBasePackage(basePackage) + '/' + "**/*.class";
 98             Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
 99             for (Resource resource : resources) {
100                 try {
101                     MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
102                     ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
103                     sbd.setSource(resource);
104                     candidates.add(sbd);
105                 } catch (Throwable ex) {
106                     throw new BeanDefinitionStoreException("Failed to read candidate entity class: " + resource, ex);
107                 }
108             }
109         } catch (IOException ex) {
110             throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
111         }
112         return candidates;
113     }
114 
115     public String parseCandidateBasePackages(List<BeanDefinitionHolder> candidates) {
116         for (BeanDefinitionHolder holder : candidates) {
117             BeanDefinition bd = holder.getBeanDefinition();
118             try {
119                 KeyGenerator annotation = AnnotationUtils.findAnnotation(Class.forName(bd.getBeanClassName()), KeyGenerator.class);
120                 if (Objects.nonNull(annotation))
121                     return annotation.value();
122             } catch (BeanDefinitionStoreException ex) {
123                 throw ex;
124             } catch (Throwable ex) {
125                 throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd
126                         .getBeanClassName() + "]", ex);
127             }
128         }
129         return null;
130     }
131 
132     /**
133      * BeanFactoryPostProcessor.postProcessBeanFactory方法實現(xiàn),其目的是觸發(fā)實體類的@Key注解掃描
134      * 并將掃描結(jié)果保存至MapperRegisterStore中,MapperRegisterStore中儲存了類的類型+@Key注解的主鍵字段
135      * @param beanFactory the bean factory used by the application context
136      * @throws BeansException
137      */
138     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
139         Map<Class<?>, Field> fieldMap = parseBeanDefinitionCandidate(beanFactory); // 調(diào)用解析BeanDefinition方法
140         MapperRegisterStore mapperRegisterStore = beanFactory.getBean(MapperRegisterStore.class); // 從BeanFactory容器中獲取MapperRegisterStore實例
141         mapperRegisterStore.putAll(fieldMap);
142     }
143 }

實體類掃描流程逐一分析

1、Spring BeanFactory生命周期:

  觸發(fā)時機->postProcessBeanFactory,了解Spring啟動流程的都知道,Spring在啟動過程中定義了很多Hook,同時也為整個Bean的創(chuàng)建到消費定義了生命周期,那么BeanFactory工廠呢?

當然,BeanFactory也生命周期,而我們恰恰就是在這個BeanFactory生命周期的觸發(fā)點上定義了實現(xiàn)

BeanFactoryPostProcessor 該定義實現(xiàn)最終會在Spring啟動流程中Refresh()下的
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
調(diào)用觸發(fā),我們可以在這個節(jié)點上定義掃描實體類的實現(xiàn);因為,這個節(jié)點剛好是BeanFactory創(chuàng)建完成BeanDefinition構(gòu)建成功、Bean未實例化前
 1 @FunctionalInterface
 2 public interface BeanFactoryPostProcessor {
 3 
 4     /**
 5      * Modify the application context's internal bean factory after its standard
 6      * initialization. All bean definitions will have been loaded, but no beans
 7      * will have been instantiated yet. This allows for overriding or adding
 8      * properties even to eager-initializing beans.
 9      * @param beanFactory the bean factory used by the application context
10      * @throws org.springframework.beans.BeansException in case of errors
11      */
12     void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
13 
14 }

2、BeanDefinitionRegistryPostProcessor 接口:

  該接口繼承于上面的接口,同樣也是在postProcessBeanFactory方法中觸發(fā);注意該接口中的唯一實現(xiàn)方法入?yún)锽eanDefinitionRegistry ,我們可以利用BeanDefinitionRegistry往Spring IOC中注入自定義的BeanDefinition,然后再由Spring完成實例和初始化。

我們的實現(xiàn)代碼中就實現(xiàn)了改接口,并且往里注入一個名為MapperRegisterStore的BeanDefinition,讓我們一起來看看這個MapperRegisterStore的定義

?

 1 public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
 2 
 3     /**
 4      * Modify the application context's internal bean definition registry after its
 5      * standard initialization. All regular bean definitions will have been loaded,
 6      * but no beans will have been instantiated yet. This allows for adding further
 7      * bean definitions before the next post-processing phase kicks in.
 8      * @param registry the bean definition registry used by the application context
 9      * @throws org.springframework.beans.BeansException in case of errors
10      */
11     void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
12 
13 }

  該定義非常的簡單,我們繼承了HashMap,并指定了該泛型;由該定義存儲了K,V結(jié)構(gòu),K為類的類型、V為類的字段屬性;這里我們用來存儲掃描的實體類的類型并且對應(yīng)的@Key注解的字段,后續(xù)利用該字段可以通過反射的方式注入自增ID的屬性

 1 /**
 2  * @Author: Song L.Lu
 3  * @Since: 2023-05-30 15:15
 4  **/
 5 public class MapperRegisterStore extends HashMap<Class<?>, Field> {
 6     private static final long serialVersionUID = -3863847035136313223L;
 7     public Field put(Class<?> k, Field v) {
 8         if (Objects.nonNull(k) && Objects.nonNull(v)) {
 9             return put(k, v);
10         }
11         return null;
12     }
13 }

  下面是自定義注入MapperRegisterStore的實現(xiàn)

1 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
2         BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(MapperRegisterStore.class);
3         beanDefinitionBuilder.setScope("singleton");
4         AbstractBeanDefinition abstractBeanDefinition = beanDefinitionBuilder.getBeanDefinition();
5         registry.registerBeanDefinition(Objects.requireNonNull(abstractBeanDefinition.getBeanClassName()), abstractBeanDefinition);
6     }

3、掃描實體類路徑:

  分析完了觸發(fā)點,接下來我們接著分析parseBeanDefinitionCandidate方法;如下列代碼所示,該方法主要調(diào)用了checkConfigrationClassCandidate來提取由@SpringBootApplication注解的主類,并且從主類上獲取由@KeyGenerator注解定義的實體類包路徑;通過包路徑再去通過scanTableInfo方法掃描出來所有的實體類Class,這里代碼實現(xiàn)時利用了MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory()實現(xiàn),該類在ResourceLoader的基礎(chǔ)上增強了緩存,讓已經(jīng)加載過的資源不用重復(fù)加載;最終再由ScannedGenericBeanDefinition 將轉(zhuǎn)為的元數(shù)據(jù)轉(zhuǎn)為BeanDefinition

 1 private Set<BeanDefinition> scanTableInfo(String basePackage) {
 2         Set<BeanDefinition> candidates = new LinkedHashSet<>();
 3         try {
 4             String packageSearchPath = "classpath*:" + resolveBasePackage(basePackage) + '/' + "**/*.class";
 5             Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
 6             for (Resource resource : resources) {
 7                 try {
 8                     MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
 9                     ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
10                     sbd.setSource(resource);
11                     candidates.add(sbd);
12                 } catch (Throwable ex) {
13                     throw new BeanDefinitionStoreException("Failed to read candidate entity class: " + resource, ex);
14                 }
15             }
16         } catch (IOException ex) {
17             throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
18         }
19         return candidates;
20     }

4、找到@Key注解的字段:

  由上面的scanTableInfo方法我們得到了所有實體類BeanDefinition集合,接下來通過遍歷這些類的字段就可以得到由@Key注解的字段,并且將字段put到MapperRegisterStore中

 1 for (BeanDefinition bd : bfs) { // 遍歷所有實體類
 2             try {
 3                 Class<?> clz = Class.forName(bd.getBeanClassName());
 4                 ReflectionUtils.doWithFields(clz, ff -> {
 5                     Annotation[] annotations = ff.getAnnotations();
 6                     for (Annotation annotation : annotations) {
 7                         if (annotation instanceof Key) // 判斷實體類上的字段是否帶有@Key注解,如果帶有那么該字段便是由@Key注解標記的實體主鍵
 8                             tableClassz.put(clz, ff);
 9                     }
10                 });
11             } catch (Throwable e) {
12                 throw new BeanDefinitionStoreException("Failed to parse entity class [" + bd
13                         .getBeanClassName() + "]", e);
14             }
15         }
 1  /**
 2      * 掃描被@Key注解標識的實體類
 3      * @param beanFactory
 4      * @return
 5      */
 6     public Map<Class<?>, Field> parseBeanDefinitionCandidate(ConfigurableListableBeanFactory beanFactory) {
 7         Map<Class<?>, Field> tableClassz = new HashMap<>();
 8         List<BeanDefinitionHolder> candidates = new ArrayList<>();
 9         String[] candidateNames = beanFactory.getBeanDefinitionNames(); // 從BeanFactory中取出已經(jīng)構(gòu)建的BeanDefinition
10         for (String name : candidateNames) {
11             BeanDefinition beanDef = beanFactory.getMergedBeanDefinition(name);
12             if (checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) // 找出@SpringBoot注解的主類,主類上使用@Key@KeyGenerator("com.xxx.xx.xx.system.notice.domain.entity")
13                 candidates.add(new BeanDefinitionHolder(beanDef, name));
14         }
15         String basePackage = parseCandidateBasePackages(candidates); // 獲取主類上的@KeyGenerator注解中的值,該值為實體類的包路徑
16         Set<BeanDefinition> bfs = scanTableInfo(basePackage); // 根據(jù)找到的實體類的包路徑掃描實體類存放的地方,并且將下面所有的實體類掃描出來
17         for (BeanDefinition bd : bfs) { // 遍歷所有實體類
18             try {
19                 Class<?> clz = Class.forName(bd.getBeanClassName());
20                 ReflectionUtils.doWithFields(clz, ff -> {
21                     Annotation[] annotations = ff.getAnnotations();
22                     for (Annotation annotation : annotations) {
23                         if (annotation instanceof Key) // 判斷實體類上的字段是否帶有@Key注解,如果帶有那么該字段便是由@Key注解標記的實體主鍵
24                             tableClassz.put(clz, ff);
25                     }
26                 });
27             } catch (Throwable e) {
28                 throw new BeanDefinitionStoreException("Failed to parse entity class [" + bd
29                         .getBeanClassName() + "]", e);
30             }
31         }
32         return tableClassz;
33     }
  至此,實體類與數(shù)據(jù)表的的主鍵映射的流程解析完畢,接下來將解析Mybatis下的自定義的攔截器,這個攔截器也是實現(xiàn)自定義ID自增的關(guān)鍵
5、Mybatis攔截器:
  熟悉Mybatis都應(yīng)該都聽過Myabtis的自定義攔截器,該攔截器允許在Executor、ParameterHandler、ResultSetHandler、StatementHandler處進行攔截,實現(xiàn)原理是利用JDK的動態(tài)代理將所有Interceptor實現(xiàn)類對前面四個接口下的

任意方法進行代理,最后會產(chǎn)生一個代理后的包裝調(diào)用鏈,這個鏈的最尾部就是實際Executor、ParameterHandler、ResultSetHandler、StatementHandler中的任意一個需要攔截的方法,那么之前的Interceptor接口的實現(xiàn)類則會對傳入
的參數(shù)或結(jié)果進行攔截處理;我們利用這個特性在update或insert方法真正執(zhí)行之前先執(zhí)行我們自定義的攔截器,并對這個攔截中傳入的參數(shù)進行處理,傳入我們需要自增長的ID的值。
攔截器的實現(xiàn)首先對傳入的參數(shù)進行提取,取出主要的插入對象obj,利用之前在Spring啟動時由@Key掃描到的實體類主鍵字段。通過反射的方式將自定義的ID生成策略賦值給實體類的主鍵ID
 1 /**
 2  * @Author: Song L.Lu
 3  * @Since: 2024-01-18 11:15
 4  **/
 5 @Intercepts({@Signature(
 6         type = Executor.class,
 7         method = "update",
 8         args = {MappedStatement.class, Object.class}
 9 )})
10 public class GeneratorInterceptor implements Interceptor {
11     private Generator generator;
12     private MapperRegisterStore mapperRegisterStore;
13 
14     public GeneratorInterceptor(Generator generator, MapperRegisterStore mapperRegisterStore) { 
15         this.generator = generator; // 注入自定義增長器的具體實現(xiàn)
16         this.mapperRegisterStore = mapperRegisterStore; // 注入存儲實體類主鍵的數(shù)據(jù)結(jié)構(gòu)類
17     }
18 
19     /**
20      * 實現(xiàn)攔截器方法
21      * @param invocation
22      * @return
23      * @throws Throwable
24      */
25     public Object intercept(Invocation invocation) throws Throwable {
26         Object[] args = invocation.getArgs();
27         if (args.length == 0) {
28             throw new BindingException("Mapper代理對象沒有傳入的參數(shù)!");
29         } else {
30             MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
31             Object obj = invocation.getArgs()[1];
32             Class<?> clz = obj.getClass();
33             Field id = this.mapperRegisterStore.get(clz);
34             if (Objects.nonNull(id)) {
35                 ReflectionUtils.makeAccessible(id);
36                 ReflectionUtils.setField(id, obj, this.generator.nextId()); // 調(diào)用ID自增長器的nextId方法,用來獲取自定義的ID,這里使用的是Twitter的雪花
37             }
38 
39             SqlCommandType commandType = mappedStatement.getSqlCommandType();
40             boolean existsInsert = false;
41             switch (commandType) { // 根據(jù)Mybatis原先解析出的sql命令類型確定是插入還是更新
42                 case INSERT:
43                     existsInsert = true;
44                     break;
45                 case UPDATE:
46                     existsInsert = false;
47                     break;
48                 default:
49                     throw new BindingException("參數(shù)綁定,發(fā)生未知錯誤!");
50             }
51 
52             // 這里調(diào)用自定義增長器實現(xiàn)額外的屬性添加,existsInsert標記當前的操作時插入還是更新
53             generator.propertyValues(obj, existsInsert);
54             return invocation.proceed();
55         }
56     }
6、Generator接口:
  Generator接口主要為引入的插件自定義實現(xiàn)自增長的策略實現(xiàn),這里演示使用雪花算法
1 /**
2  * @Author: Song L.Lu
3  * @Since: 2023-05-31 09:51
4  **/
5 public interface Generator<T> {
6     T nextId();
7     void propertyValues(Object c, boolean existsInsert);
8 }

7、Generator的實現(xiàn)文章來源地址http://www.zghlxwxcb.cn/news/detail-825180.html

 1 /**
 2  * @Author: Song L.Lu
 3  * @Since: 2023-06-01 09:32
 4  **/
 5 @Component
 6 public class SnowflakeIdGenerator implements Generator<Long> {
 7     private SnowflakeIdWorker snowflakeIdWorker;
 8 
 9     @Autowired
10     public void setSnowflakeIdWorker(SnowflakeIdWorker snowflakeIdWorker) {
11         this.snowflakeIdWorker = snowflakeIdWorker; // 注入雪花算法的生成器
12     }
13 
14     @Override
15     public Long nextId() {
16         return snowflakeIdWorker.nextId();
17     }
18 
19     @Override
20     public void propertyValues(Object c, boolean existsInsert) {
21         if(c instanceof DbBaseEntity){
22             ((DbBaseEntity) c).populate(existsInsert); // 這里定義了額外屬性的賦值
23         }
24     }
25 
26 }
實現(xiàn)自定義的ID增長器的方式有很多種,這里只是本身的一種思路,我覺得如何去實現(xiàn)的并不是太重要,重要的是如何開拓實現(xiàn)的思路,實現(xiàn)一種變通,增強思維能力和舉一反三的思維模式才是最主要的。
源碼地址:https://gitee.com/luxsong/mybatis-generator-starter

到了這里,關(guān)于利用Mybatis攔截器實現(xiàn)自定義的ID增長器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • MyBatis攔截器優(yōu)雅實現(xiàn)數(shù)據(jù)脫敏

    MyBatis攔截器優(yōu)雅實現(xiàn)數(shù)據(jù)脫敏

    現(xiàn)代網(wǎng)絡(luò)環(huán)境中,敏感數(shù)據(jù)的處理是至關(guān)重要的。敏感數(shù)據(jù)包括個人身份信息、銀行賬號、手機號碼等,泄露這些數(shù)據(jù)可能導(dǎo)致用戶隱私泄露、財產(chǎn)損失等嚴重后果。因此,對敏感數(shù)據(jù)進行脫敏處理是一種必要的安全措施。 比如頁面上常見的敏感數(shù)據(jù)都是加*遮擋處理過的,

    2024年02月06日
    瀏覽(20)
  • MyBatis Plus 攔截器實現(xiàn)數(shù)據(jù)權(quán)限控制

    MyBatis Plus 攔截器實現(xiàn)數(shù)據(jù)權(quán)限控制

    上篇文章介紹的MyBatis Plus 插件實際上就是用攔截器實現(xiàn)的,MyBatis Plus攔截器對MyBatis的攔截器進行了包裝處理,操作起來更加方便 2.1、InnerInterceptor MyBatis Plus提供的InnerInterceptor接口提供了如下方法,主要包括:在查詢之前執(zhí)行,在更新之前執(zhí)行,在SQL準備之前執(zhí)行 2.2、編寫簡

    2024年01月17日
    瀏覽(23)
  • 自定義攔截器實現(xiàn)

    自定義攔截器實現(xiàn)

    在 Spring MVC 框架中, 攔截器作為一種機制, 用于對請求進行攔截. 攔截器可以在請求進入處理器之前、處理器返回處理之后、視圖渲染之前等各個環(huán)節(jié)進行攔截. 攔截器通常用于實現(xiàn)一下功能 : 鑒權(quán)和身份認證 日志記錄和統(tǒng)計 請求參數(shù)和校驗和過濾 緩存和性能優(yōu)化 路徑重定向

    2024年02月09日
    瀏覽(27)
  • 自定義注解與攔截器實現(xiàn)不規(guī)范sql攔截(自定義注解填充插件篇)

    自定義注解與攔截器實現(xiàn)不規(guī)范sql攔截(自定義注解填充插件篇)

    在自定義注解與攔截器實現(xiàn)不規(guī)范sql攔截(攔截器實現(xiàn)篇)中提到過,寫了一個idea插件來輔助對Mapper接口中的方法添加自定義注解,這邊記錄一下插件的實現(xiàn)。 在上一篇中,定義了一個自定義注解對需要經(jīng)過where判斷的Mapper sql方法進行修飾。那么,現(xiàn)在想使用一個idea插件來

    2024年01月23日
    瀏覽(25)
  • 基于Mybatis-Plus攔截器實現(xiàn)MySQL數(shù)據(jù)加解密

    基于Mybatis-Plus攔截器實現(xiàn)MySQL數(shù)據(jù)加解密

    用戶的一些敏感數(shù)據(jù),例如手機號、郵箱、身份證等信息,在數(shù)據(jù)庫以明文存儲時會存在數(shù)據(jù)泄露的風(fēng)險,因此需要進行加密, 但存儲數(shù)據(jù)再被取出時,需要進行解密,因此加密算法需要使用對稱加密算法。 常用的對稱加密算法有AES、DES、RC、BASE64等等,各算法的區(qū)別與優(yōu)劣

    2024年02月16日
    瀏覽(27)
  • SpringBoot定義攔截器+自定義注解+Redis實現(xiàn)接口防刷(限流)

    在攔截器Interceptor中攔截請求 通過地址+請求uri作為調(diào)用者訪問接口的區(qū)分在Redis中進行計數(shù)達到限流目的 定義參數(shù) 訪問周期 最大訪問次數(shù) 禁用時長 代碼實現(xiàn) 定義攔截器:實現(xiàn)HandlerInterceptor接口,重寫preHandle()方法 注冊攔截器:配置類實現(xiàn)WebMvcConfigurer接口,重寫addIntercep

    2024年02月05日
    瀏覽(32)
  • MyBatis 攔截器介紹

    MyBatis 提供了一種插件 (plugin) 的功能,雖然叫做插件,但其實這是攔截器功能。那么攔截器攔截 MyBatis 中的哪些內(nèi)容呢? 我們進入官網(wǎng)看一看: MyBatis 允許你在已映射語句執(zhí)行過程中的某一點進行攔截調(diào)用。默認情況下,MyBatis 允許使用插件來攔截的方法調(diào)用包括: Executor

    2024年02月15日
    瀏覽(21)
  • Spring Boot 3自定義注解+攔截器+Redis實現(xiàn)高并發(fā)接口限流

    在當今互聯(lián)網(wǎng)應(yīng)用開發(fā)中,高并發(fā)訪問是一個常見的挑戰(zhàn)。為了保障系統(tǒng)的穩(wěn)定性和可靠性,我們需要對接口進行限流,防止因過多的請求導(dǎo)致系統(tǒng)崩潰。 本文將介紹如何利用Spring Boot 3中的自定義注解、攔截器和Redis實現(xiàn)高并發(fā)接口限流,幫助程序員解決這一挑戰(zhàn)。 1. 自定

    2024年04月28日
    瀏覽(25)
  • SpringBoot中接口冪等性實現(xiàn)方案-自定義注解+Redis+攔截器實現(xiàn)防止訂單重復(fù)提交

    SpringBoot中接口冪等性實現(xiàn)方案-自定義注解+Redis+攔截器實現(xiàn)防止訂單重復(fù)提交

    SpringBoot+Redis+自定義注解實現(xiàn)接口防刷(限制不同接口單位時間內(nèi)最大請求次數(shù)): SpringBoot+Redis+自定義注解實現(xiàn)接口防刷(限制不同接口單位時間內(nèi)最大請求次數(shù))_redis防刷_霸道流氓氣質(zhì)的博客-CSDN博客 以下接口冪等性的實現(xiàn)方式與上面博客類似,可參考。 什么是冪等性? 冪等

    2024年02月15日
    瀏覽(23)
  • 自定義攔截器(OpenFeign)

    全量的調(diào)用日志中可以查看到自定義攔截器增加的 custom_header_info 字段

    2024年01月19日
    瀏覽(18)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包