在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Spring中最常用的11個擴展點

jf_ro2CN3Fa ? 來源:蘇三說技術 ? 2023-01-11 10:31 ? 次閱讀


之前給大家寫過一篇 Bean 的生命周期,非常受歡迎,里面其實介紹了 Bean 生命周期中所有的擴展點。

今天給大家帶來的文章,可以作為 Spring 擴展點的補充,一共 11 個,工作中會經常用到,如果用得好,很可能會事半功倍哈。

前言

我們一說到spring,可能第一個想到的是 IOC(控制反轉) 和 AOP(面向切面編程)。

沒錯,它們是spring的基石,得益于它們的優秀設計,使得spring能夠從眾多優秀框架中脫穎而出。

除此之外,我們在使用spring的過程中,有沒有發現它的擴展能力非常強。由于這個優勢的存在,讓spring擁有強大的包容能力,讓很多第三方應用能夠輕松投入spring的懷抱。比如:rocketmq、mybatis、redis等。

今天跟大家一起聊聊,在Spring中最常用的11個擴展點。

f83be988-9151-11ed-bfe3-dac502259ad0.png

基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

1.自定義攔截器

spring mvc攔截器根spring攔截器相比,它里面能夠獲取HttpServletRequestHttpServletResponse等web對象實例。

spring mvc攔截器的頂層接口是:HandlerInterceptor,包含三個方法:

  • preHandle 目標方法執行前執行
  • postHandle 目標方法執行后執行
  • afterCompletion 請求完成時執行

為了方便我們一般情況會用HandlerInterceptor接口的實現類HandlerInterceptorAdapter類。

假如有權限認證、日志、統計的場景,可以使用該攔截器。

第一步,繼承HandlerInterceptorAdapter類定義攔截器:

publicclassAuthInterceptorextendsHandlerInterceptorAdapter{

@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)
throwsException{
StringrequestUrl=request.getRequestURI();
if(checkAuth(requestUrl)){
returntrue;
}

returnfalse;
}

privatebooleancheckAuth(StringrequestUrl){
System.out.println("===權限校驗===");
returntrue;
}
}

第二步,將該攔截器注冊到spring容器:

@Configuration
publicclassWebAuthConfigextendsWebMvcConfigurerAdapter{

@Bean
publicAuthInterceptorgetAuthInterceptor(){
returnnewAuthInterceptor();
}

@Override
publicvoidaddInterceptors(InterceptorRegistryregistry){
registry.addInterceptor(newAuthInterceptor());
}
}

第三步,在請求接口時spring mvc通過該攔截器,能夠自動攔截該接口,并且校驗權限。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

2.獲取Spring容器對象

在我們日常開發中,經常需要從Spring容器中獲取Bean,但你知道如何獲取Spring容器對象嗎?

2.1 BeanFactoryAware接口

@Service
publicclassPersonServiceimplementsBeanFactoryAware{
privateBeanFactorybeanFactory;

@Override
publicvoidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException{
this.beanFactory=beanFactory;
}

publicvoidadd(){
Personperson=(Person)beanFactory.getBean("person");
}
}

實現BeanFactoryAware接口,然后重寫setBeanFactory方法,就能從該方法中獲取到spring容器對象。

2.2 ApplicationContextAware接口

@Service
publicclassPersonService2implementsApplicationContextAware{
privateApplicationContextapplicationContext;

@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
this.applicationContext=applicationContext;
}

publicvoidadd(){
Personperson=(Person)applicationContext.getBean("person");
}
}

實現ApplicationContextAware接口,然后重寫setApplicationContext方法,也能從該方法中獲取到spring容器對象。

2.3 ApplicationListener接口

@Service
publicclassPersonService3implementsApplicationListener<ContextRefreshedEvent>{
privateApplicationContextapplicationContext;
@Override
publicvoidonApplicationEvent(ContextRefreshedEventevent){
applicationContext=event.getApplicationContext();
}

publicvoidadd(){
Personperson=(Person)applicationContext.getBean("person");
}
}

3.全局異常處理

以前我們在開發接口時,如果出現異常,為了給用戶一個更友好的提示,例如:

@RequestMapping("/test")
@RestController
publicclassTestController{

@GetMapping("/add")
publicStringadd(){
inta=10/0;
return"成功";
}
}

如果不做任何處理請求add接口結果直接報錯:

f85bffca-9151-11ed-bfe3-dac502259ad0.png

what?用戶能直接看到錯誤信息

這種交互方式給用戶的體驗非常差,為了解決這個問題,我們通常會在接口中捕獲異常:

