开发者必看:Spring Boot自动配置全攻略与Starter自制宝典

218 阅读4分钟

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应用中,需要显式配置DispatcherServletTomcat。而在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 常见问题

  1. 自动配置类未生效:确保spring.factories文件中的自动配置类名正确,并且对应的类存在。
  2. 配置属性未注入:检查application.propertiesapplication.yml文件中的配置项是否正确,以及@ConfigurationProperties注解的prefix是否匹配。
  3. 自定义starter无法自动配置:确保自定义starter的jar包已正确安装到本地仓库或发布到远程仓库,并且项目中已正确引入依赖。

5.2 解决方法

  1. 检查依赖:确保所有必要的依赖都已经添加到项目中。
  2. 清理缓存:尝试清理Spring Boot的缓存,重新启动应用。
  3. 查看日志:查看启动日志,检查是否有错误信息或警告信息。
  4. 使用@Import手动导入:如果自动配置不生效,可以尝试使用@Import注解手动导入自动配置类。

6. 常见误区

  1. 自动配置会覆盖所有配置:自动配置不会覆盖你在配置文件中显式设置的配置。
  2. 自动配置总是优先的:自动配置类通常会检查特定的条件,如果条件不满足,自动配置不会生效。
  3. 所有类都需要@ConfigurationProperties:只有需要从配置文件中自动绑定属性的类才需要@ConfigurationProperties注解。

7. 结论

Spring Boot的自动配置功能极大地简化了Spring应用的配置工作,使得开发者可以更加专注于业务逻辑的实现。通过自定义Starter,我们可以快速集成和复用一些通用的功能模块,提高开发效率。

通过理解Spring Boot自动配置的原理和源码,我们可以更好地掌握Spring Boot的使用,以及如何根据需要进行扩展和定制。