[Java] Spring Boot Auto Configure(Spring Boot自动装配)的原理

前言


Spring Boot能帮助我们Java开发者快速开发基于Spring框架的应用,除了其作为依赖管理好帮手的一众Spring-Boot-Starter之外,其自动装配(Auto Configure)特性也起到了非常重要的作用。那么Spring Boot是如何实现自动装配的呢?本文将结合源码去讲解其原理。

版本信息


  • spring-boot-autoconfigure-2.7.5.jar
  • spring-context-5.3.23.jar
  • dubbo-3.1.1.jar
  • dubbo-spring-boot-autoconfigure-3.1.1.jar

前置知识


Spring框架里最重要的模块莫过于IoC容器,流畅阅读本文的前提是读者有IoC容器的基本概念。
我们先来梳理一些概念,Spring IoC容器里的所有组件(Component,构建完整应用的基本单元)都被称为Bean。Spring IoC容器不是神,当想要帮助其使用者实例化某个具体的Bean时,需要一些额外的信息才能做到这些额外的信息被称为Meta-data(元数据或元信息),例如多个构造器选择哪个、构造器参数信息、是否需要Autowire处理等等信息,在Spring框架里,其被抽象为BeanDefinition接口,大概长下面这个样子。
BeanDefinition结构
就像如果我们要解析JSON字符串需要调JSON解析库把其转成JSON对象再从里获取你需要的信息(数据)一样,创建实例所用的这些个BeanDefinition信息是不会自己跑到Spring IoC容器里的,这需要Spring IoC容器自己去收集,收集BeanDefinition信息是Spring IoC容器的几大主要工作之一。那么Spring框架是如何收集这些信息的呢?

Spring框架如何收集BeanDefinition信息?

既然要收集信息,就不得不提信息载体,信息载体是承载信息(数据)的东西,但凡一个物体能表示1bit的信息都能被视为信息的载体,可见信息的载体有很多种,就例如各种格式的文本文件、序列化后的二进制文件、Java内存里的(Java注解、类名、方法名、参数名、参数类型)等等都是信息的载体。那么相信大家已经猜到了,Spring收集BeanDefinition信息的方式大家至少可以有这么几种:

  • XML配置:Spring所谓的XML config方式,通过XML文本文件存储信息。
  • Java配置:Spring所谓的Java config方式,利用反射API收集注解、类名、方法名、参数名、参数类型携带的信息。

Java配置又可以细分成几种:

  • 如@Component、@Service、@Controller之类的通常是Spring通过反射API和缺省信息去收集BeanDefinition信息。
  • 如@Configuration之类的则是由开发者自主装配Bean。Spring不需要费太大力气便能利用广大开发者写的Bean方法去实例化某个Bean。

除开上面两个,还有如ImportSelector接口、ImportBeanDefinitionRegistrar接口这两种方式:

  • ImportBeanDefinitionRegistrar接口:模块开发者直接向IoC容器里注册BeanDefinition,如dubbo里就用了这种方式。
  • ImportSelector接口:递归地注册ImportSelector#SselectImports方法所发现的所有ImportSelector、ImportBeanDefinitionRegistrar、@Configuration类相关的BeanDefinition。

虽然再细分ImportSelector还能分出DeferredImportSelector,不过这不在本文的讨论范畴之内,咱们先忽略它。

Spring Boot 自动装配的原理


Spring Boot自动装配本质就是向IoC容器提供BeanDefinition的过程。其用到了我们上述的四种Java Config中的后三种。

  • ImportSelector接口
  • ImportBeanDefinitionRegistrar接口
  • @Configuration类

Spring框架的Context核心模块提供了这些功能

上面提到的分三类的处理方式并不是Spring Boot提供的,而是Spring Context提供的。
你可以在下面方法里看到其处理的逻辑:

Spring-Context-5.3.23.jar文件的
org.springframework.context.annotation.ConfigurationClassParser类的
processImports方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {
	/* 省略 */
	for (SourceClass candidate : importCandidates) {
		if (candidate.isAssignable(ImportSelector.class)) { 
			// Candidate class is an ImportSelector -> delegate to it to determine imports
			/* 省略 */ 
		} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
			// Candidate class is an ImportBeanDefinitionRegistrar ->
			// delegate to it to register additional bean definitions
			/* 省略 */
		} else {
			// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
			// process it as an @Configuration class
			/* 省略 */
		}
	}
	/* 省略 */
}

