你好,我是田哥
前两天,一位朋友面试时又被问到了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。
Spring Boot常用的starter有很多,以下是一些常见的starter:
这些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过过瘾呗,哈哈哈,开搞。
我们来搞一个redisson的starter。
第一步创建一个Spring Boot项目,命名为:redisson-spring-boot-starter
。
其中,pom依赖重点:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.1.RELEASE</version>
<!-- 不让jar进行传递 避免jar包冲突-->
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
创建配置解析类:RedissonProperties
/**
* @author tianwc
* @version 1.0.0
* @date 2023年07月20日 11:35
* 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a>
*
* 解析配置项
*/
@ConfigurationProperties(prefix = "tian.redisson")
public class RedissonProperties {
private String host="localhost";
private int port=6379;
private int timeout;
//get set 省略
}
自动装配类:RedissonAutoConfiguration
/**
* @author tianwc
* @version 1.0.0
* @date 2023年07月20日 11:27
* 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a>
*/
//(条件装配)只有在我们的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
完事,这么就搞定了。是不是还没缓过神来?
再创建个spring Boot项目,把我们自定义的starter引入进来。
<dependency>
<groupId>com.tian</groupId>
<artifactId>charge-sms-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
由于我们在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("公众号:********");
}
return bucket.get().toString();
}
}
执行结果:
到这里,我们就搞定了一个自定义starter。
在实际项目中,在自定义starter时,可能还会用到下面这些注解,请注意收藏:
@Conditional
:按照一定的条件进行判断,满足条件给容器注册bean@ConditionalOnMissingBean
:给定的在bean不存在时,则实例化当前Bean@ConditionalOnProperty
:配置文件中满足定义的属性则创建bean,否则不创建@ConditionalOnBean
:给定的在bean存在时,则实例化当前Bean@ConditionalOnClass
:当给定的类名在类路径上存在,则实例化当前Bean@ConditionalOnMissingClass
:当给定的类名在类路径上不存在,则实例化当前Bean
上面我们只是搞了一个单机版的,其实还可以搞各种版本的主从、哨兵、cluster等,也可以搞成ssl访问方式。
自定义会了,那么就该如何在实际项目中使用了,咱们不能老是停留在在demo里,得用到项目中才是王道。