溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何自定義springboot starter

發(fā)布時(shí)間:2021-09-29 17:28:03 來(lái)源:億速云 閱讀:130 作者:柒染 欄目:大數(shù)據(jù)

如何自定義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è)功能:

  1. 使用Aspectj方式的aop,來(lái)實(shí)現(xiàn)對(duì)某類(lèi)函數(shù)參數(shù)監(jiān)控;

  2. 定義一個(gè)自己的配置類(lèi),讀取我們?cè)趛ml或properties里面配置的參數(shù),同時(shí)提供一個(gè)service,用來(lái)獲取我們讀取的結(jié)果。

這里是示例工程starter.

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

AOP部分

先按照正常工程把一些配置都配好,先來(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

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>

使用MyConfig配置

我們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ì)比)。

使用參數(shù)監(jiān)控AOP

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è)資訊頻道,感謝各位的閱讀!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI