在前面我講用spring-boot-starter-mail發郵件的時候,我側重看的是spring boot發郵件的便利性,今天,咱們聊下另一個方面,spring-boot-starter自身的結構。java
以前使用starter的時候,都是用了就完事了,此次發郵件的時候,好奇心上來了,點開了spring-boot-starter-mail的jar包內容,發現居然只有一個MANIFEST.MF文件,沒有class文件,沒有配置文件,很是的簡單。git
咱們看下這個MANIFEST.MF裏面都有些啥github
Manifest-Version: 1.0
Implementation-Title: Spring Boot Mail Starter
Automatic-Module-Name: spring.boot.starter.mail
Implementation-Version: 2.1.8.RELEASE
Built-By: Spring
Build-Jdk-Spec: 1.8
Created-By: Maven Archiver 3.4.0複製代碼
這個也很是的普通,比平平無奇的古天樂還要平平無奇,這不科學啊。若是隻憑這個文件就能發郵件,那我早就靠收藏寫真圖片娶到新垣結衣了。確定代碼在別的地方,在找代碼前,咱們先動手本身製做一個starter。spring
本身寫個starter也很簡單,咱們先從start.spring.io/下載一個基本的項目結構下來,而後須要修改幾個地方。apache
首先是pom文件要修改,個人pom文件是這樣的bash
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.skyblue</groupId>
<artifactId>mystarter-spring-boot-starter</artifactId>
<version>1.0</version>
<name>mystarter</name>
<description>spring boot starter demo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
複製代碼
比起原始的pom.xml,改動了這麼幾個地方。app
<artifactId>mystarter-spring-boot-starter</artifactId>複製代碼
spring 官方的推薦寫artifactId的方法是這樣maven
因此,官方用來發mail的starter是spring-boot-starter-mail,我這邊用的就是mystarter-spring-boot-starter。ide
原始pom.xml會有這一段,是須要去掉的,不然打包的時候本身寫的類加不進去,jar裏面都是spring boot的類spring-boot
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>複製代碼
另外須要加至少兩個依賴進去
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>複製代碼
其實把兩個依賴都放在<dependencies>節點裏面也行,<dependencyManagement>和<dependencies>的區別請自行搜索。
pom.xml改好了後咱們須要爲本身的starter寫class啦,咱們這邊爲了演示,就只實現打印兩個值的功能,看代碼
public interface MyStarterService {
String getMessage();
Integer getCode();
}
public class MyStarterServiceImpl implements MyStarterService{
@Autowired
private MyStarterProperties myStarterProperties;
public String getMessage() {
return myStarterProperties.getMessage();
}
public Integer getCode() {
return myStarterProperties.getCode();
}
}複製代碼
這個接口和實現類就是簡單的返回屬性值而已,屬性值的配置文件是這樣的
@ConfigurationProperties(prefix = "mystarter")
public class MyStarterProperties {
String message;
int code;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}複製代碼
@ConfigurationProperties註解表示MyStarterProperties 裏面的參數message和code都會從配置文件裏面讀取,prefix = "mystarter"表示配置文件裏面參數名稱是有前綴的,前綴就是mystarter。舉個具體的例子,好比咱們以前發郵件的參數也是配置在application.properties,參數的內容是這樣的
spring.mail.host=smtp.163.com
spring.mail.port=25
spring.mail.username=youname@163.com
spring.mail.password=yourpassword複製代碼
裏面host,port,username,password就是參數的名稱,spring.mail就是前綴。
上面這些寫好了至關於業務功能部分,如今須要把業務功能申明到spring-boot-starter體系裏面去,須要靠下面這個類
@Configuration
//告訴spring容器配置文件讀取用MyStarterProperties.class
@EnableConfigurationProperties({MyStarterProperties.class})
//導入業務組件MyStarterServiceImpl
@Import(MyStarterServiceImpl.class)
public class MyStarterAutoConfiguration {
}複製代碼
我用的是最簡單的方式,其實spring boot還提供了@Conditional 系列註解實現更加精確的配置加載Bean的條件,這裏就不詳述了。
最後,咱們須要告訴spring boot在哪裏去找到這個MyStarterAutoConfiguration ,在resources/META-INF下面建一個spring.factories文件
內容也很簡單,就一句而已
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.skyblue.mystarter.MyStarterAutoConfiguration複製代碼
這樣,其實一個自定義的starter就完成了,用mvn install就能夠直接生成一個starter了。
在給starter取名字的時候說了,官方命名格式是有固定格式的。其實官方的便利可不在名字上,而是代碼都包含在spring boot的jar裏面,咱們引入spring boot的依賴時,會自動加載spring-boot-autoconfigure.xxx.jar,打開這個jar,就能夠看到mail的真正代碼了
有沒有一種很熟悉的感受,MailProperties和上面的MyStarterProperties,MailSenderAutoConfiguration和上面的MyStarterAutoConfiguration,顯然都是同樣按照spring boot starter的規則寫的,只是這個官方starter的代碼不放在starter的jar包,而是包裝到了spring-boot-autoconfigure的jar裏面,咱們看下MailSenderAutoConfiguration的源代碼,能夠看到它就用到了@Configuration、@EnableConfigurationProperties、@Import,還用到了咱們沒用到的@Conditional註解
@Configuration
@ConditionalOnClass({ MimeMessage.class, MimeType.class, MailSender.class })
@ConditionalOnMissingBean(MailSender.class)
@Conditional(MailSenderCondition.class)
@EnableConfigurationProperties(MailProperties.class)
@Import({ MailSenderJndiConfiguration.class, MailSenderPropertiesConfiguration.class })
public class MailSenderAutoConfiguration {
/**
* Condition to trigger the creation of a {@link MailSender}. This kicks in if either
* the host or jndi name property is set.
*/
static class MailSenderCondition extends AnyNestedCondition {
MailSenderCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(prefix = "spring.mail", name = "host")
static class HostProperty {
}
@ConditionalOnProperty(prefix = "spring.mail", name = "jndi-name")
static class JndiNameProperty {
}
}
}
複製代碼
還有一個spring.factories文件,也能夠在spring-boot-autoconfigure.jar裏面找到
在裏面,咱們能夠看到完整的spring boot官方starter的AutoConfiguration類列表
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
......複製代碼
我這邊就不全列出來了,你們根據這個去找須要的官方starter就比較方便了。
咱們另外用start.spring.io/再建立一個項目,而後在pom.xml裏面加載starter的依賴
<dependency>
<groupId>com.skyblue</groupId>
<artifactId>mystart</artifactId>
<version>1.0</version>
<type>jar</type>
<scope>system</scope>
<systemPath>D:\\workspace\\mystart\\target\\mystarter-spring-boot-starter-1.0.jar</systemPath>
</dependency>複製代碼
我爲了圖方便,就直接用pom.xml調用了本地打包的starter包,若是有maven的私服,就能夠正常引入。配置application.properties文件
mystarter.message=hello world!
mystarter.code=42複製代碼
寫一個調用starter的類
@Service
public class TestService {
@Resource
private MyStarterService myStarterService;
public void message() {
System.out.println("code:" + myStarterService.getCode());
System.out.println("message:" + myStarterService.getMessage());
}
}複製代碼
啓動spring boot 查看結果
@SpringBootApplication
public class StartdemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(StartdemoApplication.class, args);
((TestService)context.getBean("testService")).message();
}
}複製代碼
console能夠看到打印出來的message和code
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.9.RELEASE) 2019-10-10 22:13:49.521 INFO 21952 --- [ main] c.w.startdemo.StartdemoApplication : Starting StartdemoApplication on skyblue with PID 21952 (D:\workspace\startdemo\target\classes started by wphmo in D:\workspace\startdemo) 2019-10-10 22:13:49.527 INFO 21952 --- [ main] c.w.startdemo.StartdemoApplication : No active profile set, falling back to default profiles: default 2019-10-10 22:13:50.405 INFO 21952 --- [ main] c.w.startdemo.StartdemoApplication : Started StartdemoApplication in 1.353 seconds (JVM running for 1.983) code:42 message:hello world!複製代碼
這樣,一個完整的自定義starter就運行成功了。