👨🏻🎓博主介绍:大家好,我是芝士味的椒盐,一名在校大学生,热爱分享知识,很高兴在这里认识大家🌟
🌈擅长领域:Java、大数据、运维、电子
🙏🏻如果本文章各位小伙伴们有帮助的话,🍭关注+👍🏻点赞+🗣评论+📦收藏,相应的有空了我也会回访,互助!!!
🤝另本人水平有限,旨在创作简单易懂的文章,在文章描述时如有错,恳请各位大佬指正,在此感谢!!!
@[TOC]
SpringBoot自动装配
主类使用了@SpringBootApplication,其内部的@EnableAutoConfiguration就会作用,开启自动装配
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition ......
- 按需加载,只有导入了相关的依赖之后才会生效
包扫描
- 默认是扫描引导类(主类)的包所在的包及其子包下的Bean。
各种配置拥有默认值
- 默认配置最终都是映射到某个类上,如:MultipartProperties
- 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
也可以更改包扫描位置使用如下
@SpringBootApplication(scanBasePackages = "icu.lookyousmileface.springboot_gettingstarted.controller")
还可以不使用@SpringApplication注解,使用@ComponentScan注解,不可以和@SpringApplication注解一起用
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("icu.lookyousmileface.springboot_gettingstarted")
会冲突
@ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ),
SpringBoot容器功能
@Configuration
Full模式与Lite模式
- 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
- 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
@Configuration(proxyBeanMethods = true) public class MyConfig { @Bean public Human human(){ return new Human("松江",18,'男'); } public Pet pet(){ return new Pet("小黑","白色"); } } public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootGettingstartedApplication.class); //查看场景启动器带来的组件 String[] beanDefinitionNames = run.getBeanDefinitionNames(); Arrays.stream(beanDefinitionNames).forEach(S->System.out.println(S)); //从容器获得对象 Human human1 = run.getBean("human",Human.class); Human human2 = run.getBean("human",Human.class); System.out.println(human1==human2); //icu.lookyousmileface.springboot_gettingstarted.config.MyConfig$$EnhancerBySpringCGLIB$$21ae3889@15515c51 表示被代理 MyConfig myConfig = run.getBean(MyConfig.class); System.out.println(myConfig); //config的proxyBeanMethods = true为单实列,否则每次调用都会产生一个新的对象 Human human = myConfig.human(); Human human3 = myConfig.human(); System.out.println(human3==human); }
- config类配置代替了大量的xml配置
- @Bean(表示对象组件)、@Component(表示组件)、@Controller(表示控制器组件)、@Service(表示业务层组件)、@Repository(标注表示数据库组件)
@ComponentScan(包扫描)、@Import(导入组件)
@Import:使用@Import导入组件,其名字为全类名如:Human,@Bean注册的注册的类为首字母小写形势human
*/ //使用@Import导入组件,其名字为全类名如:Human,@Bean注册的注册的类为首字母小写形势human @Import({Human.class,Pet.class}) @Configuration(proxyBeanMethods = true) public class MyConfig { @Bean public Human human(){ return new Human("松江",18,'男'); } public Pet pet(){ return new Pet("小黑","白色"); } }
@Conditional
- 条件装配:满足Conditional指定的条件,则进行组件注入,其下由多个子类如下:
代码实验:
@Configuration public class MyConfig { @ConditionalOnMissingBean(name = "peto1") @Bean public Human human01(){ return new Human("宋江",46,'男'); } // @Bean public Pet Peto1(){ return new Pet("小黄","yellow"); } }
- @ConditionalOnMissingBean(name = "peto1")也就是说,如果没有"pet01"Bean这个名称的容器就创建human01,否则就将不创建Human类的对象, 可以作用在方法和类上。
@ImportResource(引入原生的配置文件), 例如:要倒入Spring原生bean配置文件如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <bean id="hu01" class="icu.lookyousmileface.springboot_gettinggide.bean.Human"> <property name="name" value="武松"></property> <property name="age" value="19"></property> <property name="sex" value="男"></property> </bean> <bean id="pe01" class="icu.lookyousmileface.springboot_gettinggide.bean.Pet"> <property name="name" value="cat"></property> <property name="color" value="red"></property> </bean> </beans>
就可以在配置类上加上该注解如下,bean此时在resources目录下:
@Configuration @ImportResource("classpath:bean.xml") public class MyConfig { // @ConditionalOnMissingBean(name = "peto1") @Bean public Human human01(){ return new Human("宋江",46,'男'); } @Bean public Pet Peto1(){ return new Pet("小黄","yellow"); } }
配置绑定(@ConfigurationProperties+@Component标注在要绑定属性的类上)
application.yml(选用的是yml,选用properties遇到中文会出现乱码)
myhuman: name: 宋江 age: 89 sex: 男
Human.java
@Component prefix表示和配置文件的前缀相同的属性开始匹配,也就是上述application.yml中的myhuman @ConfigurationProperties(prefix = "myhuman") public class Human { private String name; private int age; private String sex; }
⚠️注意配置绑定之前需要导入配置出绑定处理器,解决没有提示的问题
<!--配置文件处理器,properties参数绑定对象--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
⚠️为了减少不必要插件编译进jar包或war包,可进行排除
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <!--编译时排除不必要的类--> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> <exclude> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
- application.yml
Human.java
@AllArgsConstructor @NoArgsConstructor @Data @Accessors(chain = true) @EqualsAndHashCode //@Component @ConfigurationProperties(prefix = "myhuman") public class Human { private String name; private int age; private String sex; }
MyConfig.java
@Configuration //@ImportResource("classpath:bean.xml") @EnableConfigurationProperties(Human.class) public class MyConfig { // @ConditionalOnMissingBean(name = "peto1") // @Bean // public Human human01(){ // return new Human("宋江",46,"男"); // } @Bean public Pet Peto1(){ return new Pet("小黄","yellow"); } }
- @EnableConfigurationProperties(Human.class):表示支持某个类参数绑定,也就是开启配置绑定功能,将Human这个组件注册到容器。注意不要存在其他注册该容器的情况否则报错。
自动配置原理
- @SpringBootConfiguration:@Configuration是一个配置类
- @ComponentScan:包扫描
@EnableAutoConfiguration:自动装配核心
@AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class})
@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件 public @interface AutoConfigurationPackage {} //利用Registrar给容器中导入一系列组件 //将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
@Import(AutoConfigurationImportSelector.class)
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件 2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类 3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件 4、从META-INF/spring.factories位置来加载一个文件。 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
按需开启自动配置项
虽然127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration 按照条件装配规则(@Conditional),最终会按需配置。
修改自动配置
@Bean @ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件 @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件 public MultipartResolver multipartResolver(MultipartResolver resolver) { //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。 //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范 // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; } 给容器中加入了文件上传解析器;
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
@Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() { }
总结:
• SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration • 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定 • 生效的配置类就会给容器中装配很多组件 • 只要容器中有这些组件,相当于这些功能就有了 • 定制化配置 • 用户直接自己@Bean替换底层的组件 • 用户去看这个组件是获取的配置文件什么值就去修改。 xxxxxAutoConfiguration ---> 组件 ---> xxxxProperties里面拿值 ----> application.properties
最佳实践
• 引入场景依赖 • https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter • 查看自动配置了哪些(选做) • 自己分析,引入场景对应的自动配置一般都生效了 • 配置文件中debug=true开启自动配置报告。Negative(不生效)\Positive(生效) • 是否需要修改 • 参照文档修改配置项 • https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties • 自己分析。xxxxProperties绑定了配置文件的哪些。 • 自定义加入或者替换组件 • @Bean、@Component。。。 • 自定义器 XXXXXCustomizer; • ......
开发小技巧
- lombok:通过注解的方式创建get/set/toString等方法,提高编写bean的效率,也可以使用Slfj日志输出。
devtools:跟JRebel差不多的热部署工具,只要倒入其依赖即可使用,快捷键Control+F9
<!--springboot的热部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>2.0.4.RELEASE</version> </dependency>