您好,登錄后才能下訂單哦!
如何自定義springboot starter,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
自定義 springboot starter
搞一個(gè)demo看看,demo實(shí)現(xiàn)了兩個(gè)功能:
使用Aspectj方式的aop,來(lái)實(shí)現(xiàn)對(duì)某類(lèi)函數(shù)參數(shù)監(jiān)控;
定義一個(gè)自己的配置類(lèi),讀取我們?cè)趛ml或properties里面配置的參數(shù),同時(shí)提供一個(gè)service,用來(lái)獲取我們讀取的結(jié)果。
這里是示例工程starter.
看了自動(dòng)配置原理之后,應(yīng)該知道META-INF/spring.facotires這個(gè)家伙是配置的重點(diǎn)了吧,我們很多個(gè)的東西都是在這個(gè)里頭。因此,我們自己的stater當(dāng)然也少不了這個(gè)配置文件,先貼個(gè)工程結(jié)構(gòu)。
spring-boot-myaop-starter ├─ pom.xml ├─ spring-boot-myaop-starter.iml └─ src/main ├─ java └─ resources └─ META-INF └─ spring.factories
先按照正常工程把一些配置都配好,先來(lái)寫(xiě)一個(gè)AspectJ的。
package com.wt.myaop.aspect; @Aspect @Component public class MyAspect { @Pointcut("@annotation(myAnno)") public void myAspectPointCut(MyAopAnnotation myAnno) { } @Before("myAspectPointCut(myAnno)") public void performanceTrance2(JoinPoint joinPoint, MyAopAnnotation myAnno) throws Throwable { System.out.println("--------------myaop-starter args monitor start-----------------"); Object[] args = joinPoint.getArgs(); Class<?>[] types = myAnno.argTypes(); for (int i = 0; i < Math.min(args.length, types.length); i++) { System.out.println("type:" + types[i] + "<--->arg:" + args[i]); } System.out.println("--------------myaop-starter args monitor end-----------------"); } }
我們這里使用的是@Before,在這個(gè)方法里面獲取到所有參數(shù),然后將所有參數(shù)的類(lèi)型和值都打印出來(lái),方法前后輸出日志,比較簡(jiǎn)單。
然后來(lái)看pointCut,這是一個(gè)基于注解的,這樣其他項(xiàng)目引入這個(gè)starter的時(shí)候就可以直接以注解來(lái)使用,對(duì)其他項(xiàng)目不具有侵入性。所以,我們還需要定義一個(gè)注解。
package com.wt.myaop.anno; @Target(ElementType.METHOD) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface MyAopAnnotation { Class<?>[] argTypes() default {}; }
注解里面有屬性,用來(lái)定義方法參數(shù)的類(lèi)型順序,當(dāng)然也可以不需要,放這里只是為了方便。
屬性的讀取我們需要用到一個(gè)springboot的注解@ConfigurationProperties.來(lái)看看這個(gè)配置類(lèi),為了縮短代碼長(zhǎng)度,都沒(méi)有貼getter/setter方法哦。
package com.wt.myaop.config; @ConfigurationProperties(prefix = MyConfig.MY_PREFIX) public class MyConfig { public static final String MY_PREFIX = "myconfig"; /** * 不加該注解也能正常得到值 */ @NestedConfigurationProperty private UserConfig user; private String job; }
代碼中我們定義了一個(gè)常量MY_PREFIX,這個(gè)是配置文件中的前綴,springboot會(huì)讀取這個(gè)前綴的配置來(lái)注入到這個(gè)實(shí)體當(dāng)中,當(dāng)然,配置肯定不會(huì)是在我們這個(gè)starter項(xiàng)目中來(lái)配置,是我們實(shí)際使用的project中配置的哦!要不然我還不如直接寫(xiě)死,對(duì)吧,嘻嘻。
除了基本屬性之外,還提供了一個(gè)實(shí)體屬性,UserConfig。來(lái)看看這個(gè)東西吧。
package com.wt.myaop.config; public class UserConfig { private String username; private Integer age; private String sex; }
配置信息的接收實(shí)體有了,我們現(xiàn)在來(lái)定義一個(gè)service,這個(gè)service的作用就是注入這個(gè)實(shí)體,然后輸出配置信息,以檢查我們的配置生效了。service的接口就不貼了哈,只貼實(shí)現(xiàn)類(lèi)了。
package com.wt.myaop.service; public class MyConfigServiceImpl implements MyConfigService { private final MyConfig myconfig; public MyConfigServiceImpl(MyConfig myConfig) { this.myconfig = myConfig; } @Override public void printMyConfig() { System.out.println(myConfig); } }
看構(gòu)造方法和屬性,構(gòu)造方法注入了我們的配置,當(dāng)然,我們?nèi)绻枰褂眠@個(gè)service,我們就需要手動(dòng)將這個(gè)配置注入進(jìn)來(lái),以確保service實(shí)例化時(shí),myconfig不會(huì)為null,里面的方法很簡(jiǎn)單,僅僅打印了我們的配置類(lèi)MyConfig。
starter像這樣就算完了嗎?當(dāng)然沒(méi)有,可以感受到以上這些其實(shí)都跟普通的project的配置沒(méi)有太多差別,很簡(jiǎn)單。
回憶一下我們自動(dòng)配置的核心是不是叫xxxAutoConfiguration的東西呢?spring.vactories中的哦!
好了,來(lái)吧,開(kāi)始定義我們自己的AutoConfiguration。
package com.wt.myaop.autoconfig; @Configuration @ComponentScan({"com.wt.myaop.aspect"}) @AutoConfigureAfter(AopAutoConfiguration.class) @EnableConfigurationProperties(MyConfig.class) public class MyArgsMonitorAopAutoConfig { private MyConfig myConfig; public MyArgsMonitorAopAutoConfig(MyConfig myConfig) { this.myConfig = myConfig; } @Bean public MyConfigService getMyConfigService(){ return new MyConfigServiceImpl(myConfig); } }
我們現(xiàn)在來(lái)研究一下這個(gè)類(lèi)都有什么。
@Configuration: 表明這個(gè)類(lèi)是一個(gè)配置類(lèi); @ComponentScan: 指出需要掃描的包,可以看到上面掃描的包中包含了Aspect注解的配置,當(dāng)然使用aop這部分是必不可少的; @AutoConfigureAfter: 指出當(dāng)前類(lèi)需要再某個(gè)自動(dòng)配置完成之后才開(kāi)始配置; @EnableConfigurationProperties: 為@ConfigurationProperties注解提供支持,什么意思呢; 解釋一下這個(gè),把@ConfigurationProperties標(biāo)注的類(lèi)(MyConfig)注冊(cè)成bean,以支持依賴注入,本身這些類(lèi) 是不會(huì)被注冊(cè)成bean的,當(dāng)然我們可以在配置類(lèi)上加@Component注解。使用這個(gè)注解可以在我們需要某個(gè)配置類(lèi) 注冊(cè)成bean的時(shí)候,就使用,侵入性小,避免了配置類(lèi)上加@Component注解。
從上面代碼中可以看到我們的配置類(lèi)MyConfig通過(guò)構(gòu)造方法被注入到了MyArgsMonitorAopAutoConfig當(dāng)中,而MyArgsMonitorAopAutoConfig是一個(gè)配置類(lèi),里面通過(guò)@Bean的方式配置了MyConfigService。
看起來(lái)很簡(jiǎn)答吧,實(shí)際上也很簡(jiǎn)單,autoConfiguration的配置類(lèi)就好了,引入這個(gè)類(lèi),就相當(dāng)于引入了一個(gè)Myconfig的配置類(lèi),和一個(gè)我們能夠使用的MyConfigService。
自動(dòng)配置的類(lèi)有了,需要放到spring.factories中才能生效。
# META-INF/spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.wt.myaop.autoconfig.MyArgsMonitorAopAutoConfig
這樣starter部分就算是完成了。
Tips: 自動(dòng)配置其實(shí)很簡(jiǎn)單,把需要的配置都放到一個(gè)叫做starter的工程里面,然后創(chuàng)建一個(gè)XxxAutoConfiguration的自動(dòng)配置類(lèi),把需要的配置都放到這個(gè)類(lèi)里面配置好。最后把這個(gè)自動(dòng)配置類(lèi)加到META-INF/spring.factories中就行了,是不是很容易,嘻嘻。
demo其實(shí)也是使用的之前分析源碼那個(gè)工程,下面有地址。重點(diǎn)來(lái)配置和啟動(dòng)一下我們自己的starter。
這里是示例工程demo.
有了starter之后,使用mvn clean install命令將其jar安裝到本地。
在demo工程中引入這個(gè)依賴。
<dependency> <groupId>com.wt.starter</groupId> <artifactId>spring-boot-myaop-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
我們starter里面有個(gè)MyConfig的配置對(duì)吧,好了,現(xiàn)在我們可以在yml或者properties文件中來(lái)定義了。我這里用的是yml哈。
myconfig: job: programer user: username: wt age: 25 sex: real_man
注意我們的前綴是myconfig,下面的job和user都是實(shí)體類(lèi)MyConfig的屬性,因?yàn)閡ser是個(gè)實(shí)體了類(lèi),因此下面也列出了它的屬性u(píng)sername,age,sex.(可以去前面的MyConfig和UserConfig兩個(gè)實(shí)體的屬性進(jìn)行對(duì)比)。
starter中除了上面的MyConfig之外,還有個(gè)aop監(jiān)控參數(shù)的功能,我們馬上把它用起來(lái)。
先定義一個(gè)service,接口就不貼了
@Service public class MyStarterServiceImpl implements MyStarterService { @Override @MyAopAnnotation(argTypes = {String.class}) public void helloStarter(String msg, Long currentTime) { System.out.println("----hello starter,msg = " + msg + ",currentTime = " + currentTime); } }
方法加上了注解MyAopAnnotation,為了被starter當(dāng)中的aop攔截到。
再來(lái)定義一個(gè)controller用來(lái)測(cè)試。
@RestController @RequestMapping("/starter") public class MyStarterController { @Autowired private MyConfigService myConfigService; @Autowired private MyStarterService myStarterService; @RequestMapping("/test") public Object starter() { myConfigService.printMyConfig(); myStarterService.helloStarter("wt", System.currentTimeMillis()); return "success"; } }
注意區(qū)分一下,MyConfigService是我們starter里面的,MyStarterService是我們demo工程里面的。
現(xiàn)在啟動(dòng)工程,訪問(wèn)http://127.0.0.1:8080/starter/test試試!
來(lái)看看輸出(復(fù)制了輸出,沒(méi)有使用截圖):
MyConfig{user=UserConfig{username='wt', age=25, sex='real_man'}, job='programer'} ------------------myaop-starter args monitor start-------------------- type:class java.lang.String<--->arg:wt ------------------myaop-starter args monitor end-------------------- ----hello starter,msg = wt,currentTime = 1564472340205
第一行是 myConfigService.printMyConfig()的輸出,他輸出了我們的在yml中的配置,這個(gè)輸出說(shuō)明了我們的配置被成功賦值給MyConfig了。說(shuō)明我們的config生效了。
第二行和第四行是starter當(dāng)中aop部分@Before方法的前后輸出,中間第三行輸出了參數(shù)類(lèi)型String和參數(shù)的值wt,因?yàn)槲覀冏⒔馍现恢付艘粋€(gè)String.class,因此在這里沒(méi)有把第二個(gè)參數(shù)也輸出來(lái),具體邏輯就去看starter當(dāng)中aop的那個(gè)@Before的方法了。
最后一行是被加強(qiáng)的方法的輸出,打印了msg和currentTime。
現(xiàn)在可以來(lái)想象一下springboot在yml或properties文件中的Datasource等配置咯!
看到這里starter部分就算結(jié)束了
看完上述內(nèi)容,你們掌握如何自定義springboot starter的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。