在作SpringBoot開發時,各類starter (場景啓動器) 必不可少,它們就像可插拔式的插件,只要在pom文件中引用 springboot 提供的場景啓動器, 再進行少許的配置就可使用相應的功能,但SpringBoot並不能囊括咱們的全部使用場景,這時候就須要咱們自定義starter來實現定製化功能。java
項目源碼地址:GitHubgit
前綴:spring-boot-starter- 模式:spring-boot-starter-{模塊名} 舉例:spring-boot-starter-test、spring-boot-starter-log4j2github
後綴:-spring-boot-starter 模式:{模塊}-spring-boot-starter 舉例:mybatis-spring-boot-starterspring
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"implementation 'org.springframework.boot:spring-boot-starter-aop'複製代碼
package com.codelong.log.annotion;import java.lang.annotation.*;/** * 日誌註解 * * @author codelong */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface MyLog { }複製代碼
package com.codelong.log.config;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.PropertySource;/** * 日誌配置文件類 * * @author codelong */@ConfigurationProperties(value = "mylog")@PropertySource(value = "classpath:application.yml", encoding = "UTF-8")public class MyLogProperties {/** * 日誌開始前綴 */private String prefix;/** * 日誌結束前綴 */private String suffix;public String getPrefix() {return prefix; }public void setPrefix(String prefix) {this.prefix = prefix; }public String getSuffix() {return suffix; }public void setSuffix(String suffix) {this.suffix = suffix; } }複製代碼
package com.codelong.log.config;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import java.util.Arrays;/** * MyLog註解切面類 * * @author codelong */@Aspectpublic class MyLogAspect {private MyLogProperties myLogProperties;public MyLogAspect(MyLogProperties myLogProperties) {this.myLogProperties = myLogProperties; }@Pointcut("@annotation(com.codelong.log.annotion.MyLog)")public void logAnnotationAnnotationPointcut() { }@Around("logAnnotationAnnotationPointcut()")public Object logInvoke(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println(myLogProperties.getPrefix().concat(Arrays.toString(joinPoint.getArgs()))); Object obj = joinPoint.proceed(); System.out.println(myLogProperties.getSuffix().concat(obj.toString()));return obj; } }複製代碼
要讓上面類注入spring容器,須要一個自動配置類springboot
package com.codelong.log.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * 配置類 * * @author codelong */@Configuration@EnableConfigurationProperties(MyLogProperties.class) // 該註解可使MyLogProperties注入spring容器public class MyLogAutoConfiguration {@Beanpublic MyLogAspect myLogAspect(@Autowired MyLogProperties myLogProperties) {return new MyLogAspect(myLogProperties); } }複製代碼
@Configuration //指定這個類是一個配置類 @ConditionalOnXXX //指定條件成立的狀況下自動配置類生效 @AutoConfigureOrder //指定自動配置類的順序 @Bean //向容器中添加組件 @ConfigurationProperties //結合相關xxxProperties來綁定相關的配置 @EnableConfigurationProperties //讓xxxProperties生效加入到容器中 @ConditionalOnClass:當類路徑classpath下有指定的類的狀況下進行自動配置 @ConditionalOnMissingBean:當容器(Spring Context)中沒有指定Bean的狀況下進行自動配置 @ConditionalOnProperty(prefix = 「example.service」,name = "auth", value = 「enabled」, matchIfMissing = true),當配置文件中example.service.auth.enabled=true時進行自動配置,若是沒有設置此值就默認使用matchIfMissing對應的值 @ConditionalOnMissingBean,當Spring Context中不存在該Bean時。 @ConditionalOnBean:當容器(Spring Context)中有指定的Bean的條件下 @ConditionalOnMissingClass:當類路徑下沒有指定的類的條件下 @ConditionalOnExpression:基於SpEL表達式做爲判斷條件 @ConditionalOnJava:基於JVM版本做爲判斷條件 @ConditionalOnJndi:在JNDI存在的條件下查找指定的位置 @ConditionalOnNotWebApplication:當前項目不是Web項目的條件下 @ConditionalOnWebApplication:當前項目是Web項目的條件下 @ConditionalOnResource:類路徑下是否有指定的資源 @ConditionalOnSingleCandidate:當指定的Bean在容器中只有一個,或者在有多個Bean的狀況下,用來指定首選的Beanmybatis
在resources/META-INF/下建立文件spring.factories,SpringBoot在啓動的時候會掃描項目所依賴的JAR包,尋找包含spring.factories文件的JAR包,讀取spring.factories文件獲取配置的自動配置類AutoConfiguration,而後將自動配置類下知足條件(@ConditionalOnXxx)的@Bean放入到Spring容器中(Spring Context),這樣使用者就能夠直接用來注入,由於該類已經在容器中了app
spring.factories文件內容:key是固定的org.springframework.boot.autoconfigure.EnableAutoConfiguration,value能夠有多個maven
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.codelong.log.config.MyLogAutoConfiguration複製代碼
目錄結構以下ide
這裏使用maven-publish打包插件,不用插件的可使用maven打包命令spring-boot
添加插件
id 'maven-publish'複製代碼
添加打包信息
publishing { publications { mavenJava(MavenPublication) { from components.java } } repositories { mavenLocal() } } tasks.withType(GenerateModuleMetadata) { enabled = false} jar { enabled = true}複製代碼
執行打包任務
新建一個基礎項目,添加本身的依賴
implementation 'com.codelong:log-spring-boot-starter:1.0.0'複製代碼
添加一個接口用於測試
/** * 測試 */@MyLog@PostMapping("/testLog")public String testLog(@RequestBody Message message) {return "日誌測試"; }複製代碼
修改yml文件(會有咱們日誌配置的提示)
server: port: 8080 servlet:context-path: /mylog: prefix: 請求開始--- suffix: 請求結束---複製代碼
啓動項目post訪問:http://127.0.0.1:8080/testLog
查看控制檯日誌
當咱們不用這個功能時,這些bean仍是會注入到spring容器中,這時咱們就須要動態拔插這個功能,主要是用到了@ConditionalOnBean這個註解,下面咱們來改造一下上面寫的log-spring-boot-starter項目。
@ConditionalOnBean:當容器(Spring Context)中有指定的Bean的條件下才問生效配置
package com.codelong.log.config;/** * 開關標記類 * * @author codelong */public class LogMarker { }複製代碼
package com.codelong.log.annotion;import com.codelong.log.config.LogMarker;import org.springframework.context.annotation.Import;import java.lang.annotation.*;/** * EnableMyLog註解將LogMarker注入到spring容器 * * @author longwang */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(LogMarker.class)public @interface EnableMyLog { }複製代碼
package com.codelong.log.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * 配置類 * * @author codelong */@Configuration@EnableConfigurationProperties(MyLogProperties.class) // 該註解可使MyLogProperties注入spring容器@ConditionalOnBean(LogMarker.class) //當容器中有這個LogMarkerBean就會使得下面配置生效public class MyLogAutoConfiguration {@Beanpublic MyLogAspect myLogAspect(@Autowired MyLogProperties myLogProperties) {return new MyLogAspect(myLogProperties); } }複製代碼
這裏經過EnableMyLog註解來控制是否啓動日誌功能,只有使用EnableMyLog註解纔會將LogMarkerBean注入到Spring容器內,當Spring容器內有LogMarkerBean纔會使MyLogAutoConfiguration生效。
因此當咱們要開啓日誌功能時,在springboot啓動類上添加@EnableMyLog註解就能夠了。