@GetMapping("/add")
publicStringadd(){
Stringresult="成功";
try{
inta=10/0;
}catch(Exceptione){
result="數據異常";
}
returnresult;
}

接口改造后,出現異常時會提示:“數據異常”,對用戶來說更友好。

看起來挺不錯的,但是有問題。。。

如果只是一個接口還好,但是如果項目中有成百上千個接口,都要加上異常捕獲代碼嗎?

答案是否定的,這時全局異常處理就派上用場了:RestControllerAdvice

@RestControllerAdvice
publicclassGlobalExceptionHandler{

@ExceptionHandler(Exception.class)
publicStringhandleException(Exceptione){
if(einstanceofArithmeticException){
return"數據異常";
}
if(einstanceofException){
return"服務器內部異常";
}
returnnull;
}
}

只需在handleException方法中處理異常情況,業務接口中可以放心使用,不再需要捕獲異常(有人統一處理了)。真是爽歪歪。

4.類型轉換器

spring目前支持3中類型轉換器:

  • Converter:將 S 類型對象轉為 T 類型對象
  • ConverterFactory:將 S 類型對象轉為 R 類型及子類對象
  • GenericConverter:它支持多個source和目標類型的轉化,同時還提供了source和目標類型的上下文,這個上下文能讓你實現基于屬性上的注解或信息來進行類型轉換。

這3種類型轉換器使用的場景不一樣,我們以Converter例。假如:接口中接收參數的實體對象中,有個字段的類型是Date,但是實際傳參的是字符串類型:2021-01-03 1015,要如何處理呢?

第一步,定義一個實體User:

@Data
publicclassUser{

privateLongid;
privateStringname;
privateDateregisterDate;
}

第二步,實現Converter接口:

publicclassDateConverterimplementsConverter<String,Date>{

privateSimpleDateFormatsimpleDateFormat=newSimpleDateFormat("yyyy-MM-ddHHss");

@Override
publicDateconvert(Stringsource){
if(source!=null&&!"".equals(source)){
try{
simpleDateFormat.parse(source);
}catch(ParseExceptione){
e.printStackTrace();
}
}
returnnull;
}
}

第三步,將新定義的類型轉換器注入到spring容器中:

@Configuration
publicclassWebConfigextendsWebMvcConfigurerAdapter{

@Override
publicvoidaddFormatters(FormatterRegistryregistry){
registry.addConverter(newDateConverter());
}
}

第四步,調用接口

@RequestMapping("/user")
@RestController
publicclassUserController{

@RequestMapping("/save")
publicStringsave(@RequestBodyUseruser){
return"success";
}
}

請求接口時User對象中registerDate字段會被自動轉換成Date類型。

5.導入配置

有時我們需要在某個配置類中引入另外一些類,被引入的類也加到spring容器中。這時可以使用@Import注解完成這個功能。

如果你看過它的源碼會發現,引入的類支持三種不同類型。

但是我認為最好將普通類和@Configuration注解的配置類分開講解,所以列了四種不同類型:

f871ea2e-9151-11ed-bfe3-dac502259ad0.png

5.1 普通類

這種引入方式是最簡單的,被引入的類會被實例化bean對象。

publicclassA{
}

@Import(A.class)
@Configuration
publicclassTestConfiguration{
}

通過@Import注解引入A類,spring就能自動實例化A對象,然后在需要使用的地方通過@Autowired注解注入即可:

@Autowired
privateAa;

是不是挺讓人意外的?不用加@Bean注解也能實例化bean。

5.2 配置類

這種引入方式是最復雜的,因為@Configuration注解還支持多種組合注解,比如:

  • @Import
  • @ImportResource
  • @PropertySource等。
publicclassA{
}

publicclassB{
}

@Import(B.class)
@Configuration
publicclassAConfiguration{

@Bean
publicAa(){
returnnewA();
}
}

@Import(AConfiguration.class)
@Configuration
publicclassTestConfiguration{
}

通過@Import注解引入@Configuration注解的配置類,會把該配置類相關@Import@ImportResource@PropertySource等注解引入的類進行遞歸,一次性全部引入。

5.3 ImportSelector

這種引入方式需要實現ImportSelector接口:

publicclassAImportSelectorimplementsImportSelector{

privatestaticfinalStringCLASS_NAME="com.sue.cache.service.test13.A";

publicString[]selectImports(AnnotationMetadataimportingClassMetadata){
returnnewString[]{CLASS_NAME};
}
}

@Import(AImportSelector.class)
@Configuration
publicclassTestConfiguration{
}

這種方式的好處是selectImports方法返回的是數組,意味著可以同時引入多個類,還是非常方便的。

