自动配置
以web启动器为例,在spring-boot-starter-test-3.4.3.pom文件整合了一系列的依赖,那么当启动程序后会不会进行自动配置?当获取到这个容器后,可以从容器中获取所有bean的名字,此时就可以在控制台发现很多bean的名称,都是自动配置进来的。
@SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
// 初始化IOC容器
ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(Demo1Application.class, args);
// 从容器中获取所有的bean的名字
String[] beanDefinitionNames = configurableApplicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
在这里,也可以发现自定义的bean,这是由于@RestController这个注解,等价于@Controller+@ResponseBody,它标记类为REST控制器,即标识一个类为SpringMVC控制器,处理HTTP请求,与传统的@Controller相比,直接返回数据,不返回视图。要使用注解管理类的化,前提要开启包扫描,在启动类的@SpringBootApplication注解下有@ComponentScan,这个注解行包扫描,规则是扫描当前的类(Demo1Application)的目录(demo1)及其子目录(controller)
- 如果定义了某一个场景的启动器,那么项目在加载的时候,会开启对这个启动器的自动配置,会将这个启动器所属依赖里面的相关组件全部交给ioc容器进行管理。
- 自定义组件,通过@ComponentScan注解进行包扫描,规则是扫描当前的类的目录及其子目录。
- springboot不会将所有场景的资源进行自动配置,采取的是按需加载的方式进行自动配置,只有导入对应场景的启动器,那么相关组件才能被管理到ioc容器中
注解
@Configuration
管理bean的方式
- xml的方式管理bean
- 使用注解方式管理bean,@Component,@Controller,@Service,@Repository
- 配置类
@Configuration标识当前的类是一个配置类,类似于spring里的xml配置文件,@Bean像ioc容器添加组件,类似于xml文件中的bean标签,返回值类型是添加到容器组件的数据类型,可以在启动类进行ioc容器中获取bean之后打印,即可得到注入的类。
被@Configuration修饰的配置类,也被交给容器进行管理,由于内部集成了@Conponent注解
MyConfig myConfig = context.getBean(MyConfig.class);
System.out.println(myConfig);
com.example.demo2.config.MyConfig$$SpringCGLIB$$0@7316523a
其中的SpringCGLIB表示这个配置类所属的bean是一个基于CGLIB字节码增强的代理对象
在@Configuration注解内部有一个boolean proxyBeanMethods() default true;表示每个@Bean方法被调用返回的组件都是单例的,如果变为false的话,每个@Bean方法被调用的组件都是新创建的。
@Import
- 在配置类上加入@Import(value={Animal.class})后在启动类获取bean的名称,得到com.example.demo2,pojo.Animal表示向容器中导入一个普通类的组件,组件名称是Animal的全限定名。
- 也可以导入配置配置类,名称是配置类的名称
- 导入ImportSelector接口实现的类
- 导入ImportBeanDefinitionRegistrar接口实现的类
- 总之,可以通过@Import注解向容器中导入一个组件
@Conditional
根据条件动态控制 Bean 的注册或配置类的解析。其核心作用是通过条件判断决定目标对象(如 Bean 或配置类)是否被 Spring 容器处理。SpringBoot在@ConditionalOnBean注解的集成上实现了细化,无需实现Condition接口,只需使用定义好的@ConditionalOnXXX注解,就会注入到IOC容器。例如@ConditionalOnBean(name = "cat"),表示容器中存在cat这个bean,当前@Bean注解修饰的组件才会被装配到容器中;@ConditionalOnMissBean(name = "cat"),表示容器中不存在cat这个bean,当前@Bean注解修饰的组件才会被装配到容器中。
@ImportResource
配置绑定注解
在原生的spring中可以通过set方法或者构造方法进行注入,也可以基于注解的方式进行注入。
Set方法注入
<bean id="userService" class="com.example.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
public class UserServiceImpl {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
构造方法注入
<bean id="userService" class="com.example.UserServiceImpl">
<constructor-arg ref="userDao"/>
</bean>
public class UserServiceImpl {
private final UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
}
基于注解的注入
public class UserServiceImpl {
@Autowired
private UserDao userDao;
}
那么在springboot中如何进行配置属性的自动绑定
-
@Value
注解手动进行绑定:@Value("${car.name}") private String name; -
@Component+@ConfigurationProperties注解进行属性的自动绑定:@ConfigurationProperties(prefix="car") public class car {}
-
@Configuration+@EnableConfigurationProperties注解进行属性的自动绑定:在配置类上进行定义
依赖注入和自动绑定
自动绑定(通过@ConfigurationProperties注解实现功能)时将外部的配置(如application.yml)的属性自动映射到Java对象中,目的是简化配置管理。依赖注入时管理对象之间的依赖关系,由Spring容器负责创建对象并注入其所需的依赖,目的是为了减少代码之间的耦合度。
工作流程:启动类加载(@SpringBootApplication)--> 自动扫描文件下的自动配置类(AutoConfigurationImportSelector)--> 自动配置(SpringBoot检查条件注解,例如@ConditionalOnClass,满足条件才会实例化相应的bean并注入到ioc容器中)-->配置属性的绑定(@ConfigurationProperties进行标注配置属性) --> 依赖注入(构造器,setter方法或者注解)-->环境准备-->启动程序
springboot自动配置原理
包扫描规则原理
从前面可知,扫描的目录是启动器所在的目录及其子目录。
自动配置类的加载
// annotationMetadata 中获取于@EnableAutoConfiguration相关的属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获取所有候选的自动配置类的全限定名列表(只关注这个)
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 去除重复的配置类名称,避免重复加载
configurations = this.removeDuplicates(configurations);
// 获取注解中明确排除的那些自动配置类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 验证排除的类是否确实存在于候选配置类列表中
this.checkExcludedClasses(configurations, exclusions);
// 将用户指定要排除的自动配置类从候选列表中移除
configurations.removeAll(exclusions);
// 过滤器进一步筛选配置类,可能是基于环境、条件注解等
configurations = this.getConfigurationClassFilter().filter(configurations);
// 通知关于即将导入的自动配置类和被排除的类的信息
this.fireAutoConfigurationImportEvents(configurations, exclusions);
// 包含了经过上述处理后的最终自动配置类列表以及被排除的类集合
return new AutoConfigurationEntry(configurations, exclusions);
其中的ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());是用以加载自动配置类的关键步骤,使用了ImportCandidates
类来从classpath中读取指定的自动配置类列表。
-
this.autoConfigurationAnnotation
: 这是一个指向@EnableAutoConfiguration
注解的引用。在Spring Boot的上下文中,表示寻找与自动配置相关的类。 -
this.getBeanClassLoader()
: 这个方法返回的是当前应用上下文使用的类加载器。类加载器是Java虚拟机用来查找和加载类的关键组件。在这里,它被用来定位classpath上的资源文件。 -
ImportCandidates.load(...)
: 这个静态方法会根据传入的注解(这里是@EnableAutoConfiguration
)和类加载器来查找并读取相应的AutoConfiguration.imports
文件。然后,它会解析该文件中的每一行作为全限定类名,并将这些类名收集到一个列表中返回 -
String location = String.format("META-INF/spring/%s.imports", annotation.getName());表示的就是路径
打个断点,就能得到name,因此总路径是INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
由断言和上面的总路径可知进入D:\java\mvn_repository\org\springframework\boot\spring-boot-autoconfigure\3.4.3\spring-boot-autoconfigure-3.4.3.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports这里,就可以看到很多配置信息,
那么可以得出,当加载配置信息的时候,就是加载的这153项,下图的debug也可以证明
总结
自动配置类加载流程
AOP
AOP是如何进行自动配置的,在D:\java\mvn_repository\org\springframework\boot\spring-boot-autoconfigure\3.4.3\spring-boot-autoconfigure-3.4.3.jar!\org\springframework\boot\autoconfigure\aop\AopAutoConfiguration.class路径下,有一个注解
@ConditionalOnProperty( prefix = "spring.aop", name = {"auto"}, havingValue = "true", matchIfMissing = true )
注解要生效,必须存在spring.aop开头的配置信息,在这个配置信息下,必须有一个auto属性,而且必须为true,在配置文件中的默认值为true,那么此时进行了自动的配置,下面进行验证
@SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
String[] names = context.getBeanNamesForType(AopAutoConfiguration.class);
System.out.println(names.length);
for (String s : names) {
System.out.println(s);
}
}
}
在AopAutoConfiguration.class类下,有一个AspectJAutoProxyingConfiguration类,被@ConditionalOnClass({Advice.class})进行修饰,表示如果没有导入Advice.class这个类,表示这个注入不生效,Advice.class在import org.aspectj.weaver.Advice;包下,但是现在并不存在这个包,就是没有在pom.xml引入相关的依赖,那么AspectJAutoProxyingConfiguration类在容器中也是不存在的。代码进行验证报错,即组件没有教给容器管理
cache
导入CatchAutoConfiguration之前要做很多条件注入
DispatcherServletAutoConfiguration
在package org.springframework.boot.autoconfigure.web.servlet;下
总结
- 自动配置类的名称xxxAutoConfiguration
- 自动配置类会开启信息的自动绑定,都会将定义的自动配置信息绑定到一个xxxProperties上面
- 自动配置类里面会通过@Bean注解 装配很多组件到ioc容器里面去(有很多条件装配)
- springboot永远以用户配置的组件为准,如果用户没有配置,就以springboot默认配置的为准
@ConditionalOnMissingBean - springboot很多配置信息都设置了默认值,如果用户修改,就以默认值为准,如果用户修改了,就以用户设置的值为准,例如:server.port