在前面我讲用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也很简单,咱们先从https://start.spring.io/下载一个基本的项目结构下来,而后须要修改几个地方。apache
首先是pom文件要修改,个人pom文件是这样的app
<?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,改动了这么几个地方。maven
<artifactId>mystarter-spring-boot-starter</artifactId>
spring 官方的推荐写artifactId的方法是这样ide
因此,官方用来发mail的starter是spring-boot-starter-mail,我这边用的就是mystarter-spring-boot-starter。spring-boot
原始pom.xml会有这一段,是须要去掉的,不然打包的时候本身写的类加不进去,jar里面都是spring boot的类ui
<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就比较方便了。
咱们另外用https://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就运行成功了。