5.4 ImportBeanDefinitionRegistrar

這種引入方式需要實現ImportBeanDefinitionRegistrar接口:

publicclassAImportBeanDefinitionRegistrarimplementsImportBeanDefinitionRegistrar{
@Override
publicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){
RootBeanDefinitionrootBeanDefinition=newRootBeanDefinition(A.class);
registry.registerBeanDefinition("a",rootBeanDefinition);
}
}

@Import(AImportBeanDefinitionRegistrar.class)
@Configuration
publicclassTestConfiguration{
}

這種方式是最靈活的,能在registerBeanDefinitions方法中獲取到BeanDefinitionRegistry容器注冊對象,可以手動控制BeanDefinition的創建和注冊。

6.項目啟動時

有時候我們需要在項目啟動時定制化一些附加功能,比如:加載一些系統參數、完成初始化、預熱本地緩存等,該怎么辦呢?

好消息是springboot提供了:

  • CommandLineRunner
  • ApplicationRunner

這兩個接口幫助我們實現以上需求。

它們的用法還是挺簡單的,以ApplicationRunner接口為例:

@Component
publicclassTestRunnerimplementsApplicationRunner{

@Autowired
privateLoadDataServiceloadDataService;

publicvoidrun(ApplicationArgumentsargs)throwsException{
loadDataService.load();
}
}

實現ApplicationRunner接口,重寫run方法,在該方法中實現自己定制化需求。

如果項目中有多個類實現了ApplicationRunner接口,他們的執行順序要怎么指定呢?

答案是使用@Order(n)注解,n的值越小越先執行。當然也可以通過@Priority注解指定順序。

7.修改BeanDefinition

Spring IOC在實例化Bean對象之前,需要先讀取Bean的相關屬性,保存到BeanDefinition對象中,然后通過BeanDefinition對象,實例化Bean對象。

如果想修改BeanDefinition對象中的屬性,該怎么辦呢?

答:我們可以實現BeanFactoryPostProcessor接口。

@Component
publicclassMyBeanFactoryPostProcessorimplementsBeanFactoryPostProcessor{

@Override
publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactoryconfigurableListableBeanFactory)throwsBeansException{
DefaultListableBeanFactorydefaultListableBeanFactory=(DefaultListableBeanFactory)configurableListableBeanFactory;
BeanDefinitionBuilderbeanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addPropertyValue("id",123);
beanDefinitionBuilder.addPropertyValue("name","蘇三說技術");
defaultListableBeanFactory.registerBeanDefinition("user",beanDefinitionBuilder.getBeanDefinition());
}
}

在postProcessBeanFactory方法中,可以獲取BeanDefinition的相關對象,并且修改該對象的屬性。

8.初始化Bean前后

有時,你想在初始化Bean前后,實現一些自己的邏輯。

這時可以實現:BeanPostProcessor接口。

該接口目前有兩個方法:

  • postProcessBeforeInitialization 該在初始化方法之前調用。
  • postProcessAfterInitialization 該方法再初始化方法之后調用。

例如:

@Component
publicclassMyBeanPostProcessorimplementsBeanPostProcessor{

@Override
publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{
if(beaninstanceofUser){
((User)bean).setUserName("蘇三說技術");
}
returnbean;
}
}

如果spring中存在User對象,則將它的userName設置成:蘇三說技術。

其實,我們經常使用的注解,比如:@Autowired、@Value、@Resource、@PostConstruct等,是通過AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor實現的。

9.初始化方法

目前spring中使用比較多的初始化bean的方法有:

  1. 使用@PostConstruct注解
  2. 實現InitializingBean接口

9.1 使用@PostConstruct注解

@Service
publicclassAService{
@PostConstruct
publicvoidinit(){
System.out.println("===初始化===");
}
}

在需要初始化的方法上增加@PostConstruct注解,這樣就有初始化的能力。

9.2 實現InitializingBean接口

@Service
publicclassBServiceimplementsInitializingBean{

@Override
publicvoidafterPropertiesSet()throwsException{
System.out.println("===初始化===");
}
}

實現InitializingBean接口,重寫afterPropertiesSet方法,該方法中可以完成初始化功能。

10.關閉容器前

有時候,我們需要在關閉spring容器前,做一些額外的工作,比如:關閉資源文件等。

這時可以實現DisposableBean接口,并且重寫它的destroy方法:

@Service
publicclassDServiceimplementsInitializingBean,DisposableBean{

@Override
publicvoiddestroy()throwsException{
System.out.println("DisposableBeandestroy");
}

@Override
publicvoidafterPropertiesSet()throwsException{
System.out.println("InitializingBeanafterPropertiesSet");
}
}