Spring Boot则利用了Spring Context提供的功能来实现自动装配。

@EnableAutoConfiguration:Spring Boot开启自动装配特性的注解

想要Spring Boot开启自动装配特性,我们是需要在程序入口类使用@EnableAutoConfiguration注解来开启自动装配功能,也许大家没怎么见过这个注解,不过一般使用了组合注解@SpringBootApplication的话就是等于开启了@EnableAutoConfiguration的,@SpringBootApplication注解的定义如下:

SpringBootApplication注解

@EnableAutoConfiguration本身也是一个组合注解,定义如下:

EnableAutoConfiguration注解
@Import(AutoConfigurationImportSelector.class)则是其最重要的部分,不难看出其核心是一个我们上面提到过的ImportSelector,@Import注解是向IoC容器里引入一个Bean,这里就引入了AutoConfigurationImportSelector类。

Spring Boot提供的AutoConfigurationImportSelector类干了什么?

相信大家都对spring.factories文件或多或少都有耳闻。对,你猜的没错,简单来AutoConfigurationImportSelector类主要干了两件事

  1. 利用SpringFactoriesLoader类从ClassPath上所有的META-INF/spring.factories文件读取上述三种自动配置类的信息(类名)。

SpringFactoriesLoader源码

  1. 利用ImportCandidates类从ClassPath上所有的META-INF/spring/org.springframework.boot.autoconfiguration.AutoConfiguration.imports文件里读取上述三种自动配置类的信息(类名)。

ImportCandidates源码

AutoConfigurationImportSelector类种利用这两个类的源码如下,第二种方式是较新的,而spring.factories文件的方式则是较老的一种实现。目前在笔者的spring boot 2.7.5版本里是两者都支持的(也就是提供了向下兼容性)。可以看到下方源码中,先调用了SpringFactoriesLoader,再调用了ImportCandidates。
AutoConfigurationImportSelector源码

AutoConfigurationImportSelector支持的两类文件长什么样?


在上一节我们提到了AutoConfigurationImportSelector类支持两类文件来做扩展,分别是:

  • 较老的META-INF/spring.factories文件
  • 较新的META-INF/spring/org.springframework.boot.autoconfiguration.AutoConfiguration.imports文件

1. META-INF/spring.factories文件

第一个是较老的META-INF/spring.factories文件,其本质是个Properties格式的文本文件。内容就是KEY=VALUE。
就比如说我们看一下dubbo-spring-boot-autoconfigure-3.1.1.jar里的这个文件就长这样:

dubbo的spring.factories文件

2. META-INF/spring/org.springframework.boot.autoconfiguration.AutoConfiguration.imports文件

第二个是比较新的META-INF/spring/org.springframework.boot.autoconfiguration.AutoConfiguration.imports,其就是个普通的换行符分割文本文件
我们看一下spring-boot-autoconfigure-2.7.5.jar里的这个文件长这样:

新文件

文件内容

两种文件的内容普遍都是@Configuration类的全名(包名+类名),这里的数据本质是字符串,读到内存里还是需要利用反射API转换成Class<?>实例再开始使用的,就是由ConfigurationClassParser$DeferredImportSelectorHandler的processGroupImports方法来实现的。可以看到内部调用了我们先前提到的processImports方法。
processGroupImports

反射API具体使用的地方就是上面代码里的调用asSourceClass方法了:

asSourceClass方法

结语


