Spring Boot自动配置原理详解
1. Spring Boot自动配置概述
Spring Boot自动配置是Spring Boot框架的核心特性之一,它极大地简化了Spring应用的初始搭建以及第三方库的集成。开发者无需编写大量的配置代码,Spring Boot会自动根据类路径下的jar依赖为你创建项目所需的bean,并进行相应的配置。
2. Spring Boot自动配置原理
Spring Boot的自动配置主要依赖于@EnableAutoConfiguration注解,这个注解告诉Spring Boot基于classpath中的jar依赖为你的应用进行自动配置。
2.1 @SpringBootApplication注解
@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 {
// 排除掉自动配置的class
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
// 排除掉自动配置的全路径类名
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
// 配置扫描的包路径
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
// 配置扫描的类
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
// beanName生成器
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
// 配置类代理模式:proxyBeanMethods:代理bean的方法
// Full(proxyBeanMethods = true)、保证每个@Bean方法被调用多少次返回的组件都是单实例的
// Lite(proxyBeanMethods = false)每个@Bean方法被调用多少次返回的组件都是新创建的
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
2.2 @EnableAutoConfiguration注解
@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 {};
}
2.3 AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// 省略实现细节
}
2.4 spring.factories
spring.factories文件中可以指定自动配置类,Spring Boot会在启动时加载这些类。例如:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.myproject.autoconfigure.MyAutoConfiguration
3. 源码分析
3.1 DispatcherServlet的自动配置
在Spring MVC应用中,需要显式配置DispatcherServlet和Tomcat。而在Spring Boot中,只需引入spring-boot-starter-web依赖,就可以自动配置DispatcherServlet。
源码位置:
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
关键注解:
@EnableConfigurationProperties({WebMvcProperties.class}):使WebMvcProperties.class类上的@ConfigurationProperties注解生效,将application.properties中以spring.mvc开头的配置参数自动注入到WebMvcProperties.class类的字段中。@Conditional({DefaultDispatcherServletCondition.class}):根据条件判断是否加载该自动配置类。
4. 自定义Starter实现
4.1 定义注解
首先定义一个注解,例如@PrintLog,用于标记需要打印日志的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintLog {
}
4.2 定义切面类
定义一个切面类,例如LogAspect,用于实现日志打印的逻辑。
@Slf4j
@Aspect
public class LogAspect {
@Pointcut("@annotation(com.test.annotation.PrintLog)")
public void Log(){}
@Around("Log()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String methodName = method.getName();
// 打印调用url
log.info("URL:{}", request.getRequestURL().toString());
// 打印ip
log.info("IP :{}", request.getRemoteAddr());
// 打印方法
log.info("method :{}", methodName);
// 打印参数
log.info("parameter :{}", Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
// 打印返回结果
log.info("return :" + JsonUtils.toJson(result));
return result;
}
}
4.4 定义自动配置类
定义一个自动配置类,例如LogAutoConfigure,用于将切面类注册到Spring容器中。
@Configuration
public class LogAutoConfigure {
@Bean
@ConditionalOnClass(Advice.class)
public LogAspect webLogAspect(){
return new LogAspect();
}
}
4.5 使用自定义Starter
4.5.1 在项目中引入Starter
<dependency>
<groupId>com.test.demo</groupId>
<artifactId>LogStarter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
4.5.2 定义一个Controller并标上打印日志的注解
@RestController
@RequestMapping("/test")
public class HelloWorldController {
@PrintLog
@RequestMapping("/hello")
public String helleWorld(String test){
return "hello world!";
}
}
4.7.4 启动项目开始测试
启动项目后,可以看到日志打印了请求的URL、IP、方法名、参数和返回值。
2024-10-09 11:43:21.239 [] [http-nio-8300-exec-1] INFO com.test.intercepter.LogAspect - URL:http://127.0.0.1/test/hello
2024-10-09 11:43:21.240 [] [http-nio-8300-exec-1] INFO com.test.intercepter.LogAspect - IP :172.14.70.13
2024-10-09 11:43:21.240 [] [http-nio-8300-exec-1] INFO com.test.intercepter.LogAspect - method :searchByText
2024-10-09 11:43:21.241 [] [http-nio-8300-exec-1] INFO com.test.intercepter.LogAspect - parameter :[test]
2024-10-09 11:43:21.481 [] [http-nio-8300-exec-1] INFO com.test.intercepter.LogAspect - return :hello world!
5. 故障排除指南
5.1 常见问题
- 自动配置类未生效:确保
spring.factories文件中的自动配置类名正确,并且对应的类存在。 - 配置属性未注入:检查
application.properties或application.yml文件中的配置项是否正确,以及@ConfigurationProperties注解的prefix是否匹配。 - 自定义starter无法自动配置:确保自定义starter的jar包已正确安装到本地仓库或发布到远程仓库,并且项目中已正确引入依赖。
5.2 解决方法
- 检查依赖:确保所有必要的依赖都已经添加到项目中。
- 清理缓存:尝试清理Spring Boot的缓存,重新启动应用。
- 查看日志:查看启动日志,检查是否有错误信息或警告信息。
- 使用
@Import手动导入:如果自动配置不生效,可以尝试使用@Import注解手动导入自动配置类。
6. 常见误区
- 自动配置会覆盖所有配置:自动配置不会覆盖你在配置文件中显式设置的配置。
- 自动配置总是优先的:自动配置类通常会检查特定的条件,如果条件不满足,自动配置不会生效。
- 所有类都需要
@ConfigurationProperties:只有需要从配置文件中自动绑定属性的类才需要@ConfigurationProperties注解。
7. 结论
Spring Boot的自动配置功能极大地简化了Spring应用的配置工作,使得开发者可以更加专注于业务逻辑的实现。通过自定义Starter,我们可以快速集成和复用一些通用的功能模块,提高开发效率。
通过理解Spring Boot自动配置的原理和源码,我们可以更好地掌握Spring Boot的使用,以及如何根据需要进行扩展和定制。