自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring中那些讓你愛不釋手的代碼技巧(續(xù)集)

開發(fā) 前端
本文繼續(xù)總結(jié)我認為spring中還不錯的知識點,希望對您有所幫助。

[[377975]]

 前言

上一篇文章《spring中這些能升華代碼的技巧,可能會讓你愛不釋手》發(fā)表之后,受到了不少讀者的好評,很多讀者都在期待續(xù)集。今天非常高興的通知大家,你們要的續(xù)集來了。本文繼續(xù)總結(jié)我認為spring中還不錯的知識點,希望對您有所幫助。

一. @Conditional的強大之處

不知道你們有沒有遇到過這些問題:

  1. 某個功能需要根據(jù)項目中有沒有某個jar判斷是否開啟該功能。
  2. 某個bean的實例化需要先判斷另一個bean有沒有實例化,再判斷是否實例化自己。
  3. 某個功能是否開啟,在配置文件中有個參數(shù)可以對它進行控制。

如果你有遇到過上述這些問題,那么恭喜你,本節(jié)內(nèi)容非常適合你。

@ConditionalOnClass

問題1可以用@ConditionalOnClass注解解決,代碼如下:

  1. public  class A { 
  2.  
  3. public  class B { 
  4.  
  5. @ConditionalOnClass(B.class) 
  6. @Configuration 
  7. public class TestConfiguration { 
  8.  
  9.     @Bean 
  10.     public A a() { 
  11.       return    new A(); 
  12.     } 

如果項目中存在B類,則會實例化A類。如果不存在B類,則不會實例化A類。

有人可能會問:不是判斷有沒有某個jar嗎?怎么現(xiàn)在判斷某個類了?

  • 直接判斷有沒有該jar下的某個關(guān)鍵類更簡單。

這個注解有個升級版的應用場景:比如common工程中寫了一個發(fā)消息的工具類mqTemplate,業(yè)務工程引用了common工程,只需再引入消息中間件,比如rocketmq的jar包,就能開啟mqTemplate的功能。而如果有另一個業(yè)務工程,通用引用了common工程,如果不需要發(fā)消息的功能,不引入rocketmq的jar包即可。

這個注解的功能還是挺實用的吧?

@ConditionalOnBean

問題2可以通過@ConditionalOnBean注解解決,代碼如下:

  1. @Configuration 
  2. public  class TestConfiguration { 
  3.  
  4.     @Bean 
  5.     public B b() { 
  6.         return  new B(); 
  7.     } 
  8.  
  9.     @ConditionalOnBean(name="b"
  10.     @Bean 
  11.     public A a() { 
  12.       return    new A(); 
  13.     } 

實例A只有在實例B存在時,才能實例化。

@ConditionalOnProperty

問題3可以通過@ConditionalOnProperty注解解決,代碼如下:

  1. @ConditionalOnProperty(prefix = "demo",name="enable", havingValue = "true",matchIfMissing=true ) 
  2. @Configuration 
  3. public  class TestConfiguration { 
  4.  
  5.     @Bean 
  6.     public A a() { 
  7.       return    new A(); 
  8.     } 

在applicationContext.properties文件中配置參數(shù):

  1. demo.enable=false 

各參數(shù)含義:

  • prefix 表示參數(shù)名的前綴,這里是demo
  • name 表示參數(shù)名
  • havingValue 表示指定的值,參數(shù)中配置的值需要跟指定的值比較是否相等,相等才滿足條件
  • matchIfMissing 表示是否允許缺省配置。

這個功能可以作為開關(guān),相比EnableXXX注解的開關(guān)更優(yōu)雅,因為它可以通過參數(shù)配置是否開啟,而EnableXXX注解的開關(guān)需要在代碼中硬編碼開啟或關(guān)閉。

其他的Conditional注解

當然,spring用得比較多的Conditional注解還有:ConditionalOnMissingClass、ConditionalOnMissingBean、ConditionalOnWebApplication等。

下面用一張圖整體認識一下@Conditional家族。


自定義Conditional

說實話,個人認為springboot自帶的Conditional系列已經(jīng)可以滿足我們絕大多數(shù)的需求了。但如果你有比較特殊的場景,也可以自定義自定義Conditional。

第一步,自定義注解:

  1. @Conditional(MyCondition.class) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Target({ElementType.TYPE, ElementType.METHOD}) 
  4. @Documented 
  5. public  @interface MyConditionOnProperty { 
  6.     String name() default ""
  7.  
  8.     String havingValue() default ""

第二步,實現(xiàn)Condition接口:

  1. public  class MyCondition implements Condition { 
  2.     @Override 
  3.     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 
  4.         System.out.println("實現(xiàn)自定義邏輯"); 
  5.         return  false
  6.     } 

第三步,使用@MyConditionOnProperty注解。

Conditional的奧秘就藏在ConfigurationClassParser類的processConfigurationClass方法中:


這個方法邏輯不復雜:


  1. 先判斷有沒有使用Conditional注解,如果沒有直接返回false
  2. 收集condition到集合中
  3. 按order排序該集合
  4. 遍歷該集合,循環(huán)調(diào)用condition的matchs方法。

二. 如何妙用@Import?

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

如果你看過它的源碼會發(fā)現(xiàn),引入的類支持三種不同類型。

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


普通類

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

  1. public  class A { 
  2.  
  3. @Import(A.class) 
  4. @Configuration 
  5. public class TestConfiguration { 

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

  1. @Autowired 
  2. ivate A a; 

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

@Configuration注解的配置類

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

  • @Import
  • @ImportResource
  • @PropertySource等。
  1. public  class A { 
  2.  
  3. public  class B { 
  4.  
  5. @Import(B.class) 
  6. @Configuration 
  7. public class AConfiguration { 
  8.  
  9.     @Bean 
  10.     public A a() { 
  11.         return  new A(); 
  12.     } 
  13.  
  14. @Import(AConfiguration.class) 
  15. @Configuration 
  16. public class TestConfiguration { 

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

由于文章篇幅有限不過多介紹了,這里留點懸念,后面會出一篇文章專門介紹@Configuration注解,因為它實在太太太重要了。

實現(xiàn)ImportSelector接口的類

這種引入方式需要實現(xiàn)ImportSelector接口:

  1. public  class AImportSelector implements ImportSelector { 
  2.  
  3. private  static  final String CLASS_NAME = "com.sue.cache.service.test13.A"
  4.      
  5.  public String[] selectImports(AnnotationMetadata importingClassMetadata) { 
  6.         return  new String[]{CLASS_NAME}; 
  7.     } 
  8.  
  9. @Import(AImportSelector.class) 
  10. @Configuration 
  11. public class TestConfiguration { 

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

實現(xiàn)ImportBeanDefinitionRegistrar接口的類

這種引入方式需要實現(xiàn)ImportBeanDefinitionRegistrar接口:

  1. public  class AImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { 
  2.     @Override 
  3.     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 
  4.         RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(A.class); 
  5.         registry.registerBeanDefinition("a", rootBeanDefinition); 
  6.     } 
  7.  
  8. @Import(AImportBeanDefinitionRegistrar.class) 
  9. @Configuration 
  10. public class TestConfiguration { 

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

當然@import注解非常人性化,還支持同時引入多種不同類型的類。

  1. @Import({B.class,AImportBeanDefinitionRegistrar.class}) 
  2. @Configuration 
  3. public class TestConfiguration { 

這四種引入類的方式各有千秋,總結(jié)如下:

  1. 普通類,用于創(chuàng)建沒有特殊要求的bean實例。
  2. @Configuration注解的配置類,用于層層嵌套引入的場景。
  3. 實現(xiàn)ImportSelector接口的類,用于一次性引入多個類的場景,或者可以根據(jù)不同的配置決定引入不同類的場景。
  4. 實現(xiàn)ImportBeanDefinitionRegistrar接口的類,主要用于可以手動控制BeanDefinition的創(chuàng)建和注冊的場景,它的方法中可以獲取BeanDefinitionRegistry注冊容器對象。

在ConfigurationClassParser類的processImports方法中可以看到這三種方式的處理邏輯:


最后的else方法其實包含了:普通類和@Configuration注解的配置類兩種不同的處理邏輯。

三. @ConfigurationProperties賦值

我們在項目中使用配置參數(shù)是非常常見的場景,比如,我們在配置線程池的時候,需要在applicationContext.propeties文件中定義如下配置:

  1. thread.pool.corePoolSize=5 
  2. thread.pool.maxPoolSize=10 
  3. thread.pool.queueCapacity=200 
  4. thread.pool.keepAliveSeconds=30 

方法一:通過@Value注解讀取這些配置。

  1. public  class ThreadPoolConfig { 
  2.  
  3.     @Value("${thread.pool.corePoolSize:5}"
  4.     private  int corePoolSize; 
  5.  
  6.     @Value("${thread.pool.maxPoolSize:10}"
  7.     private  int maxPoolSize; 
  8.  
  9.     @Value("${thread.pool.queueCapacity:200}"
  10.     private  int queueCapacity; 
  11.  
  12.     @Value("${thread.pool.keepAliveSeconds:30}"
  13.     private  int keepAliveSeconds; 
  14.  
  15.     @Value("${thread.pool.threadNamePrefix:ASYNC_}"
  16.     private String threadNamePrefix; 
  17.  
  18.     @Bean 
  19.     public Executor threadPoolExecutor() { 
  20.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
  21.         executor.setCorePoolSize(corePoolSize); 
  22.         executor.setMaxPoolSize(maxPoolSize); 
  23.         executor.setQueueCapacity(queueCapacity); 
  24.         executor.setKeepAliveSeconds(keepAliveSeconds); 
  25.         executor.setThreadNamePrefix(threadNamePrefix); 
  26.         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 
  27.         executor.initialize(); 
  28.         return executor; 
  29.     } 

這種方式使用起來非常簡單,但建議在使用時都加上:,因為:后面跟的是默認值,比如:@Value("${thread.pool.corePoolSize:5}"),定義的默認核心線程數(shù)是5。

  • 假如有這樣的場景:business工程下定義了這個ThreadPoolConfig類,api工程引用了business工程,同時job工程也引用了business工程,而ThreadPoolConfig類只想在api工程中使用。這時,如果不配置默認值,job工程啟動的時候可能會報錯。

如果參數(shù)少還好,多的話,需要給每一個參數(shù)都加上@Value注解,是不是有點麻煩?

此外,還有一個問題,@Value注解定義的參數(shù)看起來有點分散,不容易辨別哪些參數(shù)是一組的。

這時,@ConfigurationProperties就派上用場了,它是springboot中新加的注解。

第一步,先定義ThreadPoolProperties類

  1. @Data 
  2. @Component 
  3. @ConfigurationProperties("thread.pool"
  4. public  class ThreadPoolProperties { 
  5.  
  6.     private  int corePoolSize; 
  7.     private  int maxPoolSize; 
  8.     private  int queueCapacity; 
  9.     private  int keepAliveSeconds; 
  10.     private String threadNamePrefix; 

第二步,使用ThreadPoolProperties類

  1. @Configuration 
  2. public  class ThreadPoolConfig { 
  3.  
  4.     @Autowired 
  5.     private ThreadPoolProperties threadPoolProperties; 
  6.  
  7.     @Bean 
  8.     public Executor threadPoolExecutor() { 
  9.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
  10.         executor.setCorePoolSize(threadPoolProperties.getCorePoolSize()); 
  11.         executor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize()); 
  12.         executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); 
  13.         executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); 
  14.         executor.setThreadNamePrefix(threadPoolProperties.getThreadNamePrefix()); 
  15.         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 
  16.         executor.initialize(); 
  17.         return executor; 
  18.     } 

使用@ConfigurationProperties注解,可以將thread.pool開頭的參數(shù)直接賦值到ThreadPoolProperties類的同名參數(shù)中,這樣省去了像@Value注解那樣一個個手動去對應的過程。

這種方式顯然要方便很多,我們只需編寫xxxProperties類,spring會自動裝配參數(shù)。此外,不同系列的參數(shù)可以定義不同的xxxProperties類,也便于管理,推薦優(yōu)先使用這種方式。

它的底層是通過:ConfigurationPropertiesBindingPostProcessor類實現(xiàn)的,該類實現(xiàn)了BeanPostProcessor接口,在postProcessBeforeInitialization方法中解析@ConfigurationProperties注解,并且綁定數(shù)據(jù)到相應的對象上。

綁定是通過Binder類的bindObject方法完成的:


以上這段代碼會遞歸綁定數(shù)據(jù),主要考慮了三種情況:

  • bindAggregate 綁定集合類
  • bindBean 綁定對象
  • bindProperty 綁定參數(shù) 前面兩種情況最終也會調(diào)用到bindProperty方法。

「此外,友情提醒一下:」

使用@ConfigurationProperties注解有些場景有問題,比如:在apollo中修改了某個參數(shù),正常情況可以動態(tài)更新到@ConfigurationProperties注解定義的xxxProperties類的對象中,但是如果出現(xiàn)比較復雜的對象,比如:

  1. private Map<String, Map<String,String>>  urls; 

可能動態(tài)更新不了。

這時候該怎么辦呢?

答案是使用ApolloConfigChangeListener監(jiān)聽器自己處理:

  1. @ConditionalOnClass(com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig.class) 
  2. public class ApolloConfigurationAutoRefresh implements ApplicationContextAware { 
  3.    private ApplicationContext applicationContext; 
  4.     
  5.    @Override 
  6.    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
  7.         this.applicationContext = applicationContext; 
  8.    } 
  9.     
  10.     @ApolloConfigChangeListener 
  11.     private void onChange(ConfigChangeEvent changeEvent{ 
  12.         refreshConfig(changeEvent.changedKeys()); 
  13.     } 
  14.     private void refreshConfig(Set<String> changedKeys){ 
  15.        System.out.println("將變更的參數(shù)更新到相應的對象中"); 
  16.     } 

四. spring事務要如何避坑?

spring中的事務功能主要分為:聲明式事務和編程式事務。

聲明式事務

大多數(shù)情況下,我們在開發(fā)過程中使用更多的可能是聲明式事務,即使用@Transactional注解定義的事務,因為它用起來更簡單,方便。

只需在需要執(zhí)行的事務方法上,加上@Transactional注解就能自動開啟事務:

  1. @Service 
  2. public  class UserService { 
  3.  
  4.     @Autowired 
  5.     private UserMapper userMapper; 
  6.      
  7.     @Transactional 
  8.     public void add(UserModel userModel) { 
  9.         userMapper.insertUser(userModel); 
  10.     } 

這種聲明式事務之所以能生效,是因為它的底層使用了AOP,創(chuàng)建了代理對象,調(diào)用TransactionInterceptor攔截器實現(xiàn)事務的功能。

  • spring事務有個特別的地方:它獲取的數(shù)據(jù)庫連接放在ThreadLocal中的,也就是說同一個線程中從始至終都能獲取同一個數(shù)據(jù)庫連接,可以保證同一個線程中多次數(shù)據(jù)庫操作在同一個事務中執(zhí)行。

正常情況下是沒有問題的,但是如果使用不當,事務會失效,主要原因如下:


除了上述列舉的問題之外,由于@Transactional注解最小粒度是要被定義在方法上,如果有多層的事務方法調(diào)用,可能會造成大事務問題。


所以,建議在實際工作中少用@Transactional注解開啟事務。

編程式事務

一般情況下編程式事務我們可以通過TransactionTemplate類開啟事務功能。有個好消息,就是springboot已經(jīng)默認實例化好這個對象了,我們能直接在項目中使用。

  1. @Service 
  2. public  class UserService { 
  3.    @Autowired 
  4.    private TransactionTemplate transactionTemplate; 
  5.     
  6.    ... 
  7.     
  8.    public void save(final User user) { 
  9.          transactionTemplate.execute((status) => { 
  10.             doSameThing... 
  11.             return Boolean.TRUE
  12.          }) 
  13.    } 

使用TransactionTemplate的編程式事務能避免很多事務失效的問題,但是對大事務問題,不一定能夠解決,只是說相對于使用@Transactional注解要好些。

五. 跨域問題的解決方案

關(guān)于跨域問題,前后端的解決方案還是挺多的,這里我重點說說spring的解決方案,目前有三種:

一.使用@CrossOrigin注解

  1. @RequestMapping("/user"
  2. @RestController 
  3. public  class UserController { 
  4.  
  5.     @CrossOrigin(origins = "http://localhost:8016"
  6.     @RequestMapping("/getUser"
  7.     public String getUser(@RequestParam("name") String name) { 
  8.         System.out.println("name:" + name); 
  9.         return  "success"
  10.     } 

該方案需要在跨域訪問的接口上加@CrossOrigin注解,訪問規(guī)則可以通過注解中的參數(shù)控制,控制粒度更細。如果需要跨域訪問的接口數(shù)量較少,可以使用該方案。

二.增加全局配置

  1. @Configuration 
  2. public  class WebConfig implements WebMvcConfigurer { 
  3.  
  4.     @Override 
  5.     public void addCorsMappings(CorsRegistry registry) { 
  6.         registry.addMapping("/**"
  7.                 .allowedOrigins("*"
  8.                 .allowedMethods("GET""POST"
  9.                 .allowCredentials(true
  10.                 .maxAge(3600) 
  11.                 .allowedHeaders("*"); 
  12.  
  13.     } 

該方案需要實現(xiàn)WebMvcConfigurer接口,重寫addCorsMappings方法,在該方法中定義跨域訪問的規(guī)則。這是一個全局的配置,可以應用于所有接口。

三.自定義過濾器

  1. @WebFilter("corsFilter"
  2. @Configuration 
  3. public  class CorsFilter implements Filter { 
  4.  
  5.     @Override 
  6.     public void init(FilterConfig filterConfig) throws ServletException { 
  7.  
  8.     } 
  9.  
  10.     @Override 
  11.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
  12.         HttpServletResponse httpServletResponse = (HttpServletResponse) response; 
  13.         httpServletResponse.setHeader("Access-Control-Allow-Origin""*"); 
  14.         httpServletResponse.setHeader("Access-Control-Allow-Methods""POST, GET"); 
  15.         httpServletResponse.setHeader("Access-Control-Max-Age""3600"); 
  16.         httpServletResponse.setHeader("Access-Control-Allow-Headers""x-requested-with"); 
  17.         chain.doFilter(request, response); 
  18.     } 
  19.  
  20.     @Override 
  21.     public void destroy() { 
  22.  
  23.     } 

該方案通過在請求的header中增加Access-Control-Allow-Origin等參數(shù)解決跨域問題。

順便說一下,使用@CrossOrigin注解 和 實現(xiàn)WebMvcConfigurer接口的方案,spring在底層最終都會調(diào)用到DefaultCorsProcessor類的handleInternal方法:


最終三種方案殊途同歸,都會往header中添加跨域需要參數(shù),只是實現(xiàn)形式不一樣而已。

六. 如何自定義starter

以前在沒有使用starter時,我們在項目中需要引入新功能,步驟一般是這樣的:

  • 在maven倉庫找該功能所需jar包
  • 在maven倉庫找該jar所依賴的其他jar包
  • 配置新功能所需參數(shù)

以上這種方式會帶來三個問題:

  1. 如果依賴包較多,找起來很麻煩,容易找錯,而且要花很多時間。
  2. 各依賴包之間可能會存在版本兼容性問題,項目引入這些jar包后,可能沒法正常啟動。
  3. 如果有些參數(shù)沒有配好,啟動服務也會報錯,沒有默認配置。

「為了解決這些問題,springboot的starter機制應運而生」。

starter機制帶來這些好處:

  1. 它能啟動相應的默認配置。
  2. 它能夠管理所需依賴,擺脫了需要到處找依賴 和 兼容性問題的困擾。
  3. 自動發(fā)現(xiàn)機制,將spring.factories文件中配置的類,自動注入到spring容器中。
  4. 遵循“約定大于配置”的理念。

在業(yè)務工程中只需引入starter包,就能使用它的功能,太爽了。

下面用一張圖,總結(jié)starter的幾個要素:


接下來我們一起實戰(zhàn),定義一個自己的starter。

第一步,創(chuàng)建id-generate-starter工程:

其中的pom.xml配置如下:

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2.  
  3. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
  4.     <modelVersion>4.0.0</modelVersion> 
  5.  
  6.     <version>1.3.1</version> 
  7.     <groupId>com.sue</groupId> 
  8.     <artifactId>id-generate-spring-boot-starter</artifactId> 
  9.     <name>id-generate-spring-boot-starter</name
  10.     <dependencies> 
  11.         <dependency> 
  12.             <groupId>com.sue</groupId> 
  13.             <artifactId>id-generate-spring-boot-autoconfigure</artifactId> 
  14.             <version>1.3.1</version> 
  15.         </dependency> 
  16.     </dependencies> 
  17. </project> 

第二步,創(chuàng)建id-generate-spring-boot-autoconfigure工程:

該項目當中包含:

  • pom.xml
  • spring.factories
  • IdGenerateAutoConfiguration
  • IdGenerateService

IdProperties pom.xml配置如下:

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2.  
  3. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
  4.  
  5.     <parent> 
  6.         <groupId>org.springframework.boot</groupId> 
  7.         <artifactId>spring-boot-starter-parent</artifactId> 
  8.         <version>2.0.4.RELEASE</version> 
  9.     </parent> 
  10.     <modelVersion>4.0.0</modelVersion> 
  11.     <version>1.3.1</version> 
  12.     <groupId>com.sue</groupId> 
  13.     <artifactId>id-generate-spring-boot-autoconfigure</artifactId> 
  14.     <name>id-generate-spring-boot-autoconfigure</name
  15.  
  16.     <dependencies> 
  17.         <dependency> 
  18.             <groupId>org.springframework.boot</groupId> 
  19.             <artifactId>spring-boot-starter</artifactId> 
  20.         </dependency> 
  21.  
  22.         <dependency> 
  23.             <groupId>org.springframework.boot</groupId> 
  24.             <artifactId>spring-boot-autoconfigure</artifactId> 
  25.         </dependency> 
  26.  
  27.         <dependency> 
  28.             <groupId>org.springframework.boot</groupId> 
  29.             <artifactId>spring-boot-configuration-processor</artifactId> 
  30.             <optional>true</optional> 
  31.         </dependency> 
  32.     </dependencies> 
  33.  
  34.     <build> 
  35.         <plugins> 
  36.             <plugin> 
  37.                 <groupId>org.apache.maven.plugins</groupId> 
  38.                 <artifactId>maven-compiler-plugin</artifactId> 
  39.                 <configuration> 
  40.                     <source>1.8</source> 
  41.                     <target>1.8</target> 
  42.                 </configuration> 
  43.             </plugin> 
  44.         </plugins> 
  45.     </build> 
  46. </project> 

spring.factories配置如下:

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.sue.IdGenerateAutoConfiguration 

IdGenerateAutoConfiguration類:

  1. @ConditionalOnClass(IdProperties.class) 
  2. @EnableConfigurationProperties(IdProperties.class) 
  3. @Configuration 
  4. public class IdGenerateAutoConfiguration { 
  5.  
  6.     @Autowired 
  7.     private IdProperties properties; 
  8.  
  9.     @Bean 
  10.     public IdGenerateService idGenerateService() { 
  11.         return  new IdGenerateService(properties.getWorkId()); 
  12.     } 

IdGenerateService類:

  1. public  class IdGenerateService { 
  2.  
  3.     private Long workId; 
  4.  
  5.     public IdGenerateService(Long workId) { 
  6.         this.workId = workId; 
  7.     } 
  8.  
  9.     public Long generate() { 
  10.         return  new Random().nextInt(100) + this.workId; 
  11.     } 

IdProperties類:

  1. @ConfigurationProperties(prefix = IdProperties.PREFIX) 
  2. public  class IdProperties { 
  3.  
  4.  
  5.     public  static  final String PREFIX = "sue"
  6.  
  7.     private Long workId; 
  8.  
  9.     public Long getWorkId() { 
  10.         return workId; 
  11.     } 
  12.  
  13.     public void setWorkId(Long workId) { 
  14.         this.workId = workId; 
  15.     } 

這樣在業(yè)務項目中引入相關(guān)依賴:

  1. <dependency> 
  2.       <groupId>com.sue</groupId> 
  3.       <artifactId>id-generate-spring-boot-starter</artifactId> 
  4.       <version>1.3.1</version> 
  5. </dependency> 

就能使用注入使用IdGenerateService的功能了

  1. @Autowired 
  2.  private IdGenerateService idGenerateService; 

完美。

七.項目啟動時的附加功能

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

好消息是springboot提供了:

  • CommandLineRunner
  • ApplicationRunner

這兩個接口幫助我們實現(xiàn)以上需求。

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

  1. @Component 
  2. public  class TestRunner implements ApplicationRunner { 
  3.  
  4.     @Autowired 
  5.     private LoadDataService loadDataService; 
  6.  
  7.     public void run(ApplicationArguments args) throws Exception { 
  8.         loadDataService.load(); 
  9.     } 
  10.      

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

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

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

springboot項目啟動時主要流程是這樣的:


在SpringApplication類的callRunners方法中,我們能看到這兩個接口的具體調(diào)用:


最后還有一個問題:這兩個接口有什么區(qū)別?

  • CommandLineRunner接口中run方法的參數(shù)為String數(shù)組
  • ApplicationRunner中run方法的參數(shù)為ApplicationArguments,該參數(shù)包含了String數(shù)組參數(shù) 和 一些可選參數(shù)。

嘮嘮家常

寫著寫著又有這么多字了,按照慣例,為了避免篇幅過長,今天就先寫到這里。預告一下,后面會有AOP、BeanPostProcessor、Configuration注解等核心知識點的專題,每個主題的內(nèi)容都挺多的,可以期待一下喔。

 

責任編輯:姜華 來源: 蘇三說技術(shù)
相關(guān)推薦

2021-01-18 09:55:46

Spring代碼Java

2021-01-11 08:21:34

spring升華代碼的技巧開發(fā)

2011-09-16 09:06:20

Smalltalk

2010-12-08 09:59:10

CSS

2011-03-31 14:22:28

Chrome插件

2024-12-17 15:00:00

Python代碼

2021-04-25 10:15:38

Python編程語言軟件包

2021-04-13 10:07:08

Python軟件包編程語言

2009-08-29 08:41:07

Windows 7新功能

2024-07-24 08:33:06

JavaScript匯編語言

2011-05-12 09:12:16

Ubuntu 11.0

2020-11-03 15:10:55

Spring Batc框架Java

2017-05-24 18:00:13

AndroidAndroid DatView

2021-02-02 21:42:30

VS Code編輯器開發(fā)

2013-05-06 22:52:10

智能手機交互方式用戶體驗

2021-01-20 06:29:42

JS工具操作符

2021-12-09 13:30:17

微軟

2021-07-13 10:59:49

Python可視化

2016-05-05 10:54:53

Android開發(fā)應用

2023-07-04 08:19:25

IDEA插件
點贊
收藏

51CTO技術(shù)棧公眾號