這樣spring容器銷毀前,會調用該destroy方法,做一些額外的工作。

通常情況下,我們會同時實現InitializingBean和DisposableBean接口,重寫初始化方法和銷毀方法。

11.自定義作用域

我們都知道spring默認支持的Scope只有兩種:

  • singleton 單例,每次從spring容器中獲取到的bean都是同一個對象。
  • prototype 多例,每次從spring容器中獲取到的bean都是不同的對象。

spring web又對Scope進行了擴展,增加了:

  • RequestScope 同一次請求從spring容器中獲取到的bean都是同一個對象。
  • SessionScope 同一個會話從spring容器中獲取到的bean都是同一個對象。

即便如此,有些場景還是無法滿足我們的要求。

比如,我們想在同一個線程中從spring容器獲取到的bean都是同一個對象,該怎么辦?

這就需要自定義Scope了。

第一步實現Scope接口:

publicclassThreadLocalScopeimplementsScope{
privatestaticfinalThreadLocalTHREAD_LOCAL_SCOPE=newThreadLocal();

@Override
publicObjectget(Stringname,ObjectFactoryobjectFactory){
Objectvalue=THREAD_LOCAL_SCOPE.get();
if(value!=null){
returnvalue;
}

Objectobject=objectFactory.getObject();
THREAD_LOCAL_SCOPE.set(object);
returnobject;
}

@Override
publicObjectremove(Stringname){
THREAD_LOCAL_SCOPE.remove();
returnnull;
}

@Override
publicvoidregisterDestructionCallback(Stringname,Runnablecallback){
}

@Override
publicObjectresolveContextualObject(Stringkey){
returnnull;
}

@Override
publicStringgetConversationId(){
returnnull;
}
}

第二步將新定義的Scope注入到spring容器中:

@Component
publicclassThreadLocalBeanFactoryPostProcessorimplementsBeanFactoryPostProcessor{
@Override
publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{
beanFactory.registerScope("threadLocalScope",newThreadLocalScope());
}
}

第三步使用新定義的Scope:

@Scope("threadLocalScope")
@Service
publicclassCService{
publicvoidadd(){
}
}


審核編輯 :李倩


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 框架
    +關注

    關注

    0

    文章

    403

    瀏覽量

    17487
  • 容器
    +關注

    關注

    0

    文章

    495

    瀏覽量

    22061
  • spring
    +關注

    關注

    0

    文章

    340

    瀏覽量

    14343

