面试官:你们为什么要用Spring Boot啊,Spring Boot的自动装配是怎么实现的?
蛮荒的年代
最近用了一些spring-boot-starter-data的组件(redis,mongodb,jpa,elasticsearch等),才意识到Spring Boot真是极大的简化了开发的流程。以演进的视角来分享一下spring boot是如何通过自动装配来简化开发的
XML配置
Spring是一个容器,里面保存了你所需要的对象和对象之间的依赖关系。当我们需要对象A时,不用从头开始new,只需要告诉Spring把A给我,Spring就会把对象A给你,即IOC。刚开始这些对象以及对象之间的依赖关系是配置在XML文件中的
applicationContext.xml
<bean id="knight" class="com.st.part1.BraveKnight"> <constructor-arg ref="weapon"/> </bean> <bean id="weapon" class="com.st.part1.Weapon"> <property name="type" value="knife"/> </bean>
Weapon类
package com.st.part1; public class Weapon { private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } }
BraveKnight类
package com.st.part1; public class BraveKnight { private Weapon weapon; public BraveKnight(Weapon weapon) { this.weapon = weapon; } public Weapon getWeapon() { return weapon; } }
如果你学过SSM框架的话,应该对这些配置不陌生
public class Main { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 获得注册到spring容器中bean的名字,即前面配置文件中的id属性 String names[] = context.getBeanDefiniionNames(); for (int i = 0; i < names.length; i++) { // knight // weapon System.out.println(names[i]); } BraveKnight knight = context.getBean(BraveKnight.class); // knife System.out.println(knight.getWeapon().getType()); context.close(); } }
用XML形式可以在配置文件中,配置我们自己写的类和外部jar包的类,Spring通过反射把这些类都创建出来,并由Spring管理,在你需要的时候给你
可以看到Weapon和BraveKnight对象都被注入到spring 容器中了,而且获取BraveKnight对象时,它的weapon属性已经被设值了,即DI(依赖注入)
注解
我们不仅可以用XML来设置对象,以及对象和对象之间的关系,还可以用注解和JavaConfig
用注解的方式改造一下上面的代码
@Component public class BraveKnight { @Autowired private Weapon weapon; public Weapon getWeapon() { return weapon; } }
@Component public class Weapon { @Value("knife") // 这个值可以从外部配置文件中通过@Value注解读取到 private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } }
@Configuration // 如果不配置扫描的路径,默认扫描配置类所在的包及其子包下面的所有类 @ComponentScan public class MyConfig { }
测试类
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); String names[] = context.getBeanDefinitionNames(); for (int i = 0; i < names.length; i++) { // myConfig // braveKnight // weapon System.out.println(names[i]); } BraveKnight knight = context.getBean(BraveKnight.class); // knife System.out.println(knight.getWeapon().getType()); context.close(); } }
可以看到和上面用XML配置效果一样
JavaConfig
在我们自己的类上,我们可以加@Component注解让Spring来管理,如果是第三方jar包的类呢?它的类上并不会加@Component啊,如果不想用XML来生成第三方jar包的类,JavaConfig在这个时候就派上用场了,接着上面的例子,假如Weapon这个类是第三方jar包的类,则可以通过如下形式让Spring管理
@Configuration // 如果不配置扫描的路径,默认扫描配置类所在的包及其子包下面的所有类 // 可以通过属性basePackages = {""}指定扫描的包 @ComponentScan() public class MyConfig { // name属性默认是方法名,自己可以指定 @Bean(name = "weapon") public Weapon weapon() { Weapon weapon = new Weapon(); weapon.setType("knife"); return weapon; } }
写一个Spring Boot Starter
从上面可以看到,当我们想使用某些类时,我们必须先对类进行配置,让Spring来管理这些类,才能使用。
spring boot可以非常方便的和第三方框架整合,只需要引入一个starter依赖,就可以直接使用(省去了配置的过程),因为spring boot会自动注入我们所需要类。先来自己写一个starter。artifactId如何命名呢?Spring 官方 starter通常命名为spring-boot-starter-{name} 如 spring-boot-starter-web,Spring官方建议非官方starter命名应遵循{name}-spring-boot-starter的格式。来写一个自己的starter,这里我建了一个多模块项目,一个是starter项目,一个是测试项目
starter项目
1.pom文件如下
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> </parent> <groupId>com.st</groupId> <artifactId>demo-service-spring-boot-starter</artifactId> <version>1.0</version> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> </dependencies>
2.供其他模块使用的服务类
public class DemoService { private String host; private int port; public DemoService(String host, int port) { this.host = host; this.port = port; } public String sayHello() { return "hello, " + host + " " + port; } // 省略get和set方法 }
3.用@ConfigurationProperties将application.xml中的属性映射为配置类
@ConfigurationProperties(prefix = "demo.service") public class DemoServicePropeties { private String host; private int port; // 省略get和set方法 }
4.自动配置类
@Configuration @ConditionalOnClass(DemoService.class) @EnableConfigurationProperties(DemoServicePropeties.class) public class DemoServiceAutoConfiguration { @Bean public DemoService demoService(DemoServicePropeties propeties) { return new DemoService(propeties.getHost(), propeties.getPort()); } }
@ConditionalOnClass的作用是,判断当前classpath下是否存在指定类,若存在则将当前的配置载入spring容器
5.在src->main->java->resource目录下建META-INF文件夹,放spring.factories文件,文件内容如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.st.demoservice.DemoServiceAutoConfiguration
测试类项目
1.在pom文件中引入依赖
<dependency> <groupId>com.st</groupId> <artifactId>demo-service-spring-boot-starter</artifactId> <version>1.0</version> </dependency>
2.application.properties中配置属性
demo.service.host=127.0.0.1 demo.service.port=8080
3.测试类
@RunWith(SpringRunner.class) @SpringBootTest public class TestStarterApplicationTests { @Autowired DemoService demoService; @Test public void contextLoads() { String str = demoService.sayHello(); // hello, 127.0.0.1 8080 System.out.println(str); } }
可以看到,我们在用DemoService这个类的时候,Spring Boot已经自动帮我们注入进来了,我们并没有做任何配置,非常方便。原文阅读放了源码的github地址
Spring Boot自动装配的原理
Spring Boot自动装配是怎么实现的呢?
众所周知,所有的Spring Boot启动类上都有一个@SpringBootApplication注解。@SpringBootApplication是一个复合注解,它包含的其中一个注解为@EnableAutoConfiguration,而@EnableAutoConfiguration会导入AutoConfigurationImportSelector这个类,这个类会加载jar包里面META-INF/spring.factories配置文件里面填写的配置类。
以我们的上面写的starter为例,加载的顺序如下
META-INF/spring.factories
DemoServiceAutoConfiguration
DemoServicePropeties(读取application.properties配置文件)
DemoService
所有的starter实现都是这个套路
欢迎关注
参考博客
[1]https://www.xncoding.com/2017/07/22/spring/sb-starter.html
[2]https://www.xncoding.com/2017/07/22/spring/sb-starter.html
[3]https://juejin.im/post/5cc7c5c1f265da03a85accfd
[4]https://juejin.im/post/5cd630916fb9a03235587acd
[5]https://juejin.im/entry/58d37630570c350058c2c15c
- 点赞
- 收藏
- 分享
- 文章举报
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
- Spring Boot在开发时实现热部署(开发时修改文件保存后自动重启应用)(spring-boot-devtools)
- SpringBoot自动装配,实现自定义配置
- 面试问题spring boot自动配置是怎么实现的
- springboot自动装配(1)---@SpringBootApplication注解怎么自动装配各种组件
- Intellij IDEA 使用jrebel运行spring-boot并实现自动编译进行热部署
- spring boot自动装配之@EnableAutoConfiguration详解
- 【spring boot】9.spring boot+spring-data-jpa的入门使用,实现数据持久化
- springboot+springfox+Swagger 实现项目的restful文档的自动生成
- spring-使用注解实现Bean自动装配2
- [Spring Boot] 3. Spring Boot实现自动配置的基础
- Spring中的applicationContext.xml实现自动装配
- IntelliJ IDEA中项目使用Spring Boot用spring-boot-devtools无法实现热部署自动更新的问题
- SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析
- Spring中我们用到的功能实现:基于注解的Ioc自动装配
- Spring实现控制反转(IOC)的三种方式(三)——自动装配
- Spring Boot自定义配置实现IDE自动提示功能
- Spring中使用byName实现Beans自动装配
- Spring Boot 自动装配(一) - @Component、@ComponentScan、@Enable 模块
- Spring Boot教程(一)在springboot中用redis实现消息队列