springboot自动装配原理

从javaweb的servlet到spring的Controller再到springboot的Controller,我们一步一步的简化了我们程序的配置。

  • javaweb中,我们写的各种文件通过我们手动的方式去注入属性和管理,通过动态代理的方式来实现各种功能的横向拓展。
  • spring中,我们简化了类的属性装配和优化了类的创建管理(IOC),并且我们使用了高效的AOP思想(动态代理)来对我们现有的功能进行横向的拓展。同时也可以选择大量现成的spring内嵌功能或者第三方功能来为我们简化开发。
  • 而在springboot中,使我们的开发更进一步的简化了,它内部集成了Tomcat,使我们从Tomcat的配置中脱离出来。它的核心思想是“约定大于配置”,在spring的基础上使用注解约定的方式来使我们的配置进一步简化。一方面我们从繁重的配置bean中脱离出来,另一方面,我们可以更加轻松的整合第三方功能( Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),因为其内部已经有注解约定,只需要加入starter配置,即使生效。

SpringBoot自动配置原理

SpringBoot提供的那么方便的功能是怎么实现的呢?我们学习SpringBoot的自动配置原理,应该从@SpringBootApplication这个注解开始。

image-20210816194942352

@SpringBootApplication
public class  BlogApplication {

    public static void main(String[] args) {
        SpringApplication.run(BlogApplication.class, args);
    }
}

该注解里面又由三个重要注解修饰,分别为

  • @SpringBootConfiguration
  • @ComponentScan
  • @EnableAutoConfiguration
@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 {

其中@SpringBootConfiguration注解由@Configuration注解修饰,功能也和@Configuration注解一样。

另一个@ComponentScan注解作用是扫描本类所在的包下所有的配置类。

// @SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

其中最主要的是@EnableAutoConfiguration注解,它有两个注解修饰,一个为@AutoConfigurationPackage,另一个为@Import({AutoConfigrationSelector.class})

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

@AutoConfigrutionPackage注解也是一个自动扫描包的注解,可以扫描该类同包下的所有类组件。它和@SpringBootConfiguration注解不同。

  • @AutoConfigurationPackage注解是扫描@Entity之类的注解。
  • @SpringBoootConfiguration扫描的是@Component之类的注解。两者扫描的对象不同。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {

其中另一个注解@Import(AutoConfigurationSelector.class)导入一个AutoConfigurationImportSelector.class,这个类可以加载META-INF/spring.factories文件的信息,然后筛选出以EnableAutoConfiguration为key的数据,加载到IOC容器中,实现自动配置功能!

# 配置需要加载到IOC容器的类。这些类中会定义我们组件中的所有配置。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lyj.autoConfig.UserAutoConfiguration

SpringBoot配置文件关联

我们可以很简单的从yaml文件中做一些简单的配置,从而实现我们的个性化需求,SpringBoot是如何实现的呢?

如果我们只需要简单的关联某个普通类,我们只需要这样做。

@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "useri")
public class User {
    
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String, Object> maps;
    private List<Object> lists;
    private Dog dog;
}
  1. 使用Component来进行注入,
  2. 使用ConfigurationProperties来进行前缀绑定。

而如果我们需要绑定配置类,并且为配置类添加一个属性文件绑定前缀,那么我们将需要这样做

定义属性文件

@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "useri")
public class User {

    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String, Object> maps;
    private List<Object> lists;
    private Dog dog;
}

自定义配置类

@Configuration
@EnableConfigurationProperties(User.class)	//将User实体类注入
@ConditionalOnWebApplication	//特定情况生效(web项目时)
public class UserAutiConfiguration {
}

@EnableConfigurationProperties注解的作用是将实体类注入到IOC容器中,这样的方式下,我们只需要利用工厂模式,根据参数来创建我们需要的配置即可。

SpringBoot自定义Starter

了解了SpringBoot的自动配置原理和关联属性之后,我们会发现,自动配置中扫描的EnableAutoConfigutation就是我们配置的这个配置类,AutoConfigurationSeletor.class扫描spring.factory的文件中的类全名路径(即自定义的类),然后利用反射获取信息并注入到IOC容器中。