通过源码分析,我们可以看到Spring Boot利用了Spring Context提供的加载机制,其先利用@Import把自身最关键的组件AutoConfigurationImportSelector引入到容器里,并且引入ImportSelector是会递归引入AutoConfigurationImportSelector在两种文件里找到的所有的类,也是这种动态地加载所有这两类文件的机制是Spring Boot自动装配实现扩展性的核心。使得其他项目、组件的开发者可以利用这个扩展机制(如编写自己的spring.factories文件、@Configuration类等)去支持或叫实现自动装配。但不难看出Spring Boot虽然实现了自动装配,但实际的装配工作(@Configuration类、ImportSelector、ImportBeanDefinitionRegistrar的编码工作)还是需要由对应的项目开发者去实现的,比如dubbo就是自己实现的相应的spring boot自动装配类。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Copyright © 2017 by K. Siva Prasad Reddy Spring is the most popular Java-based framework for building enterprise applications. The Spring framework provides a rich ecosystem of projects to address modern application needs, like security, simplified access to relational and NoSQL datastores, batch processing, integration with social networking sites, large volume of data streams processing, etc. As Spring is a very flexible and customizable framework, there are usually multiple ways to configure the application. Although it is a good thing to have multiple options, it can be overwhelming to the beginners. Spring Boot addresses this “Spring applications need complex configuration” problem by using its powerful autoconfiguration mechanism. Spring Boot is an opinionated framework following the “Convention Over Configuration” approach, which helps build Spring-based applications quickly and easily. The main goal of Spring Boot is to quickly create Spring-based applications without requiring the developers to write the same boilerplate configuration again and again. In recent years, the microservices architecture has become the preferred architecture style for building complex enterprise applications. Spring Boot is a great choice for building microservices-based applications using various Spring Cloud modules. This book will help you understand what Spring Boot is, how Spring Boot helps you build Spring-based applications quickly and easily, and the inner workings of Spring Boot using easy-to-follow examples.
Pro Spring Boot 2: An Authoritative Guide to Building Microservices, Web and Enterprise Applications, and Best Practices Quickly and productively develop complex Spring applications and microservices out of the box, with minimal concern over things like configurations. This revised book will show you how to fully leverage the Spring Boot 2 technology and how to apply it to create enterprise ready applications that just work. It will also cover what's been added to the new Spring Boot 2 release, including Spring Framework 5 features like WebFlux, Security, Actuator and the new way to expose Metrics through Micrometer framework, and more. This book is your authoritative hands-on practical guide for increasing your enterprise Java and cloud application productivity while decreasing development time. It's a no nonsense guide with case studies of increasing complexity throughout the book. The author, a senior solutions architect and Principal Technical instructor with Pivotal, the company behind the Spring Framework, shares his experience, insights and first-hand knowledge about how Spring Boot technology works and best practices. Pro Spring Boot 2 is an essential book for your Spring learning and reference library. What You Will Learn Configure and use Spring Boot Use non-functional requirements with Spring Boot Actuator Carry out web development with Spring Boot Persistence with JDBC, JPA and NoSQL Databases Messaging with JMS, RabbitMQ and WebSockets Test and deploy with Spring Boot A quick look at the Spring Cloud projects Microservices and deployment to the Cloud Extend Spring Boot by creating your own Spring Boot Starter and @Enable feature Who This Book Is For Experienced Spring and Java developers seeking increased productivity gains and decreased complexity and development time in their applications and software services.
Java Spring Boot可以使用Spring Security来实现账号密码登录和注册。Spring Security是一个框架,用于在Java应用程序中提供身份验证和授权服务。以下是一个简单的示例。 首先,需要在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 接下来,需要创建一个用户实体类,包含用户名和密码字段: ``` @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column(nullable = false) private String password; // getters and setters } ``` 然后,需要创建一个实现UserDetailsService接口的类,用于从数据库中获取用户信息: ``` @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found"); } return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>()); } } ``` 在这个类中,通过UserRepository从数据库中获取User对象,然后将其转换为Spring Security的UserDetails对象。 最后,在Spring Boot的配置文件中配置Spring Security: ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/register").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/") .permitAll() .and() .logout() .logoutUrl("/logout") .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); } } ``` 在这个配置类中,首先注入UserDetailsService,然后配置HttpSecurity,指定哪些URL需要身份验证,哪些URL不需要身份验证。然后,配置登录页面、登录成功后跳转的页面和退出登录的URL。 最后,在configureGlobal方法中,使用UserDetailsService和PasswordEncoder配置AuthenticationManagerBuilder。 这样,就实现了使用Spring Security实现账号密码登录和注册。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值