前两天,一位朋友面试时又被问到了starter。我在给他做模拟面试时,我刻意强调了一下,一定要自己去动手写一个 自定义starter,果然面试中被问到了。
可能部分人没有经历过从Servlet到Spring MVC,如果经历过就知道那配置是相当的繁琐,于是后面除了个Spring Boot。
学过Spring Boot的朋友应该知道,有句话用来形容Spring Boot:约定大于配置。
为什么说Spring Boot会被称为约定大于配置呢?
Spring Boot被称为"约定大于配置"的原因是因为它采用了约定优于配置的设计理念。传统的Java开发需要进行大量的配置,如XML文件、注解等,这些配置需要开发人员自己指定。而Spring Boot则通过提供一些默认配置和约定来简化开发过程,减少了开发人员需要手动配置的工作量。
Spring Boot通过一些默认的配置属性和约定来自动完成一些常见的配置工作,如自动配置数据源、自动配置Web服务器、自动配置日志等。开发人员只需按照约定的方式组织代码和配置文件,即可快速构建一个可运行的应用程序。
这种约定大于配置的设计理念使得开发人员不再需要关注一些常规的配置细节,更加专注于业务逻辑的实现。同时,Spring Boot还提供了一些可扩展的配置选项,使得开发人员可以根据自己的需求进行定制和扩展。
总的来说,Spring Boot的约定大于配置的设计理念使得开发人员能够更加快速、高效地开发应用程序,并且减少了配置的复杂性。这也是Spring Boot能够成为Java开发中广泛使用的框架之一的原因。
在面试中,问你说一下Spring Boot自动装配原理,咱们可以简单的这么来回答:
Spring Boot的自动装配原理是通过
@EnableAutoConfiguration
注解和spring.factories
文件来实现的。
@EnableAutoConfiguration
注解是用来开启自动装配的功能,该注解通常放在应用的主类上。
spring.factories
文件是一个位于META-INF目录下的配置文件,其中定义了各个自动装配类的全限定名。当Spring Boot启动时,会加载classpath下的所有
spring.factories
文件,并根据其中定义的自动装配类的全限定名,将其实例化并添加到Spring应用上下文中。自动装配类通常会通过
@Configuration
注解来标注,同时会使用各种注解(如@ComponentScan
、@Import
等)来实现相应的自动装配逻辑。通过
@EnableAutoConfiguration
注解和spring.factories
文件,Spring Boot可以根据应用的依赖和配置信息,自动加载和配置相应的组件,简化了应用的配置和开发过程。
背一遍或许两天就忘了,再背一遍也许能记住个大概。
其实,我们只要清楚如何自定义一个starter,再去回答上面的问题会轻松很多。
我们在使用Spring Boot开发时,最常见的就是各种starter。
常见starter
Spring Boot常用的starter有很多,以下是一些常见的starter:
- spring-boot-starter-web:用于构建Web应用程序的starter,包括Spring MVC和Tomcat服务器。它提供了处理HTTP请求和响应的功能。
- spring-boot-starter-data-jpa:用于与关系型数据库进行交互的starter,包括Spring Data JPA和Hibernate。它简化了与数据库的交互,提供了常见的CRUD操作。
- spring-boot-starter-security:用于添加安全性功能的starter,包括Spring Security和OAuth2。它提供了身份验证、授权和安全配置的功能。
- spring-boot-starter-test:用于编写单元测试和集成测试的starter,包括JUnit和Spring Test。它提供了测试框架和工具,方便进行单元测试和集成测试。
- spring-boot-starter-actuator:用于监控和管理应用程序的starter,包括健康检查、指标收集和远程管理功能。它提供了监控应用程序运行状态的功能。
- spring-starter-cache:用于添加缓存支持的starter,包括Spring Cache和Ehcache。它提供了缓存数据的功能,提高应用程序的性能。
- spring-boot-starter-data-redis:用于与Redis数据库进行交互的starter,包括Spring Data Redis。它简化了与Redis的交互,提供了常见的操作方法。
- spring-boot-starter-mail:用于发送电子邮件的starter,包括JavaMail和Spring Mail。它提供了发送电子邮件的功能。
- spring-boot-starter-log4j2:用于使用Log4j2进行日志记录的starter。它提供了日志记录的功能,方便调试和错误追踪。
- spring-boot-starter-thymeleaf:用于使用Thymeleaf模板引擎的starter。它提供了使用Thymeleaf进行页面渲染的功能。
- ......
这些starter可以根据应用程序的需求选择使用,它们提供了各种功能和便利,简化了应用程序的开发和配置过程。
从命名上大家会发现都是以spring-boot-starter
开头的spring-boot-starter-xxx
。
但,我们在项目中也简单过这样的:mybatis-spring-boot-starter
,并不是上面的那种命名方式。
其实,我们通常称以spring-boot-starter开头的spring-boot-starter-xxx
是spring官方给咱们搞的,xxx-spring-boot-starter
这种格式就是自己搞的,只是个命名而已。
我们为何不能自己也来搞个starter过过瘾呗,哈哈哈,开搞。
自定义starter
我们来搞一个redisson的starter。
第一步创建一个Spring Boot项目,命名为:redisson-spring-boot-starter
。
其中,pom依赖重点:
org.springframework.boot
spring-boot-starter
2.3.1.RELEASE
true
org.redisson
redisson
3.13.1
org.springframework.boot
spring-boot-configuration-processor
true
登录后复制
创建配置解析类:RedissonProperties
/**
* @author tianwc 公众号:java后端技术全栈、面试专栏
* @version 1.0.0
* @date 2023年07月20日 11:35
* 在线刷题 1200+题和1000+篇干货文章:博客地址
*
* 解析配置项
*/
@ConfigurationProperties(prefix = "tian.redisson")
public class RedissonProperties {
private String host="localhost";
private int port=6379;
private int timeout;
//get set 省略
}
登录后复制
自动装配类:RedissonAutoConfiguration
/**
* @author tianwc 公众号:java后端技术全栈、面试专栏
* @version 1.0.0
* @date 2023年07月20日 11:27
* 在线刷题 1200+题和1000+篇干货文章:博客地址
*/
//(条件装配)只有在我们的classpath目录下有Redisson.class 我们才自动装配RedissonClient bean
//@ConditionalOnClass(Redisson.class)
@EnableConfigurationProperties(RedissonProperties.class)
@Configuration
public class RedissonAutoConfiguration {
private static final String PREFIX="redis://";
@Bean
public RedissonClient redissonClient(RedissonProperties redissonProperties){
Config config=new Config();
//redis单机模式
config.useSingleServer().setAddress(PREFIX+redissonProperties.getHost()+":"+redissonProperties.getPort())
.setConnectTimeout(redissonProperties.getTimeout());
return Redisson.create(config);
}
}
登录后复制
然后,在resources目录下创建:META-INF目录,在这个目录下创建spring.factories
文件,文件内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.tian.RedissonAutoConfiguration
登录后复制
完事,这么就搞定了。是不是还没缓过神来?
使用自定义starter
再创建个spring Boot项目,把我们自定义的starter引入进来。
com.tian
charge-sms-spring-boot-starter
1.0-SNAPSHOT
登录后复制
由于我们在RedissonProperties
类中对host和port赋了默认值,所以我们在application.properties文件中,不配任何参数时就是用到的是默认值。
如果配置的话,如下:
tian.redisson.host=localhost
tian.redisson.port=6379
tian.redisson.timeout=1000
登录后复制
但是,你真的在这么玩的时候,会发现我们平时要配置个什么的时候,基本上都会提示的,比如:
这样提示可不就爽了吗?
要想有这个提示,还得在redisson-spring-boot-starter
中搞点事情。
相关jar上面都已经引入了这里不再赘述。
同样是在META-INF下面创建一个文件(文件名需要注意不要写错):additional-spring-configuration-metadata.json
内容如下:
{
"properties": [
{
"name": "tian.redisson.host",
"type": "java.lang.String",
"description": "redis服务地址ip",
"defaultValue": "localhost"
},
{
"name": "tian.redisson.port",
"type": "java.lang.Integer",
"description": "redis服务端口号",
"defaultValue": 6379
},
{
"name": "tian.redisson.timeout",
"type": "java.lang.Integer",
"description": "redis连接超时时间",
"defaultValue": 1000
}
]
}
登录后复制
properties
对应一个数组,每个配置项占一个。
name
:就是我们的配置项type
:配置项类型description
:配置项的描述defaultValue
:默认值
重新打包,在使用的项目里执行Reload All Maven。
接着,再去properties文件里配置,就会有对应的提示了。
我们写一个controller来使用:
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@Resource
private RedissonClient redissonClient;
@GetMapping("/redisson")
public String redisson() {
RBucket bucket = redissonClient.getBucket("name");
if(bucket.get()==null){
bucket.set("公众号:java后端技术全栈");
}
return bucket.get().toString();
}
}
登录后复制
执行结果:
到这里,我们就搞定了一个自定义starter。
在实际项目中,在自定义starter时,可能还会用到下面这些注解,请注意收藏:
@Conditional
:按照一定的条件进行判断,满足条件给容器注册bean
@ConditionalOnMissingBean
:给定的在bean不存在时,则实例化当前Bean
@ConditionalOnProperty
:配置文件中满足定义的属性则创建bean,否则不创建
@ConditionalOnBean
:给定的在bean存在时,则实例化当前Bean
@ConditionalOnClass
:当给定的类名在类路径上存在,则实例化当前Bean
@ConditionalOnMissingClass
:当给定的类名在类路径上不存在,则实例化当前Bean
上面我们只是搞了一个单机版的,其实还可以搞各种版本的主从、哨兵、cluster等,也可以搞成ssl访问方式。
自定义会了,那么就该如何在实际项目中使用了,咱们不能老是停留在在demo里,得用到项目中才是王道。
我在充电桩项目中,发送短信功能中用到了自定义starter,还用到了模板方法模式。