我们可以先了解官方是怎么自定义starter的

  1. 属性配置文件绑定前缀

    // 绑定前缀
    @ConfigurationProperties(
        prefix = "server",
        ignoreUnknownFields = true
    )
    public class ServerProperties {
        private Integer port;
        private InetAddress address;
        @NestedConfigurationProperty
        private final ErrorProperties error = new ErrorProperties();
        private ServerProperties.ForwardHeadersStrategy forwardHeadersStrategy;
        private String serverHeader;
        private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
    
  2. 自定义配置类

    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties(ServerProperties.class)	//注入IOC容器
    //一堆规则定义何时生效
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)	
    @ConditionalOnClass(CharacterEncodingFilter.class)
    @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)	//绑定server.servlet.encoding的前缀,enables值默认为true。
    public class HttpEncodingAutoConfiguration {
    
        //绑定配置文件,和前面配合使用
    	private final Encoding properties;
    
        //从配置文件中获取参数。
    	public HttpEncodingAutoConfiguration(ServerProperties properties) {
    		this.properties = properties.getServlet().getEncoding();
    	}
    
        
    	@Bean
    	@ConditionalOnMissingBean	//当容器中没有这个Bean时生效
    	public CharacterEncodingFilter characterEncodingFilter() {
    
  3. 配置spring.factory文件

    # Auto Configure,这里用mp的配置文件代替显示。
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
      com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
    

其中最为复杂的配置应该是我们自动配置类的逻辑代码,这里不作一一分析。

可以来简单的实现一个我们自定义的starter,来看看它的所有流程。

自定义starter

  1. 新建springboot项目,将新项目中的多余文件删除。

    1622988148893

  2. 导入两个依赖(依赖之外的除了头部也全部删除)

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>2.3.11.RELEASE</version>
        </dependency>
    </dependencies>
    
  3. 写实体类(需要手动加入set方法和get方法,set方法用于注入属性,get方法用户获取值进行测试)

    @ConfigurationProperties(prefix = "useri")	//绑定插件
    public class UserProperties {
    
        private String name;
        private Integer age;
        private Boolean happy;
        private Date birth;
        private Map<String, Object> maps;
        private List<Object> lists;
        private Dog dog;
    
  4. 暴露一个接口用于测试

    public class UserService {
    
        public UserProperties getUserProperties() {
            return userProperties;
        }
    
        public void setUserProperties(UserProperties userProperties) {
            this.userProperties = userProperties;
        }
    
        private UserProperties userProperties;
    
        public void hello(){
            userProperties.toString();
        }
    }
    
    
  5. 写自动注入类进行配置文件的注入和注入对外暴露的接口

    @Configuration
    @EnableConfigurationProperties(UserProperties.class)
    @ConditionalOnWebApplication   //Web应用生效
    public class UserAutoConfiguration {
    
        @Autowired
        private UserProperties userProperties;
    
        @Bean
        public UserService getUserService(){
            UserService userService = new UserService();
            userService.setUserProperties(userProperties);
            return userService;
        }
    }
    
  6. 设置为springboot的自动配置类,使打包后可以识别

    新建resources/META-INF/spring.factories文件

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lyj.autoConfig.UserAutoConfiguration
    
  7. 使用maven工具下载到maven仓库

    mvn clear install
    

这样的方式下,我们的个自定义start就制作好了。

starter的使用

  1. 导入依赖

    <dependency>
        <groupId>com.lyj</groupId>
        <artifactId>lyj-springboot-starter2</artifactId>
        <version>0.0.2-SNAPSHOT</version>
    </dependency>
    
  2. 在配置文件中根据之前前缀注入属性值

    useri.name=zhangsan
    server.port=8001
    
  3. 测试接口

    @RestController
    public class TestController {
    
        @Autowired
        private UserService userService;
    
        @RequestMapping("/hello")
        public String hello(){
            return userService.getUserProperties().getName();
        }
    
    }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值