上一章中我们提到,springboot通过父项目依赖和starter的场景启动器来管理和启动相关组件,以 spring-boot-starter-web 场景管理器为例,它能够为提供 Web 开发场景所需要的几乎所有依赖,因此在使用 Spring Boot 开发 Web 项目时,只需要引入该 Starter 即可,而不需要额外导入 Web 服务器和其他的 Web 依赖。
但我们在启动springboot项目时候,springboot是如何按需加载所需要的自动配置配置项?引入哪些场景,这些场景的自动配置才会开始呢?下面以springboot启动类注解来说明。
SpringBoot启动类配置@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//1、返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
//2、查看容器中的组件
String[] names =run.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
默认情况下,Spring Boot 项目会创建一个名为 ***Application 的主程序启动类 ,该类中使用了一个组合注解 @SpringBootApplication,用来开启 Spring Boot 的自动配置,另外该启动类中包含一个 main() 方法,用来启动该项目。
其中 @SpringBootApplication可以使用下面三个注解替换,但要指定xxxApplication所在的包路径
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.example.demo")//注意,这里要指定DemoApplication所在的包
public class DemoApplication {
public static void main(String[] args) {
//1、返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
//2、查看容器中的组件
String[] names =run.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
1. @SpringBootApplication
@SpringBootApplication 注解表明该类是springBoot的主配置类,应运行该类的主方法启动SpringBoot应用,改注解是有以下多个注解组成。
@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 {...}
2. @ComponentScan(了解)
@CmponentScan标注用于指定要扫描的包,将哪些包下面的带有实例化注解的类进行扫描,生成组件,交给SpringIOC容器管理。参数是包路径。
如果用在主启动类中,包路径必须是主启动类所在的包,否则扫描不到主启动类,无法启动报错。
3. @SpringBootConfiguration
@Configuration标注在一个类上,说明该类SpringBootConfiguration是配置类,是springBoot的一个配置类;即 @SpringBootConfiguration标注在一个类似上,这个类也是是springBoot的一个配置类;在主程序 DemoApplication 上适应,说明主程序DemoApplication也可以作为配置类。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {....}
4. @Configuration
@Configuration底层
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
@Configuration是配置类注解,是spring的底层注解;从@Component可以看出配置类也是一个组件;这个注解@Configuration用在类上,表示告诉spring这个类是配置类===配置文件。
@Configuration // 告诉spring这是一个配置类 == 配置文件
public class DemoConfig {
@Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
public User user(){
return new User();
}
}上述代码等价与在xml中配置bean标签
@Configuration修饰的类,默认带有参数proxyBeanMethods,参数proxyBeanMethods默认值是true。
proxyBeanMethods=true:如果为true值,springboot总会检查这个组件在容器中是否存在,如果存在则不会新创建对象。配置类对象是被cglib代理类增强的代理对象。调用配置类中的注册方法都是得到的都是同一个bean对象。即@Configuration默认创建的bean对象是单例的。
@Configuration //默认使用属性@Configuration(proxyBeanMethods=true)
public class DemoConfig {
@Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
public User user(){
return new User();
}
}测试类
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { //1、返回IOC容器 ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); //2、查看容器中的组件 String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } //配置类的测试使用 //从容器中获取组件 User user = run.getBean("user", User.class); //从容器中获取配置类组件,配置类组件是一个代理对象 DemoConfig demoConfig = run.getBean(DemoConfig.class); User user0 = demoConfig.user(); User user1 = demoConfig.user(); System.out.println(user0==user1); } }
proxyBeanMethods=false:如果为fasle值,则springboot不会检查这个组件在容器中是否存在,配置类对象不是被cglib代理类增强的代理对象,调用配置类中的注册方法都是得到的是不同的bean对象。
@Configuration (proxyBeanMethods=false)
public class DemoConfig {
@Bean // 给容器中添加组件,以方法名为组件id,返回的值就是组件
public User user(){
return new User();
}
}测试类
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { //1、返回IOC容器 ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); //2、查看容器中的组件 String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } //配置类的测试使用 //从容器中获取组件 User user = run.getBean("user", User.class); //从容器中获取配置类组件,配置类组件是一个代理对象 DemoConfig demoConfig = run.getBean(DemoConfig.class); User user0 = demoConfig.user(); User user1 = demoConfig.user(); System.out.println(user0==user1); } }
5. @EnableAutoConfiguration(重头戏)
@EnableAutoConfiguration:开启自动配置功能;
以前需要配置的东西,现在springBoot帮我们自动配置,通过这个注解开启自动配置;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage
- 自动配置包,将主配置类(@SpringBootApplication标注的类)的所在包下的所有子包内的所有组件全部加入到spring容器中;
@Import(AutoConfigurationPackages.Registrar.class)
- spring底层注解@import,给容器导入一个组件Registrar;再由组件Registrar 导入一系列其他的组件;
- 组件Registrar 导入指定路径下的包
@Import({AutoConfigurationImportSelector.class})
- spring底层注解@import,给容器导入一个组件;导入的组件由AutoConfigurationImportSelector.class提供;AutoConfigurationImportSelector批量导入组件,它决定导入哪些组件,过程如下图源码:
通过源码发现:
- 通过getAutoConfigurationEntry(annotationMetadata)方法导入一些组件。
- 通过List
configurations = getCandidateConfigurations(annotationMetadata, attributes)方法获取所有要导入的组件。 - 通过List
loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) - 通过Map
> loadSpringFactories(ClassLoader classLoader) - 从META-INF/spring.factories位置加载一个文件,就是默认扫描当前系统所有META-INF/spring.factories位置的文件。
- 重点是spring-boot-autoconfigure-2.6.7.jar包下的META-INF/spring.factories文件,里面配置好了要自动加载的组件有哪些。如下图:
5. @Import导入某个组件
例如导入自动配置选择器组件:@Import({AutoConfigurationImportSelector.class}) ,也可以导入多个组件,组件之间用逗号隔开。导入组件在IOC容器中的名字默认是类的全限定名。
@SpringBootApplication @Import(User.class) public class DemoApplication { public static void main(String[] args) { //1、返回IOC容器 ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); //2、查看容器中的组件 String[] names = run.getBeanNamesForType(User.class); for (String name : names) { System.out.println(name); } } }
6. @ImportResource导入spring的配置类
@ImportResource用来导入spring的配置文件,例如@ImportResource(“classpath:springbean.xml”)
步骤1:service类
public class MyServicve { }
步骤2:创建配置文件
在resources目录下新建一个名为beans.xml的XML自定义配置文件,在该配置文件中通过配置向Spring容器中添加MyConfigServicve类对象。
步骤3:在启动类上引入XML文件
@SpringBootApplication @ImportResource("classpath:beans.xml") public class DemoApplication { public static void main(String[] args) { //1、返回IOC容器 ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); //2、查看容器中的组件 MyServicve servicve = run.getBean(MyServicve.class); System.out.println(servicve); } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)