从javaweb的servlet到spring的Controller再到springboot的Controller,我们一步一步的简化了我们程序的配置。
- 在javaweb中,我们写的各种文件通过我们手动的方式去注入属性和管理,通过动态代理的方式来实现各种功能的横向拓展。
- 在spring中,我们简化了类的属性装配和优化了类的创建管理(IOC),并且我们使用了高效的AOP思想(动态代理)来对我们现有的功能进行横向的拓展。同时也可以选择大量现成的spring内嵌功能或者第三方功能来为我们简化开发。
- 而在springboot中,使我们的开发更进一步的简化了,它内部集成了Tomcat,使我们从Tomcat的配置中脱离出来。它的核心思想是“约定大于配置”,在spring的基础上使用注解约定的方式来使我们的配置进一步简化。一方面我们从繁重的配置bean中脱离出来,另一方面,我们可以更加轻松的整合第三方功能( Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),因为其内部已经有注解约定,只需要加入starter配置,即使生效。
SpringBoot自动配置原理
SpringBoot提供的那么方便的功能是怎么实现的呢?我们学习SpringBoot的自动配置原理,应该从@SpringBootApplication
这个注解开始。
@SpringBootApplication
public class BlogApplication {
public static void main(String[] args) {
SpringApplication.run(BlogApplication.class, args);
}
}
该注解里面又由三个重要注解修饰,分别为
- @SpringBootConfiguration
- @ComponentScan
- @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
其中
@SpringBootConfiguration
注解由@Configuration
注解修饰,功能也和@Configuration注解一样。另一个
@ComponentScan
注解作用是扫描本类所在的包下所有的配置类。
// @SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
其中最主要的是
@EnableAutoConfiguration
注解,它有两个注解修饰,一个为@AutoConfigurationPackage
,另一个为@Import({AutoConfigrationSelector.class})
。
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
@AutoConfigrutionPackage
注解也是一个自动扫描包的注解,可以扫描该类同包下的所有类组件。它和@SpringBootConfiguration注解不同。
- @AutoConfigurationPackage注解是扫描@Entity之类的注解。
- @SpringBoootConfiguration扫描的是@Component之类的注解。两者扫描的对象不同。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
其中另一个注解
@Import(AutoConfigurationSelector.class)
导入一个AutoConfigurationImportSelector.class,这个类可以加载META-INF/spring.factories
文件的信息,然后筛选出以EnableAutoConfiguration
为key的数据,加载到IOC容器中,实现自动配置功能!
# 配置需要加载到IOC容器的类。这些类中会定义我们组件中的所有配置。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lyj.autoConfig.UserAutoConfiguration
SpringBoot配置文件关联
我们可以很简单的从yaml文件中做一些简单的配置,从而实现我们的个性化需求,SpringBoot是如何实现的呢?
如果我们只需要简单的关联某个普通类,我们只需要这样做。
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "useri")
public class User {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
}
- 使用Component来进行注入,
- 使用ConfigurationProperties来进行前缀绑定。
而如果我们需要绑定配置类,并且为配置类添加一个属性文件绑定前缀,那么我们将需要这样做
定义属性文件
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "useri")
public class User {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
}
自定义配置类
@Configuration
@EnableConfigurationProperties(User.class) //将User实体类注入
@ConditionalOnWebApplication //特定情况生效(web项目时)
public class UserAutiConfiguration {
}
@EnableConfigurationProperties
注解的作用是将实体类注入到IOC容器中,这样的方式下,我们只需要利用工厂模式,根据参数来创建我们需要的配置即可。
SpringBoot自定义Starter
了解了SpringBoot的自动配置原理和关联属性之后,我们会发现,自动配置中扫描的EnableAutoConfigutation
就是我们配置的这个配置类,AutoConfigurationSeletor.class
扫描spring.factory的文件中的类全名路径(即自定义的类),然后利用反射获取信息并注入到IOC容器中。
我们可以先了解官方是怎么自定义starter的
-
属性配置文件绑定前缀
// 绑定前缀 @ConfigurationProperties( prefix = "server", ignoreUnknownFields = true ) public class ServerProperties { private Integer port; private InetAddress address; @NestedConfigurationProperty private final ErrorProperties error = new ErrorProperties(); private ServerProperties.ForwardHeadersStrategy forwardHeadersStrategy; private String serverHeader; private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
-
自定义配置类
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(ServerProperties.class) //注入IOC容器 //一堆规则定义何时生效 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) //绑定server.servlet.encoding的前缀,enables值默认为true。 public class HttpEncodingAutoConfiguration { //绑定配置文件,和前面配合使用 private final Encoding properties; //从配置文件中获取参数。 public HttpEncodingAutoConfiguration(ServerProperties properties) { this.properties = properties.getServlet().getEncoding(); } @Bean @ConditionalOnMissingBean //当容器中没有这个Bean时生效 public CharacterEncodingFilter characterEncodingFilter() {
-
配置spring.factory文件
# Auto Configure,这里用mp的配置文件代替显示。 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\ com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
其中最为复杂的配置应该是我们自动配置类的逻辑代码,这里不作一一分析。
可以来简单的实现一个我们自定义的
starter
,来看看它的所有流程。
自定义starter
-
新建springboot项目,将新项目中的多余文件删除。
-
导入两个依赖(依赖之外的除了头部也全部删除)
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>2.3.11.RELEASE</version> </dependency> </dependencies>
-
写实体类(需要手动加入set方法和get方法,set方法用于注入属性,get方法用户获取值进行测试)
@ConfigurationProperties(prefix = "useri") //绑定插件 public class UserProperties { private String name; private Integer age; private Boolean happy; private Date birth; private Map<String, Object> maps; private List<Object> lists; private Dog dog;
-
暴露一个接口用于测试
public class UserService { public UserProperties getUserProperties() { return userProperties; } public void setUserProperties(UserProperties userProperties) { this.userProperties = userProperties; } private UserProperties userProperties; public void hello(){ userProperties.toString(); } }
-
写自动注入类进行配置文件的注入和注入对外暴露的接口
@Configuration @EnableConfigurationProperties(UserProperties.class) @ConditionalOnWebApplication //Web应用生效 public class UserAutoConfiguration { @Autowired private UserProperties userProperties; @Bean public UserService getUserService(){ UserService userService = new UserService(); userService.setUserProperties(userProperties); return userService; } }
-
设置为springboot的自动配置类,使打包后可以识别
新建resources/META-INF/spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lyj.autoConfig.UserAutoConfiguration
-
使用maven工具下载到maven仓库
mvn clear install
这样的方式下,我们的个自定义start就制作好了。
starter的使用
-
导入依赖
<dependency> <groupId>com.lyj</groupId> <artifactId>lyj-springboot-starter2</artifactId> <version>0.0.2-SNAPSHOT</version> </dependency>
-
在配置文件中根据之前前缀注入属性值
useri.name=zhangsan server.port=8001
-
测试接口
@RestController public class TestController { @Autowired private UserService userService; @RequestMapping("/hello") public String hello(){ return userService.getUserProperties().getName(); } }