原文標題:聊聊 Spring 中最常用的 11 個擴展點

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Spring事務實現原理

    作者:京東零售 范錫軍 1、引言 springspring-tx模塊提供了對事務管理支持,使用spring事務可以讓我們從復雜的事務處理中得到解脫,無需要去處理獲得連接、關閉連接、事務提交和回滾等
    的頭像 發表于 11-08 10:10 ?824次閱讀
    <b class='flag-5'>Spring</b>事務實現原理

    對電動汽車電池11大誤解的解答

    近幾年,電動汽車技術快速發展,尤其是其電池技術,本文將探索關于電動汽車(EV)電池——這一汽車行業中最具前景的新技術之一——的11最常見誤解,以及與無線電池管理系統(BMS)相關的內
    的頭像 發表于 11-07 17:18 ?894次閱讀

    電感碎了一點點能正常用

    電子發燒友網站提供《電感碎了一點點能正常用嗎.docx》資料免費下載
    發表于 09-04 11:32 ?0次下載

    Spring Cloud Gateway網關框架

    Spring Cloud Gateway網關框架 本軟件微服務架構中采用Spring Cloud Gateway網關控制框架,Spring Cloud Gateway是Spring C
    的頭像 發表于 08-22 09:58 ?494次閱讀
    <b class='flag-5'>Spring</b> Cloud Gateway網關框架

    wds的常用組網有哪幾種?

    (Point-to-Point)模式 點對點模式是WDS中最常用的一種組網方式,主要用于實現兩或多個無線設備之間的直接通信。在這種模式下,兩無線設備之間建立一無線鏈路,實現數據的
    的頭像 發表于 07-18 09:52 ?1045次閱讀

    玩轉Spring狀態機

    說起Spring狀態機,大家很容易聯想到這個狀態機和設計模式中狀態模式的區別是啥呢?沒錯,Spring狀態機就是狀態模式的一種實現,在介紹Spring狀態機之前,讓我們來看看設計模式中的狀態模式
    的頭像 發表于 06-25 14:21 ?953次閱讀
    玩轉<b class='flag-5'>Spring</b>狀態機

    功能測試覆蓋中最常見的是什么方法

    功能測試覆蓋是軟件測試過程中的一重要環節,它主要關注軟件產品的功能實現是否符合需求規格說明。在功能測試覆蓋中,有多種方法可以采用,以確保測試的全面性和有效性。本文將詳細介紹功能測試覆蓋中最常
    的頭像 發表于 05-30 14:55 ?714次閱讀

    SpingBoot的5擴展,超級實用!

    我們在啟動Spring Boot項目的時候,是執行這樣一方法來啟動的
    的頭像 發表于 02-22 11:28 ?470次閱讀
    SpingBoot的5<b class='flag-5'>個</b><b class='flag-5'>擴展</b><b class='flag-5'>點</b>,超級實用!

    光學鏡頭常用的光機界面

    若光學透鏡的球形光學表面與機械零件的尖角處直接接觸,則該光機界面為尖角界面,實際加工出來的“尖角”是一較小的圓角。 尖角界面加工最為簡單,加工成本也最低廉,也是光學鏡頭結構體中最常用的光機界面。
    發表于 02-21 12:38 ?877次閱讀
    光學鏡頭<b class='flag-5'>常用</b>的光機界面

    計算機網絡中最常用的通信協議

    絞線是目前最常用的傳輸介質之一,它由兩根絞在一起的導線組成,可用于傳輸模擬或數字信號。同軸電纜也是一種常用的傳輸介質,它由內外兩層導體組成,中間填充絕緣材料。
    發表于 02-19 09:51 ?746次閱讀

    常用電氣元件的原理及應用

    斷路器、接觸器、中間繼電器、熱繼電器、按鈕、指示燈、萬能轉換開關和行程開關是電氣控制回路中最常見的八種元件,小編以圖文并茂的方式介紹常用電氣元件的原理及應用,通過了解它們在電氣回路中的作用來掌握這些元件平時的運行情況。
    的頭像 發表于 01-20 10:45 ?5081次閱讀
    <b class='flag-5'>常用</b>電氣元件的原理及應用

    最常見的直流負載工作方式

    見的直流負載工作方式。 1. 固定電阻負載 固定電阻負載是最簡單和最常見的直流負載方式之一。它由一或多個電阻器組成,用于產生一恒定的電阻值,從而使電路中的電流保持穩定。這種負載常用
    的頭像 發表于 01-18 15:12 ?771次閱讀

    plc中常用的編寫程序的語言有哪些 plc中上升沿和下降沿怎么用

    梯形圖是PLC中最常用和最廣泛支持的編程語言。它以線圖的形式展現,使用繼電器邏輯圖形符號來表示邏輯關系和控制流程。
    發表于 01-10 15:04 ?697次閱讀

    Spring事務傳播性的相關知識

    本文主要介紹了Spring事務傳播性的相關知識。
    的頭像 發表于 01-10 09:29 ?447次閱讀
    <b class='flag-5'>Spring</b>事務傳播性的相關知識

    使用Spring Boot 3.2虛擬線程搭建靜態文件服務器

    Spring Boot 3.2 于 2023 年 11 月大張旗鼓地發布,標志著 Java 開發領域的一關鍵時刻。這一突破性的版本引入了一系列革命性的功能。
    的頭像 發表于 01-09 09:34 ?1126次閱讀
    使用<b class='flag-5'>Spring</b> Boot 3.2虛擬線程搭建靜態文件服務器
    主站蜘蛛池模板: 国产片一区二区三区| 欧美日韩国产一区二区三区不卡| 一级毛片免费全部播放完整| 天堂网最新版中文| 奇米影视四色首页手机在线| 国内精品 第一页| 91免费在线视频| 天堂中文在线网| 狠狠躁夜夜躁人人爽天天段| 午夜三级福利| 人人干天天操| 久久99操| 在线观看视频一区| 四虎永久在线免费观看| 天堂网bt| 97午夜影院| 天天做天天添天天谢| 神马影院午夜dy888| 亚洲三级黄色| 欧美日韩国产一区二区三区不卡| 波多野结衣在线网址| 国产毛片精品| 一级aaaaa毛片免费视频| 成年人污视频| 特黄特色| 窝窝午夜视频| 2018国产精品| 色男人的天堂| 国产精品资源网| 六月丁香啪啪| 人人精品久久| 在线视频 亚洲| 性猛交毛片| 热久久综合这里只有精品电影| 天天躁夜夜躁狠狠躁躁| 九九re热| 人人草人人爽| 88av免费观看| 四虎影院在线观看网站| 天天综合天天| 五月综合色婷婷影院在线观看|